Объединение 2 наборов данных через пересечение - PullRequest
0 голосов
/ 01 февраля 2019

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

У меня есть следующий наборданных (обратите внимание, что способ чтения данных ниже - это ID1, Ford, имеет следующие атрибуты и значения A: B, B: C и C: D

+------------------------------------------------+
| ID     NAME     Attribute      Attribute Value |
+------------------------------------------------+
| 1      Ford         A                  B       |
| 1      Ford         B                  C       |
| 1      Ford         C                  D       |
| 2      BMW          A                  B       |
| 2      BMW          C                  D       |
| 2      BMW          F                  G       |
| 3      TESLA        Z                  Y       |
| 3      TESLA        E                  F       |
| 3      TESLA        A                  B       |
+------------------------------------------------+

. Я бы хотел сравнить каждый идентификаторв таблице по отношению к остальным и вывести результат. Первое сравнение будет проверять ID 1 против 2 и 3 и делать сравнение и видеть, где совпадения, а где нет.

ouput (1stсравнение сделано только против только 1 записи):

+----------------------------------------------------------------------------+
| BaseID  BaseNAME   Target ID   TargetName    MatchedOn    Baseonly Tgtonly |
+----------------------------------------------------------------------------+
| 1        Ford         2          BMW           A:B;C:D     B:C     F:G     |
+----------------------------------------------------------------------------+

Раньше добрый человек помогал мне реализовать декартово произведение, но данные были в несколько ином формате - но это было слишком медленно. Поэтому я хотелпосмотреть, есть ли у кого-нибудь идеи о том, как лучше всего достичь желаемого результата?

Ответы [ 2 ]

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

Работает в Oracle 12 +.

В 11g вы можете объединять элементы коллекции, используя listagg или UDF.

with
function collagg(p in sys.ku$_vcnt) return varchar2 is
result varchar2(4000);
begin
  for i in 1..p.count loop result := result || '; ' || p(i); end loop;
  return(substr(result,2));
end;
t(id, name, attr, val) as
( select 1, 'Ford',  'A', 'B' from dual union all
  select 1, 'Ford',  'B', 'C' from dual union all
  select 1, 'Ford',  'C', 'D' from dual union all
  select 2, 'BMW',   'A', 'B' from dual union all
  select 2, 'BMW',   'C', 'D' from dual union all
  select 2, 'BMW',   'F', 'G' from dual union all
  select 3, 'TESLA', 'Z', 'Y' from dual union all
  select 3, 'TESLA', 'E', 'F' from dual union all
  select 3, 'TESLA', 'A', 'B' from dual)
, t0 as
(select id, name, 
        cast(collect(cast(attr||':'||val as varchar2(4000))) as sys.ku$_vcnt) c
   from t t1
  group by id, name)
select t1.id baseid,
       t1.name basename,
       t2.id tgtid,
       t2.name tgtname,
       collagg(t1.c multiset intersect t2.c) matchedon,
       collagg(t1.c multiset except t2.c) baseonly,
       collagg(t2.c multiset except t1.c) tgtonly
  from t0 t1 join t0 t2 on t1.id < t2.id;
0 голосов
/ 01 февраля 2019

Это может быть быстрее:

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, который работает хуже взапросы.

...