【问题标题】:Parent/child join query with and without conditions on the child父/子连接查询有和没有条件的孩子
【发布时间】:2020-01-25 11:45:12
【问题描述】:

架构和示例数据

简单的一对多父/子关系:

CREATE TABLE Parent (
  id VARCHAR PRIMARY KEY
);

CREATE TABLE Child (
  id VARCHAR PRIMARY KEY,
  type INT,
  parent_id VARCHAR REFERENCES Parent(id)
);

INSERT INTO Parent VALUES ('p1'), ('p2'), ('p3'), ('p4');
INSERT INTO Child VALUES ('c1', 0, 'p2'), ('c2', 42, 'p2'), ('c3', 0, 'p3'), ('c4', 42, 'p4');

SQL Fiddle

查询

我们总是询问有孩子的父母。每当特定父级是结果的一部分时,在任何情况下都应该检索其所有子级。

用例 A)

对孩子没有限制 - 只显示所有父/子数据,包括没有孩子的父母 (p1)。

LEFT JOIN 查询

SELECT p.*, c.* FROM parent p
LEFT JOIN child c ON c.parent_id = p.id

产生所需的输出:

id|id|type|
--|--|----|
p1|  |    |
p2|c1|   0|
p2|c2|  42|
p3|c3|   0|
p4|c4|  42|

用例 B)

对孩子有限制,例如type = 42。我们想检索具有type = 42any 孩子的父母(及其所有 孩子),即期望的输出是:

id|id|type|
--|--|----|
p2|c1|   0|
p2|c2|  42|
p4|c4|  42|

注意c1 被检索是因为它的父p2 有一个匹配的子c2,即使c1 的类型不匹配。

这个带有相关子查询的查询实现了这个结果:

SELECT p.*, c.* FROM parent p
JOIN child c ON c.parent_id = p.id
WHERE EXISTS (SELECT 1 FROM child c2 WHERE c2.parent_id = p.id AND c2.type = 42)

问题

1) 第二个查询 WHERE EXISTS 是用例 B) 的标准方法吗?

2) 由于在现实生活中存在多种关联和多种可能的过滤条件,有没有一种方法可以简化整个查询的构造,涵盖用例 A) 和 B)?在“主查询”的意义上,可以通过各种子关联的条件“丰富”。在单父表的情况下,有一个“主查询”

SELEECT * from Parent p
WHERE 1 = 1

父级上的任何条件都可以简单地将AND'ed 到“主查询”的WHERE 子句中,从而使这个查询构造变得非常容易。

对于在各种子节点上具有各种条件的连接,是否有任何可比性,保留“总是为每个返回的父节点返回所有子节点”语义?

【问题讨论】:

  • 请在代码问题中给出minimal reproducible example--剪切&粘贴&运行代码;具有期望和实际输出(包括逐字错误消息)的示例输入(作为初始化代码);标签和版本;明确的规范和解释。对于包含最少代码的错误,您可以给出的代码是您显示的代码可以通过您显示的代码扩展为不正常。 (调试基础。)对于包含 DBMS 和 DDL 的 SQL,其中包括约束、索引和表格初始化。请参阅 How to Ask、其他 help center 链接和投票箭头鼠标悬停文本。
  • 如果要显示所有父数据,为什么要加入子表?
  • 您对您要做什么的描述不清楚。使用足够多的单词、句子和对部分示例的引用来清楚完整地表达你的意思。 PS 了解 LEFT JOIN ON 返回什么: INNER JOIN ON rows UNION ALL 不匹配的左表行,由 NULL 扩展。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。在 OUTER JOIN ON 之后,需要右 [sic] 表列不为 NULL 的 WHERE 或 INNER JOIN 删除任何由 NULL 扩展的行,即只留下 INNER JOIN ON 行,即“将 OUTER JOIN 转换为 INNER JOIN”。你有那个。
  • "检查是否有子过滤条件,如果有,我添加 WHERE 子句" where 过滤连接的所有行,所以它过滤父子行对,它没有t 只是过滤孩子。如果您想限制哪些孩子左加入父母,那么您需要在离开加入之前或期间将孩子过滤器应用于子表。如果您只想要有孩子的父母,那么您可以内部加入并将限制放在 on 或 where 中。但是你不清楚什么输入需要什么输出,或者你所说的过滤是什么,或者你的可交付成果是什么。
  • @philipxy 感谢您的 cmets。如果您有时间再看一遍:我编辑了问题,希望能澄清一些事情。

标签: sql join left-join parent-child correlated-subquery


【解决方案1】:

我认为您可以修改第二个查询以将 exists 条件移动到 on 子句:

SELECT p.*, c.*
FROM parent p LEFT JOIN
     child c
     ON c.parent_id = p.id AND
        EXISTS (SELECT 1
                FROM child c2
                WHERE c2.parent_id = p.id AND
                      c2.type = 42
               );

也就是说,使用窗口函数更简单:

SELECT p.*, c.*
FROM parent p LEFT JOIN
     (SELECT c.*,
             SUM(CASE WHEN c.type = 42 THEN 1 ELSE 0 END) OVER (PARTITION BY c.parent_id) as num_42
      FROM child c
     ) c
     ON c.parent_id = p.id AND
        num_42 > 0;

【讨论】:

  • 感谢您的回复。我同意对于 OUTER JOIN,EXISTS 条件是 ON 还是 WHERE 子句的一部分很重要……但是,在 INNER JOIN 的情况下,这在结果和性能方面应该是等效的,参见。 stackoverflow.com/questions/1018952。窗口函数的想法很有趣 - 谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-20
  • 2011-10-20
  • 2020-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多