Это может быть быстрее:
with
t1 as (select distinct a.id ia, a.name na, b.id ib, b.name nb
from t a join t b on a.id < b.id),
t2 as (
select ia, na, ib, nb,
cast(multiset(select attr||':'||val from t where id = ia intersect
select attr||':'||val from t where id = ib )
as sys.odcivarchar2list) a1,
cast(multiset(select attr||':'||val from t where id = ia minus
select attr||':'||val from t where id = ib )
as sys.odcivarchar2list) a2,
cast(multiset(select attr||':'||val from t where id = ib minus
select attr||':'||val from t where id = ia )
as sys.odcivarchar2list) a3
from t1)
select ia, na, ib, nb,
(select listagg(column_value, ';') within group (order by null) from table(t2.a1)) l1,
(select listagg(column_value, ';') within group (order by null) from table(t2.a2)) l2,
(select listagg(column_value, ';') within group (order by null) from table(t2.a3)) l3
from t2
order by ia, ib
dbfiddle demo
- подзапрос
t1
создает пары «машин», которые мы сравним t2
сборов для каждой пары наборов общих или разных атрибутов.sys.odcivarchar2list
является встроенным типом, просто таблица строк окончательный запрос изменяет коллекции в список строк.Результат:
IA NA IB NB L1 L2 L3
-- ------------ --- ----- --------- ------------ -----------
1 Ford 2 BMW A:B;C:D B:C F:G
1 Ford 3 TESLA A:B B:C;C:D E:F;Z:Y
2 BMW 3 TESLA A:B C:D;F:G E:F;Z:Y
Я надеюсь, что это будет быстрее, потому что мы не используем какую-либо пользовательскую функцию и количество операций сведено к минимуму.
Альтернатива - использовать что-то вроде этой функции:
-- find different or common attributes
create or replace function dca(i1 in number, i2 in number, op in char)
return varchar2 is
ret varchar2(1000);
begin
case op
when 'M' then -- minus
select listagg(attr||':'||val, ';') within group (order by null) into ret
from (select attr, val from t where id = i1 minus
select attr, val from t where id = i2 );
when 'I' then -- intersect
select listagg(attr||':'||val, ';') within group (order by null) into ret
from (select attr, val from t where id = i1 intersect
select attr, val from t where id = i2 );
end case;
return ret;
end;
в этом запросе:
select ia, na, ib, nb,
dca(ia, ib, 'I') ab, dca(ia, ib, 'M') a_b, dca(ib, ia, 'M') b_a
from (select distinct a.id ia, a.name na, b.id ib, b.name nb
from t a join t b on a.id < b.id)
order by ia, ib;
Это тоже работает, но это UDF, который работает хуже взапросы.