【问题标题】:SQL : where vs. on in joinSQL : where vs. on in join
【发布时间】:2011-02-07 09:05:39
【问题描述】:

也许是一个愚蠢的问题,但请考虑以下 2 个表格:

T1                       
Store    Year
01  2009
02  2009
03  2009
01  2010
02  2010
03  2010

T2
Store
02

为什么这个 INNER JOIN 会给我想要的结果(过滤 ON 子句中的 [year]):

select t1.*
from t1
inner join t2
on t1.store = t2.store
and t1.[year] = '2009'

Store    Year
02  2009

为什么 LEFT OUTER JOIN 包含 2010 年的记录?

select t1.*
from t1
left outer join t2
on t1.store = t2.store
and t1.year = '2009'
where t2.store is null

01  2009
03  2009
01  2010
02  2010
03  2010

我必须在“WHERE”子句中编写 [year] 过滤器:

select t1.*
from t1
left outer join t2
on t1.store = t2.store
where t2.store is null
and t1.year = '2009'

01  2009
03  2009

就像我说的那样,也许是一个愚蠢的问题,但它困扰着我!

【问题讨论】:

  • 使用降价来格式化你的代码。这样,您就不必用<br /> 标记来混淆它,并且您的表格列将正确排列(即使用空格而不是制表符)。
  • 唯一愚蠢的问题是没有被问到的问题。 ;-)

标签: sql tsql join outer-join


【解决方案1】:

您的第二个查询返回第一个表中的所有结果,除了在联接中匹配的结果(其结果可以在第一个查询中看到。)如果您去掉“where t2”,这可能更容易让您可视化.store is null”子句,并将“t2.Store”添加到所选列。您应该看到的结果是:

01 2009 null
02 2009 02
03 2009 null
01 2010 null
02 2010 null
03 2010 null

如您所见,如果您随后仅过滤到 t2.store 为空的行,那么您所做的就是减去第二行(唯一与 join 子句匹配的行。)

【讨论】:

  • 再次感谢您的意见,但第一个答案解释了“为什么”
  • 没关系。顺便说一句,请注意,在 StackOverflow 中,“第一个答案”并不意味着您认为它意味着什么。我在你的意思之前发布了 15 分钟,所以他最初是“第 3 个”帖子。然而,它现在显示为第一个,因为它被投票赞成。参考作者的答案更容易,例如:“@Unreason 的帖子很好地解释了原因。”
【解决方案2】:

如果你按照 LEFT JOIN 的定义去

  • 对于左侧表中的每一行,如果存在,它将从右侧表中返回匹配的行
  • 如果右侧不存在任何行,它仍会从左侧表中返回一行,右侧表中的所有列都设置为 NULL

现在意识到存在等同于 ON 条件评估为真,这一切都说得通。

对于 t1.year 为 2010 的行,on 表达式的计算结果为 false(对于 t1.year = 2010 的所有行,x AND 2010=2009 为 false),但由于它是左连接,左表中的行将仍然被返回(根据定义)。

所以这种条件只能写成where条件,不能写成join条件。

(一般情况下不必是表格,而是选择表达式)

编辑: 正如 Erwin 有趣地指出的那样,where 条件可以变成 JOIN with

select t1.*
from t1
left outer join t2
on t1.store = t2.store
or t1.year = '2009'
where t2.store is null

因为:

t1.store t1.year t2.store t1.store=t2.store t1.year=2009  join(OR)
01       2009    02       false             true          true
02       2009    02       true              true          true
03       2009    02       false             true          true
01       2010    02       false             false         false
02       2010    02       true              false         true
03       2010    02       false             false         false

所以只有上表中连接列为false的行才会在t2.*字段中返回空值。

我的意思是这种条件不能变成纯连接,因为左连接的工作方式(即使连接条件为假,仍然返回记录),尽可能带有内部连接条件(可以变成纯连接,反之亦然)。

关于提议的查询 - 我的建议是不要使用它。

使用 OR 的查询通常比使用 AND 条件的查询执行得更差(OR 扩展结果集;AND 限制)。这既适用于连接条件,也适用于 where 条件。

您的查询将比在 where 条件中具有 t1.year = '2009' 的查询执行得更差,因为在以后的情况下,可以使用索引(如果存在),因为如果您像这样加入,您基本上是在人为地将一个表中的记录与另一个表中的记录连接起来,以便您的 where 条件仅过滤您需要的记录。 仅从 t1 获取以 2009 开头的记录应该更有效(假设有一个关于年份的索引并且选择性足够高,会在 WHERE 条件下发生)。

这些关于性能的建议都可以而且应该通过检查查询计划来验证。

最后,查询有点模糊 - 在 t1.year = '2009' 的情况下,连接变成笛卡尔积(稍后被过滤掉)并不是很明显。所以,如果简单的LEFT JOINwhere t1.year = 2009 AND t2.store is null 的假设性能更好并且更具可读性,我不会使用这个查询。

【讨论】:

  • 谢谢你,这是一个完美的答案!
  • 抱歉,由于声誉不足,我无法为这个问题投票,但我会尽快为您的答案投票
  • 根据您的回答,我做了一个小实验:select t1.* from t1 left outer join t2 on t1.store = t2.store -- 使用 OR 而不是 AND OR t1.year = '2009' where t2.store is null 给了我正确的答案!
  • @Erwin:它确实返回了相同的结果,但它有点晦涩难懂,平均而言,它的性能会比 WHERE 变体差。我将更新我的答案以清理最后的陈述和一般建议。
  • 嗨!当然 t1.year = 2009 应该用在 WHERE 子句中,我只是想测试一下你的解释,它出色地通过了! Tnx 进一步阐述你的答案!
猜你喜欢
  • 1970-01-01
  • 2016-03-16
  • 2010-11-04
  • 2016-04-28
  • 2019-01-15
  • 1970-01-01
  • 2014-02-11
  • 2011-01-21
  • 2019-06-09
相关资源
最近更新 更多