【问题标题】:SQL Select only rows where exact multiple relationships existSQL 仅选择存在确切多个关系的行
【发布时间】:2013-01-01 18:55:27
【问题描述】:

这与this question密切相关,但增加了另一个要求。

给定一个父表'parent'

╔════════════╦════════╗
║ PARENT_ID  ║ NAME   ║
╠════════════╬════════╣
║         1  ║ bob    ║
║         2  ║ carol  ║
║         3  ║ stew   ║
╚════════════╩════════╝

以及父级和(此处未指定)属性表之间的多对多关系表“rel”

╔════════════╦══════════╗
║ PARENT_ID  ║ PROP_ID  ║
╠════════════╬══════════╣
║         1  ║       5  ║
║         1  ║       1  ║
║         2  ║       5  ║
║         2  ║       4  ║
║         2  ║       1  ║
║         3  ║       1  ║
║         3  ║       3  ║
╚════════════╩══════════╝

如何选择具有所有一组指定关系的所有父母?例如。使用示例数据,我怎样才能找到与属性 5 和 1 有关系的所有父母?

【问题讨论】:

    标签: mysql sql


    【解决方案1】:
    SELECT PARENT_ID
    FROM rel
    GROUP BY PARENT_ID
    HAVING SUM(PROP_ID NOT IN (5,1)) = 0
       AND SUM(PROP_ID = 1) = 1 
       AND SUM(PROP_ID = 5) = 1
    

    【讨论】:

    • +1 但这并不完全正确。如果缺少一个,它仍然有效。
    • 感谢您的提示。我修好了。
    • 是的。我认为这就是OP想要的。否则我可以改成HAVING SUM(PROP_ID NOT IN (5,1)) = 0 AND SUM(PROP_ID = 1)=1 and SUM(PROP_ID = 5)=1
    • @sorencito:不知道你在说什么minusoperator。
    • @fthiella 这不太行,因为两个 5 和没有 1 仍然会通过。
    【解决方案2】:

    此替代方案的优点是语句结构恒定且只有一个参数,与您要查找的关系数量无关:

    SELECT parent_id FROM rel 
    GROUP BY parent_id 
    HAVING GROUP_CONCAT(prop_id ORDER BY prop_id ASC SEPARATOR ",") = '1,5';
    

    缺点:

    • 您需要预先准备一个有序的、逗号分隔的 prop_id 字符串。
    • 这适用于 MySQL,但不适用于所有数据库服务器。

    【讨论】:

      【解决方案3】:

      如果你想选择所有至少一个5和一个1的父母,你可以使用:

      SELECT PARENT_ID
      FROM rel
      GROUP BY PARENT_ID
      HAVING SUM(PROP_ID = 1)
             AND SUM(PROP_ID = 5)
             AND SUM(PROP_ID NOT IN (5,1)) = 0
      

      如果您正好需要一个5和一个1,请参阅this答案

      【讨论】:

      • @juergend 是的,但我刚刚意识到它有点错误。它至少强制每个之一,但不完全每个之一。
      【解决方案4】:

      有两个嵌套的子查询,像这样..

       Select pa.Id
       From parents pa
       Where not exists -- This ensures that all specifies properties exist
          (Select * From property y
           Where propertyId In (1,5)
               And Not Exists
                   (Select * From parentProperty
                    Where parentId = pa.parentId 
                        And propertyId = y.propertyId ))
         And not exists -- This ensures that only specified list of properties exist
          (Select * From parentProperty
           Where parentId = pa.parentId 
              And propertyId Not In (1,5) )
      

      第一个读 "显示所有父级,其中 属性的指定列表中的属性不在指定父级的父级属性表中......"

      第二个子查询如下: “还要确保 parentProperties 表中不存在指定列表中的任何属性的父属性的记录。”

      【讨论】:

        【解决方案5】:
        SELECT PARENT_ID
        FROM rel
        GROUP BY PARENT_ID
        HAVING
          COUNT(PROP_ID)=2 AND
          COUNT(DISTINCT case when PROP_ID IN ( 1, 5 ) then PROP_ID end)=2
        

        这将选择恰好有两行的所有PARENT_ID,其中恰好有两个不重复的PROP_ID 匹配。

        【讨论】:

          【解决方案6】:

          假设 (PARENT_ID, PROP_ID) 是唯一的:

          SELECT r1.PARENT_ID
          FROM rel r1
          INNER JOIN rel r2 ON r1.PARENT_ID = r2.PARENT_ID AND r2.PROP_ID = 5 
          INNER JOIN rel r3 ON r1.PARENT_ID = r3.PARENT_ID AND r3.PROP_ID = 1
          GROUP BY r1.PARENT_ID
          HAVING COUNT(*) = 2
          

          或者,

          SELECT parent.PARENT_ID
          FROM parent
          INNER JOIN
          (
              SELECT PARENT_ID
              FROM rel
              WHERE PROP_ID IN (1,5)
              GROUP BY PARENT_ID
              HAVING COUNT(*) = 2
          ) good ON parent.PARENT_ID = good.PARENT_ID
          LEFT OUTER JOIN rel bad ON parent.PARENT_ID = bad.PARENT_ID 
              AND bad.PROP_ID NOT IN (1,5)
          WHERE bad.PARENT_ID IS NULL
          

          甚至,

          SELECT DISTINCT parent.PARENT_ID
          FROM parent
          INNER JOIN rel r2 ON parent.PARENT_ID = r2.PARENT_ID AND r2.PROP_ID = 5 
          INNER JOIN rel r3 ON parent.PARENT_ID = r3.PARENT_ID AND r3.PROP_ID = 1
          LEFT OUTER JOIN rel r0 ON parent.PARENT_ID = r0.PARENT_ID 
              AND r0.PROP_ID NOT IN (1,5)
          WHERE r0.PARENT_ID IS NULL
          

          【讨论】:

            【解决方案7】:

            如果 MySql 支持minus,查询可能如下所示:

            select parent_id
            from rel
            where prop_id in (5,1)
            group by parent_id
            having count(distinct prop_id)=2 and count(prop_id)=2
            minus
            select parent_id
            from rel
            where prop_id not in (5,1);
            

            not in 将删除那些超过 (5,1) 的关系,例如(5,1,3)。

            我知道您正在使用 MySql,因此我的回答是错误的。把它当作另一个想法吧。

            【讨论】:

            • MySql 不支持减号,但即使支持,您的查询也会返回只有一个属性匹配的 parent_id 以及存在重复属性的 parent_id。请看这个小提琴sqlfiddle.com/#!4/ed577/6/0 第二个查询应该是正确的
            • @fthiella 感谢您的输入 - 我根据您的小提琴更正了查询。没想到表中包含重复项,也没有注意到第一个 IN 子句中的错误。
            【解决方案8】:

            即使 (PARENT_ID, PROP_ID) 不是唯一的,此查询也为真:

            SELECT PARENT_ID FROM rel WHERE
            PROP_ID IN (5,1) AND 
            PARENT_ID NOT IN (SELECT DISTINCT PARENT_ID FROM rel WHERE PROP_ID NOT IN (5,1))
            GROUP BY PARENT_ID HAVING COUNT(DISTINCT PROP_ID) = 2
            

            【讨论】:

              【解决方案9】:

              希望对您有所帮助:

              SELECT p.PARENT_ID , r.PROP_ID FROM rel r LEFT JOIN parent p ON p.PARENT_ID = r.PARENT_ID WHERE r.PROP_ID = 5 OR r.PROP_ID = 1
              

              【讨论】:

              • 输出错误,因为它包含重复项以及与 (5,1) 以外的其他关系的 parent_id。
              【解决方案10】:

              您可以通过分组来做到这一点。

              选择 PARENT_ID FROM link_tbl PROP_ID 在哪里 (5,1) 按 PARENT_ID 分组 有 COUNT(*) = 2

              【讨论】:

              • HAVING 中的计数是在WHERE 过滤器之后应用的,因此它们可能还有其他计数。
              猜你喜欢
              • 2012-12-15
              • 2015-10-14
              • 1970-01-01
              • 1970-01-01
              • 2012-12-01
              • 1970-01-01
              • 2020-12-22
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多