左外连接不保留左表

Ari*_*ian 4 sql t-sql sql-server left-join sql-server-2008

我有这个问题:

SELECT Q_ID,
   Q_DESC,
   COUNT(Q_ID)
FROM   #tmp_rep
   LEFT OUTER JOIN po_Questions po
        ON  Q_ID = Certificate AND FUNCS = 1
        AND LEN(LTRIM(RTRIM(po.UserName))) > 0
        AND LEN(LTRIM(RTRIM(po.UserNumber))) > 0
GROUP BY
   Q_ID,
   Q_DESC
ORDER BY
   Q_ID
Run Code Online (Sandbox Code Playgroud)

表#tmp_rep有2列(Q_ID,Q_Desc)和4 行.表po_Questions有10个在证书行列中使用3个Q_ID代码.如果我运行此查询,那么每件事情都可以,对于Q-ID = 4,我得到0表示计数,但如果我以这种方式编写该查询:

SELECT Q_ID,
   Q_DESC,
   COUNT(Q_ID)
FROM   #tmp_rep
   LEFT OUTER JOIN po_Questions po
        ON  Q_ID = Certificate 
WHERE FUNCS = 1
      AND LEN(LTRIM(RTRIM(po.UserName))) > 0
        AND LEN(LTRIM(RTRIM(po.UserNumber))) > 0

GROUP BY
   Q_ID,
   Q_DESC
ORDER BY
   Q_ID
Run Code Online (Sandbox Code Playgroud)

然后我在结果中只获得3行,而Q_ID = 4不属于结果.为什么SQL Server有这种行为?

谢谢

Mar*_*ith 6

对于非匹配的行po.UserNameNULL如此LEN(LTRIM(RTRIM(po.UserName)))NULL

NULL > 0当谓词出现在你正在将你的外部联接转回内部联接时,评估结果UNKNOWN不是TRUE这样WHERE.类似于FUNCSSQLMenace指出的那样.

您可能想要下载Itzik Ben Gan的逻辑查询处理海报.

从概念上讲,会发生以下情况(但不应将其与物理实现的方式混淆!)

对于您的第一个查询:

  • 笛卡尔积#tmp_rep,po_Questions
  • 然后ON Filter应用有效地执行INNER JOIN打开Q_ID = Certificate但也排除po_Questions与谓词不匹配的任何行.
  • 然后将非匹配的OuterRows from #tmp_rep添加回来.这些将包含NULL所有列 po_Questions
  • 没有WHERE条款,所以这是最终的结果.

对于您的第二个查询:

  • 笛卡尔积#tmp_rep,po_Questions
  • 然后ON Filter应用有效地INNER JOIN开启Q_ID = Certificate.
  • 然后将非匹配的OuterRows from #tmp_rep添加回来.这些将包含NULL所有列 po_Questions
  • 然后WHERE评估该子句.这肯定会删除上一步中的所有行,也可能删除其他行.