【问题标题】:left join turns into inner join左连接变成内连接
【发布时间】:2010-07-15 14:07:03
【问题描述】:
SELECT
a.foo
b.bar
c.foobar
FROM tableOne AS a
INNER JOIN tableTwo AS b ON a.pk = b.fk
LEFT JOIN tableThree AS c ON b.pk = c.fk
WHERE a.foo = 'something'
AND c.foobar = 'somethingelse'

在 where 子句之后加上 and 子句似乎将左连接变成了内连接。我看到的行为是,如果 tableThree 中没有“somethingelse”,则将返回 0 行。

如果我将 c.foobar = 'somethingelse' 移动到连接子句中,存储的连接将像左连接一样。

    SELECT
    a.foo
    b.bar
    c.foobar
    FROM tableOne AS a
    INNER JOIN tableTwo AS b ON a.pk = b.fk
    LEFT JOIN tableThree AS c ON b.pk = c.fk
    AND c.foobar = 'somethingelse'
    WHERE a.foo = 'something'

有人可以指出一些描述为什么会发生这种情况的文档吗?非常感谢

【问题讨论】:

    标签: sql


    【解决方案1】:

    这是因为您的 WHERE 子句。

    当您在WHERE 子句(即NOT NULL)中从左连接的右侧指定一个值时,您必须消除所有NULL 值,它实际上变成了INNER JOIN

    如果你写,AND (c.foobar = 'somethingelse' OR c.foobar IS NULL) 会解决问题的。

    您还可以将c.foobar 部分移动到您的连接谓词中,这也将解决问题。

    【讨论】:

    • @DaveMarkle 是同样的情况,当我从左连接的右侧指定值时,加入 group by 或有。 (我的意思是它将左转换为内)
    • @Dave Markle 你知道在这种情况下,WHERE 子句或 JOIN 谓词会更快吗?
    • 您的里程可能因不同的 DBMS 而异,但在 SQL Server 中,性能是相同的,因为基于成本的优化器能够理解查询在功能上是相同的。
    • 结果会一样吗? where 子句的部分是否移动到连接谓词中?
    • 是的,结果会和内连接一样。
    【解决方案2】:

    您看到这种情况的原因是,对于 c 中不存在的那些行(即无法连接),左连接将 c 的所有列设置为 NULL。这意味着比较 c.foobar = 'somethingelse' 不正确,这就是没有返回这些行的原因。

    如果您将c.foobar = 'somethingelse' 移动到连接条件中,当条件不为真时,该连接仍会返回这些行(尽管具有 NULL 值)。

    【讨论】:

      【解决方案3】:

      'where' 子句在连接之后执行。这对于内部联接无关紧要,但对于外部联接很重要。

      简化示例

      SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk WHERE c.foobar='somethingelse'
      
      Raw data                  Outer Join Result        Select Result
      b.pk c.fk c.foorbar       b.pk c.fk c.foorbar      c.foorbar
      1    1    something       1    1    something      <not in result set>
      1    1    somethingelse   1    1    somethingelse  somethingelse
      
      
      SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk AND c.foobar='somethingelse'
      
      Raw data                  Outer Join Result        Select Result
      b.pk c.fk c.foorbar       b.pk c.fk c.foorbar      c.foorbar
      1    1    something       1    null null           null
      1    1    somethingelse   1    1    somethingelse  somethingelse
      

      【讨论】:

        【解决方案4】:

        联接正在执行它们的工作,然后 where 正在删除 c.foobar 'somethingelse' 的记录。

        效果看起来像内连接,但实际上不是。

        【讨论】:

          【解决方案5】:

          Left Join 返回左表(示例中为 tableTwo)中的所有内容以及右表(示例中为 tableThree)中的任何匹配行。当您过滤左连接右侧的某些内容(即 tableThree)并且您没有考虑不匹配的值时,您实际上要求一个值存在并且该值是“某物”,这相当于一个内部加入。如果您要做的是查找 tableThree 中没有行且 foobar 值为“something”的所有 tableTwo 行,则可以将过滤移至 on 子句:

          Select a.foo, b.bar, c.foobar
          From tableOne As a
              Inner Join tableTwo as b
                  On b.fk = a.pk
              Left Join tableThree as c
                  On c.fk = b.pk
                      And c.foobar = 'something'
          Where a.foo = 'something'
              And c.pk Is Null
          

          最后添加,c.pk Is Null 过滤没有 tableThree 值且 foobar 值为 'something' 的值。如果只想查看 tableThree 值,当它们的 foobar 值为 'something' 时(否则为 null ),然后删除我添加的 c.pk Is Null 的附加过滤器。

          【讨论】:

            【解决方案6】:

            第一种情况(c.foobar 在 where 子句中c.foobar 的过滤发生在连接发生之后(它们正确发生),这样您就可以有效地过滤掉所有没有'somethingelse' 的结果记录..

            第二种情况c.foobar 是连接的一部分,因此它在连接时进行评估,只控制连接的输出,而不是最终的记录集(返回null,连接失败)..

            【讨论】:

              【解决方案7】:

              LEFT JOIN 在没有匹配行的地方产生 NULL。在这种情况下,对于不匹配的行, c.foobar 将为 NULL。但是您的 WHERE 子句正在寻找一个特定的值:'somethingelse',因此将过滤掉所有 NULL 值。由于 INNER JOIN 在右侧也不会产生 NULL 值,因此两者看起来相同。您可以添加 'OR c.foobar IS NULL' 以允许返回空值。

              当您将条件移动到 ON 子句时,它成为 JOIN 行匹配的一部分,而不是最终过滤器。连接匹配可能会失败,然后外部连接在“c.foobar”为 NULL 或不是“somethingelse”的情况下返回 NULL。

              【讨论】:

                【解决方案8】:

                它不会将 LEFT JOIN 转换为 INNER JOIN,认为效果可能看起来是一样的。 当你设置 WHERE 条件时

                AND c.foobar = 'somethingelse'

                您将摆脱所有允许 LEFT JOIN 发挥作用的情况。在这种情况下,c.foobar 的某些值将为 NULL。在 JOIN 条件上设置此项仍然允许不匹配的 LEFT JOIN 结果,仅限制 C 结果返回的内容。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-01-30
                  • 2015-10-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-16
                  • 2017-08-15
                  • 2010-10-06
                  • 2015-03-04
                  相关资源
                  最近更新 更多