Поиск подстрок в строке и делить - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть таблица со столбцом varchar2 (5000 символов), которая содержит несколько предложений.Где-то в таком предложении показывается число, за которым следует другое число.Я хочу выбрать эти цифры и разделить их.Не уверен, как это сделать в sqlplus oracle 11g.

Я думаю, что я должен использовать такие функции, как SUBSTR, REGEXP_SUBSTR, INSTR.Не уверен, что лучше всего подходит для этой работы.

столбцы похожи на это:

blah bahwl balwo hxkswl blahxhsh alshbhe NUM 40003.26 in 4 pieces. etc
bwh bahwl bafado hxkswl alshbhe NUM 6006.16 in 9 pieces. etc
badh baadfl balwo hxkswl blahxhsh alshbhe NUM 200 in 30 pieces. etc
bfda bdafl hxkswl NUM 33 in 4 pieces. etc
blcfh bfdwl bfdlwo alshbhe NUM 54545.01 in 700 pieces. etc

Поэтому я хочу найти и выбрать номер сразу после NUM и выбрать число между «в»и «кусочки», а затем разделить эти числа.

select .... 40003.26 / 4 
select .... 6006,16 / 9 
select ....  200 / 30 

и т. д.

Любая помощь любезно приветствуется

Ответы [ 3 ]

0 голосов
/ 10 февраля 2019

Эта опция выбирает NUM blabla частей входной строки столько раз, сколько там появляется NUM (это то, что делает INTER CTE).Последняя строка SELECT (строки 13 и далее) извлекает числовые значения и делит их для получения результата.

SQL> with test (col) as
  2    (select 'blah bahwl balwo hxkswl blahxhsh alshbhe NUM 40003.26 in 4 pieces. etc
  3  bwh bahwl bafado hxkswl alshbhe NUM 6006.16 in 9 pieces. etc
  4  badh baadfl balwo hxkswl blahxhsh alshbhe NUM 200 in 30 pieces. etc
  5  bfda bdafl hxkswl NUM 33 in 4 pieces. etc
  6  blcfh bfdwl bfdlwo alshbhe NUM 54545.01 in 700 pieces. etc' from dual
  7    ),
  8  inter as
  9    (select regexp_substr(col, 'NUM \d+(\.\d+)? in \d+', 1, level) res
 10     from test
 11     connect by level <= regexp_count(col, 'NUM')
 12    )
 13  select res,
 14         regexp_substr(res, '\d+(\.\d+)?') c1,
 15         regexp_substr(res, '\d+$') c2,
 16         --
 17         round(to_number(regexp_substr(res, '\d+(\.\d+)?'), '99999999D99',
 18                         ' NLS_NUMERIC_CHARACTERS = ''.,''') /
 19               to_number(regexp_substr(res, '\d+$')), 2) result
 20  from inter;

RES                  C1         C2             RESULT
-------------------- ---------- ---------- ----------
NUM 40003.26 in 4    40003.26   4            10000,82
NUM 6006.16 in 9     6006.16    9              667,35
NUM 200 in 30        200        30               6,67
NUM 33 in 4          33         4                8,25
NUM 54545.01 in 700  54545.01   700             77,92

SQL>
0 голосов
/ 10 февраля 2019

Если вы можете гарантировать , что ALL (и я имею в виду ALL ) строк будут иметь фрагмент, подобный описанному вами: NUM ..... in ..... pieces где ..... - это два числа со вторым числом, отличным от 0 (поэтому мы не делим на 0), тогда вы можете использовать для этого XMLQuery, например:

with
  test_data as (
    select 1 id, 'blah bahwl NUM 40003.26 in 4 pieces. etc' str from dual union all
    select 2   , 'bwh balshbh NUM 6006.16 in 9 pieces. etc'     from dual union all
    select 3   , 'badh sh alshbh NUM 200 in 30 pieces. etc'     from dual union all
    select 4   , 'bfda bdafl hxksw NUM 33 in 4 pieces. etc'     from dual union all
    select 5   , 'bl lshbh NUM 54545.01 in 700 pieces. etc'     from dual
  )
select id,
       xmlquery(regexp_replace(str, '^.*?NUM(.*?)in(.*?)pieces.*$', '\1 div \2')
                returning content).getNumberVal() as division_result
from   test_data
;    

   ID DIVISION_RESULT
----- ---------------
    1    10000.815000
    2      667.351111
    3        6.666667
    4        8.250000
    5       77.921443

Даже если вы не знакомы с XMLQueryНетрудно понять, как это работает в этом особом случае.Вы можете выбрать SELECT результат REGEXP_REPLACE отдельно, чтобы увидеть фактический ввод в XMLQuery.

Однако это решение будет подавлять любой дополнительных входов в большем наборе данных, показанном ниже какa WITH предложение.

Если такие исключения возможны, вы должны сообщить нам, как следует обрабатывать каждое из них.Обратите внимание, что id = 6 не имеет требуемого фрагмента вообще;id = 7 отсутствует второй номер;id = 8 имеет второе число, равное 0;и id = 9 содержит текст там, где должно быть число.

Это показывает, почему, возможно, если вам приходится сталкиваться с такими проблемами, вам следует рассмотреть возможность добавления двух столбцов, возможно, виртуальных, если ваша версия Oracle высокадостаточно, чтобы хранить только необходимые числа - тогда обработка возможных исключений станет намного проще.Это также, очевидно, приблизит вашу модель данных к первой нормальной форме, которую ваша текущая модель грубо нарушает.

with
  test_data as (
    select 1 id, 'blah bahwl NUM 40003.26 in 4 pieces. etc' str from dual union all
    select 2   , 'bwh balshbh NUM 6006.16 in 9 pieces. etc'     from dual union all
    select 3   , 'badh sh alshbh NUM 200 in 30 pieces. etc'     from dual union all
    select 4   , 'bfda bdafl hxksw NUM 33 in 4 pieces. etc'     from dual union all
    select 5   , 'bl lshbh NUM 54545.01 in 700 pieces. etc'     from dual union all
    select 6   , 'blah blah blah'                               from dual union all
    select 7   , 'blah blah NUM 23 in pieces etc.'              from dual union all
    select 8   , 'blah NUM 23 in 0 pieces'                      from dual union all
    select 9   , 'blah NUM 12 in three pieces etc.'             from dual
  )
    ............  How should the exceptions for id between 6 and 9 be handled?
0 голосов
/ 10 февраля 2019

Вы можете использовать regexp_substr, regexp_replace с вкладом l / rtrim, как показано ниже

with t1 as
 (select 'blah bahwl balwo hxkswl blahxhsh alshbhe NUM 40003.26 in 4 pieces. etc
             bwh bahwl bafado hxkswl alshbhe NUM 6006.16 in 9 pieces. etc
             badh baadfl balwo hxkswl blahxhsh alshbhe NUM 200 in 30 pieces. etc
             bfda bdafl hxkswl NUM 33 in 4 pieces. etc
             blcfh bfdwl bfdlwo alshbhe NUM 54545.01 in 700 pieces. etc' as str
    from dual),
t2 as
 (select regexp_substr(str, '(.*)pieces.*', 1, level) as str
    from t1
   cross join dual
  connect by level <= regexp_count(str, 'pieces')),
t3 as
 (select str,
         regexp_replace(str, '[^0-9]in[^0-9]', '/') str1,
         regexp_substr(str, '[^0-9]*') str2,
         regexp_substr(str, '[^0-9]*$') str3
    from t2)
select rtrim(ltrim(str1, str2), str3) as "Result String" from t3


Result String
-------------
40003.26/4
6006.16/9
200/30
33/4
54545.01/700

Демо

...