关于左外连接和Where条件的SQL标准

时间:2010-05-21 14:43:51

标签: sql left-join

根据我放置过滤条件的位置,查询中的过滤条件会得到不同的结果。我的问题是:

  • 是否存在技术差异 这些查询之间?
  • SQL标准中是否有任何内容 这解释了不同的 来自查询的记录集?

鉴于简化的方案:

--Table: Parent  Columns: ID, Name, Description
--Table: Child   Columns: ID, ParentID, Name, Description

--Query 1
SELECT p.ID, p.Name, p.Description, c.ID, c.Name, c.Description
FROM   Parent p
   LEFT OUTER JOIN Child c ON (p.ID = c.ParentID)
WHERE  c.ID IS NULL OR c.Description = 'FilterCondition'

--Query 2
SELECT p.ID, p.Name, p.Description, c.ID, c.Name, c.Description
FROM   Parent p
   LEFT OUTER JOIN Child c
   ON (p.ID = c.ParentID AND c.Description = 'FilterCondition')

我认为查询会返回相同的结果集,当他们没有时,我感到很惊讶。我正在使用MS SQL2005并且在实际查询中,查询1返回~700行,查询2返回~1100行,我无法检测返回行和排除哪些行的模式。查询1中仍有许多行,子行包含数据和NULL数据。我更喜欢查询2的样式(我认为它更优),但我认为查询会返回相同的结果。

修改/概述:

这里提供了一些很棒的答案。我很难选择给谁答案。我决定选择mdma,因为它是第一个答案,也是最清晰的答案之一。根据提供的答案,这是我的总结:

可能的结果:

  • A:没有孩子的父母
  • B:有孩子的父母
  • | - > B1:有孩子的父母没有孩子匹配过滤器
  • \ - > B2:有孩子的父母,其中一个或多个与过滤器匹配

查询结果:

  • 查询1返回(A,B2)
  • 查询2返回(A,B1,B2)

由于左连接,查询2始终返回父级。在查询1中,WHERE子句在左连接之后执行,因此不包括子项中没有子项匹配过滤器的父项(案例B1)。

注意:在B1的情况下仅返回父信息,在B2的情况下,仅返回与过滤器匹配的父/子信息。

HLGEM提供了一个很好的链接:

http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

7 个答案:

答案 0 :(得分:10)

是的,存在巨大差异。在LEFT JOIN的ON子句中放置过滤器时,在结果连接到外部表之前,将应用过滤器。在WHERE子句中应用过滤器时,它会在应用LEFT JOIN之后发生。

简而言之,第一个查询将排除存在子行但子描述不等于过滤条件的行,而第二个查询将始终返回父行的行。

答案 1 :(得分:9)

第一个查询将返回父项没有子项或某些子项与过滤条件匹配的情况。具体而言,父项具有一个子项但与过滤条件不匹配的情况将被省略。

第二个查询将为所有父项返回一行。如果过滤条件不匹配,则将为所有c列返回NULL。这就是为什么在查询2中获得更多行的原因 - 具有与过滤条件不匹配的子项的父项将输出NULL子值,在第一个查询中它们将被过滤掉。

答案 2 :(得分:3)

将条件放在where子句中会将其转换为内部联接(除非您使用的地方id为null,这使得记录不在表中) 请参阅此内容以获得更全面的解释:

http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

答案 3 :(得分:2)

对于此记录集:

parent

id
1

child

id    parent filter
1     1      OtherCondition
2     1      OtherCondition

,第一个查询将返回0个记录,而第二个查询将返回1个记录:

WITH    parent (id) AS
        (
        SELECT  1
        ),
        child (id, parent, condition) AS
        (
        SELECT  1, 1, 'OtherCondition'
        UNION ALL
        SELECT  2, 1, 'OtherCondition'
        )
SELECT  *
FROM    parent
LEFT JOIN
        child
ON      child.parent = parent.id   

/* The children are found, so no fake NULL records returned */

1   1   1   OtherCondition
1   2   1   OtherCondition

现在添加WHERE子句:

WITH    parent (id) AS
        (
        SELECT  1
        ),
        child (id, parent, condition) AS
        (
        SELECT  1, 1, 'OtherCondition'
        UNION ALL
        SELECT  2, 1, 'OtherCondition'
        )
SELECT  *
FROM    parent
LEFT JOIN
        child
ON      child.parent = parent.id       
WHERE   child.id IS NULL OR child.condition = 'FilterCondition'

WHERE子句过滤上一步返回的记录,没有记录符合条件。

虽然这个:

WITH    parent (id) AS
        (
        SELECT  1
        ),
        child (id, parent, condition) AS
        (
        SELECT  1, 1, 'OtherCondition'
        UNION ALL
        SELECT  2, 1, 'OtherCondition'
        )
SELECT  *
FROM    parent
LEFT JOIN
        child
ON      child.parent = parent.id       
        AND child.condition = 'FilterCondition'

1   NULL    NULL    NULL

返回一条假记录。

答案 4 :(得分:1)

只有description != 'FilterCondition'子项的父项不会出现在查询1中,因为在连接行后会评估WHERE子句。

答案 5 :(得分:1)

第一个查询返回的行数较少,因为它只返回没有子节点的行,或者具有与过滤条件匹配的子节点。

WHERE子句排除其余的(那些DO有孩子但与过滤条件不匹配的子句。)

第二个查询显示上述所有三个条件。

答案 6 :(得分:0)

我注意到可以使结果发生变化的几个差异。在第一个查询中,您有LEFT OUTER JOIN Child c ON (p.ID = c.ParentID) ,然后在第二个查询中有LEFT OUTER JOIN Child c ON (p.ID = c.ParentID AND c.Description = 'FilterCondition'),这使得第二个查询返回所有父项孩子满足你的条件,因为第一个条件也将返回没有孩子的父母。另请参阅连接条件的优先级和条件。