sql选择具有匹配子集的记录

sql选择具有匹配子集的记录,sql,sql-server,tsql,Sql,Sql Server,Tsql,有两组员工:经理和员工。 对于每位经理,都有一张经理会议表,其中列出了每位经理参加的会议。一个类似的表格grunt_meetings列出了每个grunt参加的会议 因此: 老板不喜欢经理和咕噜人知道完全相同的信息。这使他的头很痛。他想确定这种情况,这样他就可以将经理降职为咕噜,或者将咕噜提升为经理,或者让他们一起打高尔夫球。老板喜欢打高尔夫球 任务是列出经理和grunt的每一个组合,他们都参加了完全相同的会议。如果经理参加的会议比呼噜声多,那就没有对手了。如果咕噜人参加的会议比经理多,那就没有对

有两组员工:经理和员工。
对于每位经理,都有一张经理会议表,其中列出了每位经理参加的会议。一个类似的表格grunt_meetings列出了每个grunt参加的会议

因此:

老板不喜欢经理和咕噜人知道完全相同的信息。这使他的头很痛。他想确定这种情况,这样他就可以将经理降职为咕噜,或者将咕噜提升为经理,或者让他们一起打高尔夫球。老板喜欢打高尔夫球

任务是列出经理和grunt的每一个组合,他们都参加了完全相同的会议。如果经理参加的会议比呼噜声多,那就没有对手了。如果咕噜人参加的会议比经理多,那就没有对手了

预期结果如下:

ManagerID            GruntID
2                    7
1                    5 
…因为经理2和grunt 7都参加了(a,b),而经理1和grunt 5都参加了(a,b,c)

我可以用一种笨拙的方式来解决它,方法是将子查询中的会议子集转换成XML,并将每个grunt的XML列表与每个经理的XML进行比较。但这太可怕了,我还必须向所有者解释什么是XML。我不喜欢打高尔夫球

有没有更好的方法来执行
“WHERE{subset1}={subset2}”
?感觉好像我错过了一些聪明的加入


以下是一个有效的版本:

select m.mId, g.gId, count(*) --select m.mid, g.gid, mm.meetingid, gm.meetingid as gmm
from manager m cross join
     grunt g left outer join
     (select mm.*, count(*) over (partition by mm.mid) as cnt
      from manager_meeting mm
     ) mm
     on mm.mid = m.mId full outer join
     (select gm.*, count(*) over (partition by gm.gid) as cnt
      from grunt_meeting gm
     ) gm
     on gm.gid = g.gid and gm.meetingid = mm.meetingid 
group by m.mId, g.gId, mm.cnt, gm.cnt
having count(*) = mm.cnt and mm.cnt = gm.cnt;
字符串比较方法更短,可能更容易理解,也可能更快

编辑:

对于获取精确匹配的特定情况,可以简化查询:

select mm.mId, gm.gId
from (select mm.*, count(*) over (partition by mm.mid) as cnt
      from manager_meeting mm
     ) mm join
     (select gm.*, count(*) over (partition by gm.gid) as cnt
      from grunt_meeting gm
     ) gm
     on gm.meetingid = mm.meetingid and
        mm.cnt = gm.cnt
group by mm.mId, gm.gId
having count(*) = max(mm.cnt);
在性能和清晰度方面,这可能比字符串版本更具竞争力


它统计grunt和manager之间的匹配数。然后,它检查这是否是每个人的所有会议。

一个替代版本-但需要另一个表。基本上,我们给每一次会议赋予两个不同的幂,因为它是“价值”,然后将每一位经理的会议价值和每一位员工的会议价值相加。在他们相同的地方,我们有一场比赛

应该可以使
满足_值
表成为TVF,但这稍微简单一点

附加表:

CREATE TABLE meeting_values (value INT, meetingID CHAR(1));
INSERT INTO meeting_values VALUES
 (1,'a'),(2,'b'),(4,'c'),(8,'d'),(16,'e');
以及查询:

SELECT managemeets.mID, gruntmeets.gID
FROM
    ( SELECT gm.gID, sum(value) AS meeting_totals
      FROM grunt_meeting gm 
             INNER JOIN 
       meeting_values mv ON gm.meetingID = mv.meetingID
      GROUP BY gm.gID 
    ) gruntmeets 
     INNER JOIN
    ( SELECT mm.mID, sum(value) AS meeting_totals
      FROM manager_meeting mm 
             INNER JOIN 
           meeting_values mv ON mm.meetingID = mv.meetingID
      GROUP BY mm.mID 
    ) managemeets ON gruntmeets.meeting_totals = managemeets.meeting_totals
复仇的尝试——一个:

基本上,从经理的一系列会议中减去grunt的一系列会议,然后反过来。如果两个结果都不包含行,则grunt和经理参加了同一组会议


请注意,此查询将匹配从未参加过一次会议的经理和抱怨者。

好吧,SQL Fiddle+1,一个有趣的背景故事,一次自己解决问题的尝试,以及一个具有挑战性的问题。要是所有的问题都能如此彻底就好了!哦,但是-1代表::-)我保证,我的产品代码中没有长字符声明!在模糊代码的同时采取了一些快捷方式。实际上,我并不想-1只是想对任何读者提及这个坏习惯。。在我看来,XML方法是完全合理的+回答得好。我在一个切线上尝试做一些聪明的事情,除了/相交或交叉应用,很快就感觉失败了。@AaronBertrand。我能改进它一点。聪明的方法,尽管我们很快就耗尽了两个的能力。虽然我很想告诉大家,“我们不能再开会了,我们已经有63次了。”太好了。我也开始走上了INTERSECT/EXCEPT这条路,但像Aaron一样,我放弃了一些嵌套版本的“a不包含B或B包含a但不包含C”之类的东西。
SELECT managemeets.mID, gruntmeets.gID
FROM
    ( SELECT gm.gID, sum(value) AS meeting_totals
      FROM grunt_meeting gm 
             INNER JOIN 
       meeting_values mv ON gm.meetingID = mv.meetingID
      GROUP BY gm.gID 
    ) gruntmeets 
     INNER JOIN
    ( SELECT mm.mID, sum(value) AS meeting_totals
      FROM manager_meeting mm 
             INNER JOIN 
           meeting_values mv ON mm.meetingID = mv.meetingID
      GROUP BY mm.mID 
    ) managemeets ON gruntmeets.meeting_totals = managemeets.meeting_totals
SELECT
  m.mID,
  g.gID
FROM
  manager AS m
INNER JOIN
  grunt AS g
ON  NOT EXISTS (
  SELECT meetingID
  FROM   manager_meeting
  WHERE  mID = m.mID
  EXCEPT
  SELECT meetingID
  FROM   grunt_meeting
  WHERE  gID = g.gID
)
AND NOT EXISTS (
  SELECT meetingID
  FROM   grunt_meeting
  WHERE  gID = g.gID
  EXCEPT
  SELECT meetingID
  FROM   manager_meeting
  WHERE  mID = m.mID
);