Oracle:比较重叠关系

时间:2018-11-08 14:44:16

标签: oracle set

我想根据相关表中的行比较两个记录。假设我有两个表,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 ID中找出其他FOOS具有相同ID的情况。

  • 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 :(得分:3)

我们可以使用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
  ;

Demo

答案 1 :(得分:2)

这是不使用嵌套表的替代方法:

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与其他val_id相同的foo_id进行比较来进行工作。

如果返回的行数与该foo_id的行数相同,则您知道所有val都存在于该foo_id中。如果没有,那么我们就不在乎它们。

这是否比嵌套表解决方案要快,取决于您来查找数据等。