Как обработать столбец, содержащий значения строки, разделенные запятыми или диапазон, в Oracle - PullRequest
0 голосов
/ 07 апреля 2020

Используя Oracle 12 c DB, у меня есть следующий пример табличных данных, который мне требуется помощь с использованием SQL и PL / SQL.

Данные таблицы следующие:

Table Name: my_data

ID      ITEM        ITEM_LOC
------- ----------- ----------------
1       Item-1      0,1
2       Item-2      0,1,2,3,4,7
3       Item-3      0-48
4       Item-4      0,1,2,3,4,5,6,7,8
5       Item-5      1-33
6       Item-6      0,1
7       Item-7      0,1,5,8

Используя приведенные выше данные в таблице my_data, как лучше всего обработать этот ITEM_LO C, так как мне нужно использовать значения в этом столбце в качестве отдельного значения, то есть:

0,1 означает, что SQL необходимо вернуть либо 0, либо 1, либо

значений диапазона, то есть:

0-48 означает, что SQL необходимо вернуть значение между 0 и 48.

Возвращенные значения для обоих сценариев ios должны начинаться с самого низкого до самого высокого значения и не могут быть повторно использованы после обработки.

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

Требуемый результат для ID = 2 может быть 7. Для этого ID = 2 ITEM_LO C = 7 не может быть использован снова.

Желаемый результат для ID = 5 может быть 31. Для этого ID = 5 ITEM_LO C = 31 не может быть использован снова.

Для данных ITEM_LO C, которые не могут быть снова используется против этого идентификатора, я смотрю на удержание другой таблицы для хранения этой или, возможно, для разделения всех данных в отдельные строки с новым столбцом с именем VALUE_USED.

Ответы [ 2 ]

2 голосов
/ 07 апреля 2020

Этот запрос показывает, как извлечь список значений ITEM_LOC на основе того, разделены ли они запятыми (что означает «принимать именно эти значения») или разделенными da sh (что означает «найти все значения между началом и конечная точка »). Я немного изменил ваши данные выборки (не хотелось отображать ~ 50 значений, если 5 из них сделали свою работу).

  • строки # 1 - 6 представляют данные выборки.
  • первая select (строки # 7 - 15) разбивает значения через запятую на строки
  • вторая select (строки # 17 - 26) использует иерархический запрос, который добавляет 1 к начальному значение, вплоть до конечного значения элемента.

SQL> with my_data (id, item, item_loc) as
  2    (select 2, 'Item-2', '0,2,4,7' from dual union all
  3     select 7, 'Item-7', '0,1,5'   from dual union all
  4     select 3, 'Item-3', '0-4'     from dual union all
  5     select 8, 'Item-8', '5-8'     from dual
  6    )
  7  select id,
  8    item,
  9    regexp_substr(item_loc, '[^,]+', 1, column_value) loc
 10  from my_data
 11    cross join table(cast(multiset
 12      (select level from dual
 13       connect by level <= regexp_count(item_loc, ',') + 1
 14      ) as sys.odcinumberlist))
 15  where instr(item_loc, '-') = 0
 16  union all
 17  select id,
 18    item,
 19    to_char(to_number(regexp_substr(item_loc, '^\d+')) + column_value - 1) loc
 20  from my_data
 21    cross join table(cast(multiset
 22      (select level from dual
 23       connect by level <= to_number(regexp_substr(item_loc, '\d+$')) -
 24                           to_number(regexp_substr(item_loc, '^\d+')) + 1
 25      ) as sys.odcinumberlist))
 26  where instr(item_loc, '-') > 0
 27  order by id, item, loc;

        ID ITEM   LOC
---------- ------ ----------------------------------------
         2 Item-2 0
         2 Item-2 2
         2 Item-2 4
         2 Item-2 7
         3 Item-3 0
         3 Item-3 1
         3 Item-3 2
         3 Item-3 3
         3 Item-3 4
         7 Item-7 0
         7 Item-7 1
         7 Item-7 5
         8 Item-8 5
         8 Item-8 6
         8 Item-8 7
         8 Item-8 8

16 rows selected.

SQL>

Я не знаю, что вы имели в виду, говоря, что "item_lo c не может быть использован снова". Используется , где ? Если вы используете вышеупомянутый запрос, например, для курсора FOR l oop, тогда да - эти значения будут использоваться только один раз, поскольку каждая итерация l oop выбирает следующее item_loc значение.

0 голосов
/ 07 апреля 2020

Как уже говорили другие, плохая идея хранить данные таким образом. Скорее всего, у вас может быть ввод , как это, и вам, вероятно, понадобится отображение данных, подобных этим, но вам не нужно хранить данные так, как они вводятся или отображаются .

Я собираюсь сохранить данные как отдельные LOC элементы на основе ввода. Я предполагаю, что данные содержат только целые числа, разделенные запятыми, или пары целых чисел, разделенные дефисом. Пробелы игнорируются. Список через запятую не должен быть в любом порядке. В парах, если левое целое больше правого, я не возвращаю элемент LOC.

create table t as 
with input(id, item, item_loc) as (
  select 1, 'Item-1', ' 0,1' from dual union all
  select 2, 'Item-2', '0,1,2,3,4,7' from dual union all
  select 3, 'Item-3', '0-48' from dual union all
  select 4, 'Item-4', '0,1,2,3,4,5,6,7,8' from dual union all
  select 5, 'Item-5', '1-33' from dual union all
  select 6, 'Item-6', '0,1' from dual union all
  select 7, 'Item-7', '0,1,5,8,7 - 11' from dual
)
select distinct id, item, loc from input, xmltable(
 'let $item := if (contains($X,",")) then ora:tokenize($X,"\,") else $X
  for $i in $item
    let $j := if (contains($i,"-")) then ora:tokenize($i,"\-") else $i
    for $k in xs:int($j[1]) to xs:int($j[count($j)])
    return $k'
  passing item_loc as X
  columns loc number path '.'
);

Теперь, чтобы "использовать" элемент, я просто удаляю его из таблицы:

delete from t where rowid = (
  select min(rowid) keep (dense_rank first order by loc)
  from t
  where id = 7
);

Чтобы вернуть данные в том же формате, в котором они были введены, используйте MATCH_RECOGNIZE:

select id, item, listagg(item_loc, ',') within group(order by first_loc) item_loc
from t
match_recognize(
  partition by id, item order by loc
  measures a.loc first_loc,
    a.loc || case count(*) when 1 then null else '-'||b.loc end item_loc
  pattern (a b*)
  define b as loc = prev(loc) + 1
)
group by id, item;

ID  ITEM    ITEM_LOC
1   Item-1  0-1
2   Item-2  0-4,7
3   Item-3  0-48
4   Item-4  0-8
5   Item-5  1-33
6   Item-6  0-1
7   Item-7  1,5,7-11

Обратите внимание, что выходные данные здесь не будут точно такими же, как входные, потому что любые последовательные целые числа будут сжаты в пару.

...