Oracle: сравните перекрывающиеся отношения - PullRequest
0 голосов
/ 08 ноября 2018

Я хочу сравнить две записи на основе строк в связанной таблице. Допустим, у меня есть две таблицы: FOOS и BARS.

FOOS

+----+
| id |
+----+
| 1  |
| 2  |
| 3  |
+----+

BARS 

+--------+-------+
| foo_id | value |
+--------+-------+
| 1      |   1   |
| 1      |   2   |
| 1      |   3   |
| 2      |   1   |
| 2      |   2   |  
| 3      |   3   |
+--------+-------+

Таким образом, у каждого Foo есть коллекция баров.

  • Foo 1 имеет значения баров 1, 2, 3
  • Foo 2 имеет значения бара 1, 2
  • Foo 3 имеет значения бара 3

Я хочу узнать по идентификатору FOO, какие другие идентификаторы FOOS имеют такие же идентификаторы.

  • Foo 1 не имеет соответствующих foos.
  • Foo 2 соответствует Foo 1, потому что Foo 1 имеет значения бара 1 и 2.
  • Foo 3 соответствует Foo 1, потому что Foo 1 имеет значения бара 3.

Я хотел бы представить результат в виде таблицы:

+---------+----------+
| src_foo | targ_foo |
+---------+----------+
|    2    |    1     |
|    3    |    1     |
+---------+----------+

Вот скрипт установки БД, предоставляющий вышеуказанные данные:

create table foos (id number(32) not null) ;
create table bars (foo_id number(32) not null, val number(32) not null);

insert into foos (id) values (1);
insert into foos (id) values (2);
insert into foos (id) values (3);

insert into bars (val, foo_id) values (1, 1);
insert into bars (val, foo_id) values (2, 1);
insert into bars (val, foo_id) values (3, 1);
insert into bars (val, foo_id) values (1, 2);
insert into bars (val, foo_id) values (2, 2);
insert into bars (val, foo_id) values (3, 3);

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Вот альтернатива, которая не использует вложенные таблицы:

SELECT src_foo, targ_foo
FROM   (SELECT b1.foo_id targ_foo, b2.foo_id src_foo, b2.foo_id_val_count
        FROM   bars b1
               INNER JOIN (SELECT foo_id, value, count(*) OVER (PARTITION BY foo_id) foo_id_val_count FROM bars) b2 ON b1.foo_id != b2.foo_id AND b1.value = b2.value)
GROUP BY src_foo, targ_foo, foo_id_val_count
HAVING foo_id_val_count = COUNT(*)
ORDER BY src_foo, targ_foo;

Демо: https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=d1124734edcb943c0cc8b1166bd9f03f

Он работает, сначала выясняя, сколько строк имеется для каждого foo_id, а затем сравнивая каждый foo_id с другими foo_id, где значения одинаковы.

Если возвращается столько же строк, сколько строк для этого foo_id, вы знаете, что все значения присутствуют в этом foo_id. Если нет, то нам плевать на них.

Является ли это быстрее, чем решение с вложенными таблицами, или нет, решать вам за вашими данными и т. Д.

0 голосов
/ 08 ноября 2018

Мы могли бы сделать это, используя MULTISET операторы и COLLECT функцию.

Создать вложенную таблицу TYPE.

CREATE OR REPLACE TYPE numtab as TABLE OF NUMBER(32);

Редактировать

Мы могли бы использовать оператор SUBMULTISET для вложенных таблиц, который более прост и выполняет тот же метод, что и ниже. Спасибо @ mathguy

WITH t AS (
     SELECT foo_id,
            CAST(COLLECT(val) AS numtab) x
     FROM bars
     GROUP BY foo_id
)   
SELECT a.foo_id a_foo_id,
       b.foo_id b_foo_id
FROM t a
CROSS JOIN t b
WHERE a.x != b.x 
AND a.x SUBMULTISET OF b.x 

Другой метод

WITH t AS (
     SELECT foo_id,
            CAST(COLLECT(val) AS numtab) x
     FROM bars
     GROUP BY foo_id
) 
select a_foo_id,b_foo_id FROM
( SELECT a.foo_id a_foo_id,
         b.foo_id b_foo_id,
         a.x a_x,
         b.x b_x,
         a.x MULTISET INTERSECT b.x as i
  FROM t a
    CROSS JOIN t b
  WHERE a.x != b.x
) where i = a_x
  ;

Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...