MySQL如何找到具有确切子级集的父级?

MySQL如何找到具有确切子级集的父级?,mysql,sql,Mysql,Sql,MySQL 5.5 parent table: id | facts 但这也会得到一个包含这组子项的父记录:1,2,3,5,7,9。我只想要那些有确切孩子的父母:1,3,5,7,9 有办法吗 编辑: child.parent_id和child.fk都不是唯一的。child.fk是链接到另一个表的外键。多对多关系,因此父母很可能生下1,2,3,5,7,9个孩子。我执行此查询的全部原因是,如果已经存在这样的父项,则尽量避免为1,3,5,7,9创建新的父项。假设child.id对于每个child.pa

MySQL 5.5

parent table: id | facts 但这也会得到一个包含这组子项的父记录:1,2,3,5,7,9。我只想要那些有确切孩子的父母:1,3,5,7,9

有办法吗

编辑: child.parent_id和child.fk都不是唯一的。child.fk是链接到另一个表的外键。多对多关系,因此父母很可能生下1,2,3,5,7,9个孩子。我执行此查询的全部原因是,如果已经存在这样的父项,则尽量避免为1,3,5,7,9创建新的父项。

假设child.id对于每个child.parent\u id都是唯一的


这个问题称为精确关系划分。本文中有很多有用的代码和解释:

解决方法之一是:

SELECT p.id AS parent_id
FROM parent AS p
WHERE EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 1 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 3 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 5 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 7 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 9 AND c.parent_id = p.id)
  AND NOT EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk NOT IN (1,3,5,7,9) AND c.parent_id = p.id) ;

还有一个类似问题的链接,在StackOverflow,你会发现超过10种不同的解决方案注:这不是用于精确除法,而是用于带余数的除法和Postgres的性能测试:

类似于eggyal的解决方案,但我只是想把它作为一种替代方案,因为它应该更易于跨RDBMS移植

选择c.parent\u id 从child_表c 按c.parent\u id分组 当c.id在1,3,5,7,9中时有SUMCASE,那么1 ELSE-1 END=5 5是您希望在本例中匹配的in子句中的子项的精确计数

这只适用于不同的子级,如果存在重复项,它将中断


.

我认为依靠计数本身是不可靠的。eggyal的解决方案看起来更健壮,因为它还检查NOT值。为什么您认为COUNT*不可靠?我需要检查NOT值吗?尝试运行脚本并查看结果,但不检查NOT值。它似乎不起作用。这里检查一下我更新的这个小提琴,将您的查询与更新的模式一起使用:您在错误的列上查询。应该是b.fk而不是b.parent_ID。请看这里:我稍微修改了我的问题。子表是一个联接表,因此它应该是fk而不是id。fk当然可以有副本。如果这让人困惑,很抱歉。@ButtleButkus我们的意思是复合唯一列。UNIQUEparent\u ID、child\u ID和not UNIQUEparent\u ID和UNIQUEChild\u ID都是不同的。我想要的是能够让每一组子项都有一个唯一的父项。我不想让两个父母有完全相同的孩子。所以父母的id=1可以有孩子=1,3,5。而父_id=2可以有1,3,5,6或1,2,3,5或其他。我试图强制设置唯一性。@JW웃 是的,唯一的父母身份证,孩子身份证就是这样。@eggyal我认为这不太管用。查看此提琴:如果在IN部分中添加2,它将返回1和2。在这种情况下,它应该只返回1。您并没有定义表别名cOh,实际上是这样。这是可行的,但我希望可能会有更像eggyal的解决方案,因为它要短得多,并且不会涉及几十个子查询。有许多类似于eggyal的解决方案。不要相信,虽然那样短=更有效。测试以确定。感谢链接。看起来很有趣。我可能需要睡一觉。嗨@ypercube。这是离题的,但也许你有个主意。我上次回答了,但我把它删掉了,因为它因为使用左连接而被大量否决了…是空的。为什么不存在比左连接更有效的..为空?我做了一些测试,但它们的性能都是一样的。MySQL没有,只有Postgres、DB2和SQL Server有,Oracle有负号。@ypercube doh尝试在数据库之间切换,最后在SQL Server上测试:删除,直到我修复它…@ypercube:Oracle也有负号,DB2和其他几个也有负号,例如,Derby,H2,…@a_horse_,没有名字是的,thnx,我错过了DB2。这使得几乎所有的数据库管理系统都很好,最常见的数据库管理系统除了,除了MySQL:@ypercube:这就是MySQL的世界——在90年代,它的SQL特性被卡住了。
SELECT t1.`id` 
from `parent_table` t1 
  LEFT JOIN `child_table` t2 ON t1.id=t2.parent_id
WHERE t2.`fk` = 1 
  AND t2.`fk` = 3  
  AND t2.`fk` = 5 
  AND t2.`fk` = 7 
  AND t2.`fk` = 9
SELECT  a.id, a.facts
FROM    parent a
        INNER JOIN child b
            ON a.id = b.parent_ID
WHERE   b.id IN (1,3,5,7,9) AND        -- <<== list all ChildID here
        EXISTS                         -- <<== this part checks if the parent_ID
        (                              --           present on the EXISTS clause
            SELECT  parent_ID          --           which only filters parents
            FROM    child c            --           with 5 children
            WHERE   b.parent_ID = c.parent_ID
            GROUP   BY parent_ID
            HAVING  COUNT(*) = 5       -- <<== total number of children
        )
GROUP   BY a.id, a.facts
HAVING  COUNT(*) = 5                   -- <<== total number of children
SELECT   parent_id
FROM     child_table
GROUP BY parent_id
HAVING   SUM(id IN (1,3,5,7,9)) = COUNT(*)
     AND COUNT(DISTINCT id) = 5
SELECT p.id AS parent_id
FROM parent AS p
WHERE EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 1 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 3 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 5 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 7 AND c.parent_id = p.id)
  AND EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk = 9 AND c.parent_id = p.id)
  AND NOT EXISTS
      ( SELECT * FROM child AS c
        WHERE c.fk NOT IN (1,3,5,7,9) AND c.parent_id = p.id) ;