sql - 识别具有不一致关系的记录

标签 sql sql-server tsql

您可以运行所有这些 SQL 并查看结果 here .

  • 跳到结果和随之而来的问题以了解问题的实质。

  • 我有一张俱乐部表(俱乐部就像一群人或一群人,比如“游泳俱乐部”或“针织俱乐部”)。
    DECLARE @club TABLE (
       Id INT
       ,Name NVARCHAR(255)
       );
    INSERT INTO @club VALUES
       (1, 'Swim Club')
       ,(2, 'Knitting Club')
       ,(3, 'Bridge Club');
    

    我有一张成员(member)表。
    DECLARE @member TABLE (
       Id INT
       ,Name NVARCHAR(255)
       );
    INSERT INTO @member VALUES
       (1, 'John Jones')
       ,(2, 'Sally Smith')
       ,(3, 'Rod Roosevelt')
       ,(4, 'Bobby Burns')
       ,(5, 'Megan Moore');
    

    成员(member)可以属于许多俱乐部,因此有一个成员(member)表,将俱乐部与成员(member)联系起来(还描述了会费价格)。
    DECLARE @membership TABLE (
       Id INT
       ,Member INT --FK to @member
       ,Club INT --FK to @club
       ,Dues INT --the cost of membership
       );
    INSERT INTO @membership VALUES
       (1,1,1,10)
       ,(2,1,2,5)
       ,(3,2,1,10)
       ,(4,2,3,20)
       ,(5,3,1,10)
       ,(6,3,2,5)
       ,(7,4,2,5)
       ,(8,4,3,20)
       ,(9,5,1,10)
       ,(10,5,3,20);
    

    大多数成员(member)只需支付相关的会费。但是,有些成员(member)是由其他成员(member)赞助的。因此,这些被赞助成员(member)的会费将由另一名成员(member)(赞助商)支付。因此,我们有一个赞助表。赞助表将赞助商(支付会费)连接到特定俱乐部的赞助商(由赞助商支付会费)。因为赞助是特定于俱乐部的,所以赞助记录连接两个成员(member)记录而不是两个成员(member)记录。
    DECLARE @sponsorship TABLE (
       Id INT
       ,Sponsee_Membership INT --FK to Sponsee's @membership record
       ,Sponsor_Membership INT --FK to Sponsor's @membership record
       );
    INSERT INTO @sponsorship VALUES
       (1,5,1)
       ,(2,8,4)
       ,(3,9,3)
       ,(4,10,4);
    

    为了全面了解我们的俱乐部/成员(member)/赞助商,我们有:
    SELECT
        mship.Id AS 'Mship'
        ,mem.Name AS 'Member'
        ,c.Name AS 'Club'
        ,mship.Dues
        ,spons_mem.Name AS 'Sponsor'
    FROM
        @membership AS mship
        JOIN @member AS mem
            ON mship.Member = mem.Id
        JOIN @club AS c
            ON mship.Club = c.Id
        LEFT JOIN @sponsorship AS spons
            ON spons.Sponsee_Membership = mship.Id
        LEFT JOIN @membership AS spons_mship
            ON spons_mship.Id = spons.Sponsor_Membership
        LEFT JOIN @member AS spons_mem
            ON spons_mem.Id = spons_mship.Member;
    

    这给了我们这些 结果 :
    Mship   Member        Club         Dues  Sponsor
      1   John Jones     Swim Club      10    NULL
      2   John Jones     Knitting Club   5    NULL
      3   Sally Smith    Swim Club      10    NULL
      4   Sally Smith    Bridge Club    20    NULL
      5   Rod Roosevelt  Swim Club      10    John Jones
      6   Rod Roosevelt  Knitting Club   5    NULL
      7   Bobby Burns    Knitting Club   5    NULL
      8   Bobby Burns    Bridge Club    20    Sally Smith
      9   Megan Moore    Swim Club      10    Sally Smith
     10   Megan Moore    Bridge Club    20    Sally Smith
    

    赞助应该涵盖所有共享成员资格。
  • 也就是说,如果 Sally 赞助 Bobby,任何时候他们都在同一个俱乐部,Sally 将被识别为 Bobby 的赞助商。
  • 我们可以在行 Mship=7 中看到这一点和 Mship=8 .
  • Bobby 和 Sally 都在桥牌俱乐部,因此 Sally 被确定为 Bobby 的桥牌俱乐部成员(member)的赞助商。
  • Sally 不是 Knitting Club 的成员,因此 Bobby 的 Knitting Club 成员(member)身份并未将 Sally 显示为赞助商。

  • 抱歉设置太长了。 这是我的实际问题 :
  • 我如何确定哪里缺少赞助?
    在示例中,我们有行 Mship=5Mship=6 .
  • 约翰是罗德的赞助商。
  • 我们可以看到对罗德游泳俱乐部成员(member)资格的赞助。
  • 罗德和约翰也是针织俱乐部的成员,
  • 但是 罗德没有将约翰显示为他的针织俱乐部成员(member)资格的赞助商 .
  • 这是不正确的,这就是我所追求的。
  • 我想查询所有此类缺失的赞助 .

  • 我可以使用游标/WHILE 循环来完成此操作,但我知道此类解决方案通常不会采用适当的基于集合的方法。对此的正确查询是什么样的?
    非常感谢。

    最佳答案

    这是一个可能响应您的要求的 SQL 查询。

    逻辑是使用子查询根据映射member.id生成赞助商和赞助商之间的映射。而不是 memberships.id ;为此,我们使用聚合。然后,外部查询搜索赞助商和赞助商都参加但在赞助表中没有声明关系的俱乐部

    该查询为每个违规成员返回一条记录,其中包含 sponsee 和赞助商名称。

    SELECT mship1.Id, m1.Name Member, m2.Name Sponsor, c.Name Club, mship1.Dues
    FROM 
        @membership mship1
        INNER JOIN @club c ON c.Id = mship1.Club
        INNER JOIN (
            SELECT ms1.Member Sponsee_Member , MAX(ms2.Member) Sponsor_Member
            FROM @sponsorship ss
            INNER JOIN @membership ms1 ON ms1.Id = Sponsee_Membership
            INNER JOIN @membership ms2 ON ms2.Id = Sponsor_Membership
            GROUP BY ms1.Member
        ) rels ON rels.Sponsee_Member = mship1.Member
        INNER JOIN @membership mship2 ON mship2.Member = rels.Sponsor_Member AND mship2.Club = mship1.Club
        INNER JOIN @member m1 ON m1.Id = mship1.Member
        INNER JOIN @member m2 ON m2.Id = mship2.Member
        LEFT JOIN @sponsorship sship  ON sship.Sponsor_Membership = mship2.Id
    WHERE sship.Id IS NULL
    ;
    

    the rextester that you provided ,这将返回:
    Id   | Member          | Sponsor      | Club           | Dues
    -----|-----------------|--------------|----------------|-----
    6    | Rod Roosevelt   | John Jones   | Knitting Club  | 5
    

    创建此查询的工作让我想到您可以优化数据库设计。当前的模型将难以保持一致性:您的问题本身就证明了这一点。将来,如果赞助商注册一个新俱乐部,而他的一名赞助商已经参加了,会发生什么?您将再次需要检测丢失的 sponshorship关系,并以某种方式创建它。

    您实际上在赞助商和 sponsee 之间建立了 1-1 关系,因为您声明赞助应该涵盖所有共享成员资格。看起来您不会允许赞助商拥有多个赞助商,即使是跨不同的俱乐部。

    我建议你放弃 sponshorship表并直接在成员表中存储发起人的自外键。从那里开始,很容易检查两个成员有哪些共同点,并使用 SQL 查询正确分配会费。

    关于sql - 识别具有不一致关系的记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54754812/

    相关文章:

    sql - Firebird SQL子字符串功能不起作用

    sql - 如何在单个查询中使用 count ,'like' 和 group by?

    mysql - 更新 MYSQL 中现有的唯一键列

    .net - 在队列之间发送时 TransactionScope 提升到 MSDTC?

    sql-server - 垂直表结构的 T-SQL 查询

    java - 防止Java程序中的SQL注入(inject)攻击

    sql - T-SQL员工层次结构递归查询

    sql-server - 用于从 SQL Server 数据库检索数据的用户友好工具

    sql - 在 SQL 中而不是在 Group By 中选择列

    sql - SQL 中的递归 - 公用表表达式与 WHILE