需要一种方法来查找两个多对多关系之间的匹配项

时间:2018-10-24 19:55:41

标签: sql sql-server

给出下表:

   ------------     ------------
   |     BA   |     |    CA    | 
   ------+-----     ------+-----
   | BId|| AId|     | CId|| AId|
   ------+------     ------+-----
   |  B1 |  2 |     |  C1 |  2 |
   |  B1 |  3 |     |  C2 |  2 |
   |  B2 |  2 |     |  C2 |  3 |
   |  B2 |  4 |     |  C3 |  4 |
   |  B2 |  5 |     |  C3 |  5 |
   ------------     ------------

我该如何编写查询以仅返回那些(DISTINCT)CId,其中具有相同CId的任何CA行集合都从BA中相似的行集合中找到(AId的)完全匹配?

在上面的示例中,应返回C2,因为CA中具有[C2,2]和[C2,3]的行(可以将其称为{(n,2),(n,3)的集合) }?)在BA中找到一个完全匹配的内容:它的前两行-[B1,2]和[B1,3](我们可以将此集合称为{(m,2),(m,3)}吗?)

3 个答案:

答案 0 :(得分:1)

可以使用CABA的CTE计算元素数量。然后,您可以通过以下方式获取完整行:

SQL

with ca_info as (
      select
           cid
         , count(*) as ccount
        from ca
      group by cid
  ),
ba_info as (
      select
           bid
         , count(*) as bcount
        from ba
      group by bid
  )
select
*
from
    ba
    join ca on (ba.aid = ca.aid)
    join ba_info on ba.bid=ba_info.bid
    join ca_info on ca.cid=ca_info.cid
where ccount = bcount

SQL Fiddle

结果
bid     aid     cid     aid     bid     bcount  cid     ccount
B1      2       C2      2       B1      2       C2      2
B1      3       C2      3       B1      2       C2      2

如果您只对C2本身感兴趣,则可以进一步限制结果集:

with ca_info as (
      select
           cid
         , count(*) as ccount
        from ca
      group by cid
  ),
ba_info as (
      select
           bid
         , count(*) as bcount
        from ba
      group by bid
  )
select
distinct ca.cid
from
    ba
    join ca on (ba.aid = ca.aid)
    join ba_info on ba.bid=ba_info.bid
    join ca_info on ca.cid=ca_info.cid
where ccount = bcount

超集

要获得子集/超集,可以更改强制执行集合相等的条件:

where ccount <= bcount

这将返回所有集合,其中Bx至少与Cy一样多:

bid    aid    cid    aid    bid    bcount    cid    ccount
B1     2      C1     2      B1     2          C1    1
B1     2      C2     2      B1     2          C2    2
B1     3      C2     3      B1     2          C2    2
B2     2      C1     2      B2     3          C1    1
B2     2      C2     2      B2     3          C2    2
B2     4      C3     4      B2     3          C3    2
B2     5      C3     5      B2     3          C3    2

答案 1 :(得分:1)

执行此操作的一种方法是使用JOINCOUNT。您首先要计算CIds(每个CIds重复多少次)。然后,对BIds执行相同的操作。然后,您加入BA并链接(AIds)和COUNT。您的目标是匹配ID及其AId的数量。

示例:

DECLARE 
    @a TABLE(id INT)

INSERT INTO @a VALUES 
(1),
(2),
(3),
(4),
(5),
(6)

DECLARE 
    @b TABLE(id CHAR(2))

INSERT INTO @b VALUES 
('B1'),
('B2'),
('B3')

DECLARE 
    @c TABLE(id CHAR(2))

INSERT INTO @c VALUES 
('C1'),
('C2'),
('C3')


DECLARE 
    @ba TABLE(BId CHAR(2), AId INT)

INSERT INTO @ba VALUES 
('B1',2),
('B1', 3),
('B2', 2),
('B2', 4),
('B2', 5)

DECLARE 
    @ca TABLE(CId CHAR(2), AId INT)

INSERT INTO @ca VALUES 
('C1',2),
('C2',2),
('C2',3),
('C3',4),
('C3',5)




SELECT DISTINCT CId 
FROM (
SELECT *
,   COUNT(*) OVER(PARTITION BY CId) cnt
FROM @ca ca
) c
LEFT JOIN (
    SELECT *
    ,   COUNT(*) OVER(PARTITION BY BId) cnt
    FROM @ba ba
) b ON b.AId = c.AId AND b.cnt = c.cnt 
WHERE 
    b.cnt IS NOT NULL 

因此,在示例中,C2重复了2次,在BA中,B1也重复了2次。这是第一个条件,第二个条件是匹配两个AIds,如果它们相同,则您具有组匹配。

答案 2 :(得分:1)

我认为最简单的解决方案使用窗口函数:

select ca.cid, ba.bid
from (select ca.*, count(*) over (partition by cid) as cnt
      from ca
     ) ca join
     (select ba.*, count(*) over (partition by bid) as cnt
      from ba
     ) ba
     on ca.aid = ba.aid and ca.cnt = ba.cnt
group by ca.cid, ba.bid, ca.cnt
having ca.cnt = count(*)  -- all match

Here是db <>小提琴。

结果集都是匹配的cid / bid对。

这里的逻辑非常简单。对于每个cidbid,子查询都会计算aid的计数。此数字必须匹配。

然后joinaid上-这是一个内部联接,因此仅生成匹配对。最后的group by用于生成匹配计数,以查看是否与所有aid相符。

此特定版本假定每个表中的行都是唯一的,尽管如果不是这种情况,可以轻松调整查询。