Как проверить, является ли массив чисел подмножеством разделённых запятыми чисел в строке?
Пытаюсь проверить, является ли массив чисел подмножеством разделённых запятыми чисел в строке.
Например, я хочу получить ID записей в таблице:
| ID | STRING1 | STRING2 |
|---|---|---|
| 1 | 100,101,201 | 1,2,3,4 |
| 2 | 100,103,201,202 | 1,4,9,10 |
| 3 | 101,102,200 | 1,3,4 |
которые удовлетворяют условию (псевдокод):
(100,201) is subset of STRING1 AND (1,4) is subset of STRING2
Ожидаемый результат: ID 1 и 2
Свободный перевод вопроса How to find numbers inside selected string? от участника @maxime mkhe
Ответы (2 шт):
Вот один из способов, организовать все для более легкгой поддержки. Предполагаю, что версия 12.1 или выше, поэтому можно использовать функции JSON (для удобной работы по разделению списков). В более старых версиях придется написать функцию по-другому, но концепция будет той же.
Идея заключается в том, чтобы написать функцию, которая разбивает списки чисел и возвращает вместо них вложенную таблицу. Потребуется глобальный тип данных (на уровне схемы).
Подготовка схемы:
create or replace type tbl_of_num is table of number;
/
create or replace function str_to_tbl(s varchar2) return tbl_of_num deterministic is
pragma udf;
ton tbl_of_num;
begin
select cast(collect(val) as tbl_of_num) into ton
from json_table('[' || s || ']', '$[*]' columns val number path '$');
return ton;
end;
/
create table t (id, string1, string2) as
select 1, '100,101,201', '1,2,3,4' from dual union all
select 2, '100,103,201', '1,4,9,10' from dual union all
select 3, '101,102,200', '1,3,4,10' from dual;
Сначала определяю две переменные связывания, инициализирую их и использую в запросе:
variable string1 varchar2(100)
variable string2 varchar2(100)
exec :string1 := '100,201'; :string2 := '1,4'
select id
from t
where str_to_tbl(:string1) submultiset of str_to_tbl(string1)
and str_to_tbl(:string2) submultiset of str_to_tbl(string2);
ID
----------
1
2
Возможны другие способы передачи значений в переменных связывания, это зависит от приложения.
Ешё два пути решения задачи без создания дополнительных обьектов БД.
Для удобства создал только одну вложенную таблиицу numtab, но если и это нежелательно, для непривилегированных пользователей доступнa mdsys.sdo_numtab.
Наиболее эффективно, с обявлением функции в запросе:
with
function toNumTab (str varchar2) return numtab is
pos int := 1; len int := 0;
r numtab := numtab ();
begin <<split>> loop
len := instr (str||',', ',', pos);
exit split when len = 0;
r.extend; r(r.last) := to_number (substr (str, pos, len-pos));
pos := len+1;
end loop;
return r;
end;
args (arg1, arg2) as (
select numtab (100, 201), numtab (1, 4)
from dual
)
select id
from t, args
where args.arg1 submultiset of toNumTab (string1) and
args.arg2 submultiset of toNumTab (string2);
На чистом SQL можно расспарсить список чисел из строки стандартной функцией XMLTable, но в этом случае, надо будет ее результат сначало конвертировать во вложеную таблицу, чтобы воспользоваться фунццией submultiset:
with
t1 (id, nt1, nt2) as (
select id,
(select cast (collect (to_number (column_value)) as numtab) from xmlTable (string1)),
(select cast (collect (to_number (column_value)) as numtab) from xmlTable (string2))
from t
),
args (arg1, arg2) as (
select numtab (100, 201), numtab (1, 4)
from dual
)
select id
from t1, args
where arg1 submultiset of t1.nt1 and
arg2 submultiset of t1.nt2
Оба запроса выше вернут ожидаемый результат (см. db<>fiddle).