【问题标题】:SQL select a parent with children only inSQL选择一个只有孩子的父母
【发布时间】:2024-01-03 09:13:01
【问题描述】:

如果你有父表

create table parent (
  pid int not null,
  name varchar(255)
)

还有一个父子连接表

create table parent_child (
  pid int not null,
  cid int not null,
  foreign key (pid) references parent(pid),
  foreign key (cid) references child(cid)
)
create table child(
  cid int not null,
  name varchar(255)
)

我如何在以下列表('dave','henry','myriam','jill')中找到所有孩子的名字的所有父母的名字。

如果父母有一个不同名字的孩子,我不想看到他们,但是如果他们有 1 个或多个孩子并且他们的所有孩子的名字都在列表中,我想看到父母的名字。

我确实找到了这个https://*.com/a/304314/1916621,这将帮助我找到一个拥有这些名字的孩子的父母,但我不知道如何找到那些只有孩子名字在该列表子集中的父母。

如果有人知道不同方法的性能权衡,则加分。

【问题讨论】:

    标签: sql tags parent subset


    【解决方案1】:
    SELECT 
        p.pid, 
        p.name
    FROM 
        parent p
    WHERE NOT EXISTS (
        SELECT *
        FROM 
            parent_child pc 
            JOIN child c 
                ON pc.cid = c.cid
                AND c.name NOT IN ('dave','henry','myriam','jill')
        WHERE 
            p.pid = pc.pid
    ) AND EXISTS (
        SELECT *
        FROM 
            parent_child pc 
            JOIN child c 
                ON pc.cid = c.cid
                AND c.name IN ('dave','henry','myriam','jill')
        WHERE 
            p.pid = pc.pid
    )
    

    另一种方法...没有子查询,但需要额外的DISTINCT 来消除parent 记录的重复加入parent_child 表。

    SELECT DISTINCT
        p.pid, 
        p.name
    FROM 
        parent p 
        JOIN parent_child pc_exists ON pc_exists.pid = p.pid
        JOIN child c_exists 
            ON c_exists.cid = pc_exists.cid
            AND c_exists.name IN ('dave','henry','myriam','jill')
        LEFT JOIN parent_child pc_notExists ON pc_notExists.pid = p.pid
        LEFT JOIN child c_notExists 
            ON c_notExists.cid = pc_notExists.cid
            AND c_notExists.name NOT IN ('dave','henry','myriam','jill')
    WHERE
        c_notExists.cid IS NULL
    

    【讨论】:

    • 我认为是第一个正确的解决方案...但是如果有一个具有许多不同名称的大型数据库,那么“不存在”中的选择会花费太长时间吗?想知道是否有可能没有子查询。
    • @user1916621 让我添加一个没有子查询的示例,但我敢打赌,两个查询要么使用相同的执行计划,要么发布在这里的not exists / exists 查询会更快。 . 因为我必须在另一种方法中添加一个额外的distinct 运算符。了解哪种方式更快的最佳方法是尝试两种方式。
    【解决方案2】:

    这是我的中等赌注:

    示例表:

    家长

    PID     NAME
    1       dad john
    2       mum sandy
    3       dad frank
    4       mum kate
    5       mum jean
    

    儿童

    CID     NAME
    11      dave
    22      maryam
    33      henry
    44      maryam
    16      jill
    17      lina
    23      jack
    34      jill
    55      dave
    

    Parent_Child

    PID     CID
    1       11
    1       16
    1       17
    2       22
    3       33
    4       44
    2       23
    5       55
    3       34
    

    查询:

    select p.pid, p.name, 
    group_concat(c.name) as children
    from parent as p
    inner join parent_child as pc
    on p.pid = pc.pid
    join child as c
    on pc.cid = c.cid
    where c.name
    in ('dave','henry','maryam','jill')
    group by p.pid
    ;
    

    结果:

    PID     NAME        CHILDREN
    1       dad john    dave,jill
    2       mum sandy   maryam
    3       dad frank   henry,jill
    4       mum kate    maryam
    5       mum jean    dave
    

    使用 REGEXP 和 GROUP_CONCAT

    对于 SQL,它比 infind_in_set 要好得多。我所做的更改,我将列表用作comma delimitted string ;)

    *但这里的问题是:group_concat 字符串顺序必须在逗号分隔的字符串中找到。* 除非我们让REGEXP 更高效:)

    查询:

    select x.pid, x.name,
    x.children from(
    select p.pid, p.name, 
    group_concat(c.name) as children,
      count(c.name) as counts
    from parent as p
    inner join parent_child as pc
    on p.pid = pc.pid
    join child as c
    on pc.cid = c.cid
    group by p.pid) as x
    where 'dave,maryam,henry,jill'
    REGEXP x.children
    ;
    

    结果:

    PID     NAME        CHILDREN
    3       dad frank   henry,jill
    4       mum kate    maryam
    5       mum jean    dave
    

    *SQLFIDDLE

    【讨论】:

    • +1 完成创建 Sql Fiddle 的工作,但我认为应该排除 PID 1,因为该父级还有一个不在列表中的子级 (lina)... .
    • @MichaelFredrickson 正是 OP 想要列表中的一个孩子或更多孩子(=一位父母的所有孩子)的父母。棘手的intersect 类型的查询,我向你致敬:) +1
    • @MichaelFredrickson 我只是想了解一下我对REGEXP 所做的更新的看法。您认为使用更好的正则表达式模式会有效吗?
    • 看起来它得到了正确的结果(+1)......至于效率,我不确定 - 可能很大程度上取决于所涉及的数据。 REGEXP 将混淆任何索引,因此与调整正则表达式所节省/花费的性能相比,这可能会产生更大的性能损失......我不是一个正则表达式的人,所以不幸的是我不能推荐一个尝试更有效的模式...如果您最终将这种方法与其他解决方案之一进行比较,我很想知道它的比较方式。
    • @MichaelFredrickson 感谢分享信息。好吧,我测试了您的两个查询。很抱歉在这件事上困扰着你。我执行的方式可能有些奇怪,似乎并没有产生我们需要的东西。 SQLFIDDLE
    最近更新 更多