在JOIN和WHERE中过滤查询的区别?

时间:2015-12-24 15:25:29

标签: sql join where resultset

在SQL中,我试图根据ID过滤结果,并想知道

之间是否存在任何逻辑差异
SELECT value 
FROM table1 
JOIN table2 ON table1.id = table2.id 
WHERE table1.id = 1

SELECT value 
FROM table1 
JOIN table2 ON table1.id = table2.id AND table1.id = 1

对我而言,似乎逻辑是不同的虽然你总会获得相同的结果集,但我想知道是否有任何条件可以获得两个不同的结果集(或者它们总是会返回完全相同的结果集)两个结果集)

3 个答案:

答案 0 :(得分:38)

答案是 NO 差异,但是:

我总是喜欢这样做。

  • 始终在ON子句
  • 中保留加入条件
  • 始终将过滤器的放在where子句

这使查询更加可读

所以我将使用此查询:

SELECT value
FROM table1
INNER JOIN table2
        ON table1.id = table2.id
WHERE table1.id = 1

但是,当您使用OUTER JOIN'S时,将过滤器保持在ON条件和Where条件下会有很大差异。

逻辑查询处理

以下列表包含查询的一般形式,以及根据逻辑处理不同子句的顺序分配的步骤编号。

(5) SELECT (5-2) DISTINCT (5-3) TOP(<top_specification>) (5-1) <select_list>
(1) FROM (1-J) <left_table> <join_type> JOIN <right_table> ON <on_predicate>
| (1-A) <left_table> <apply_type> APPLY <right_table_expression> AS <alias>
| (1-P) <left_table> PIVOT(<pivot_specification>) AS <alias>
| (1-U) <left_table> UNPIVOT(<unpivot_specification>) AS <alias>
(2) WHERE <where_predicate>
(3) GROUP BY <group_by_specification>
(4) HAVING <having_predicate>
(6) ORDER BY <order_by_list>;

流程图逻辑查询处理

Enter image description here

  • (1)FROM:FROM阶段标识查询的源表和 处理表运算符。每个表运算符都应用一系列 分阶段。例如,连接中涉及的阶段是(1-J1) 笛卡尔积,(1-J2)ON滤波器,(1-J3)添加外行。 FROM 阶段生成虚拟表VT1。

  • (1-J1)笛卡尔积:此阶段执行笛卡尔积 表运算符中涉及的两个表之间的(交叉连接), 生成VT1-J1。

  • (1-J2) ON滤镜:此阶段根据VT1-J1过滤行 出现在ON子句中的谓词(&lt; on_predicate&gt;)。只要 插入谓词计算结果为TRUE的行 VT1-J2。
  • (1-J3)添加外行:如果指定了OUTER JOIN(相反于 CROSS JOIN或INNER JOIN),保留的一个或多个表中的行 未找到匹配项的行将添加到VT1-J2的行中 外行,生成VT1-J3。
  • (2) WHERE :此阶段根据此过滤来自VT1的行 出现在WHERE子句()中的谓词。只要 谓词计算结果为TRUE的行将插入VT2。
  • (3)GROUP BY:此阶段基于组排列来自VT2的行 在GROUP BY子句中指定的列列表上,生成VT3。 最终,每组将有一个结果行。
  • (4)HAVING:此阶段根据VT3过滤来自VT3的组 出现在HAVING子句中的谓词(&lt; having_predicate&gt;)。 仅插入谓词评估为TRUE的组 进入VT4。
  • (5)SELECT:此阶段处理SELECT子句中的元素, 生成VT5。
  • (5-1)评估表达式:此阶段评估表达式 SELECT列表,生成VT5-1。
  • (5-2)DISTINCT:此阶段从VT5-1中删除重复的行, 生成VT5-2。
  • (5-3)TOP:此阶段过滤指定的最高数字或百分比 VT5-2中的行数基于ORDER定义的逻辑顺序 BY子句,生成表VT5-3。
  • (6)ORDER BY:此阶段根据VT5-3对行进行排序 ORDER BY子句中指定的列列表,生成游标 VC6。

它来自 this excellent link

答案 1 :(得分:12)

虽然使用 INNER JOINS 没有区别,但正如VR46指出的那样,使用 OUTER JOINS 并评估第二个表中的值时存在显着差异(对于左连接 - 右连接的第一个表)。请考虑以下设置:

DECLARE @Table1 TABLE ([ID] int)
DECLARE @Table2 TABLE ([Table1ID] int, [Value] varchar(50))

INSERT INTO @Table1
VALUES
(1),
(2),
(3)

INSERT INTO @Table2
VALUES
(1, 'test'),
(1, 'hello'),
(2, 'goodbye')

如果我们使用左外连接从中选择并在where子句中添加条件:

SELECT * FROM @Table1 T1
LEFT OUTER JOIN @Table2 T2
    ON T1.ID = T2.Table1ID
WHERE T2.Table1ID = 1

我们得到以下结果:

ID          Table1ID    Value
----------- ----------- --------------------------------------------------
1           1           test
1           1           hello

这是因为where子句限制了结果集,所以我们只包含table1中ID为1的记录。但是,如果我们将条件移动到on子句:

SELECT * FROM @Table1 T1
LEFT OUTER JOIN @Table2 T2
    ON T1.ID = T2.Table1ID
    AND T2.Table1ID = 1

我们得到以下结果:

ID          Table1ID    Value
----------- ----------- --------------------------------------------------
1           1           test
1           1           hello
2           NULL        NULL
3           NULL        NULL

这是因为我们不再按照table1的ID 1过滤结果集 - 而是我们过滤JOIN。因此,即使table1的ID为2 DOES在第二个表中有匹配,它也会从连接中排除 - 但不是结果集(因此为空值)。

因此,对于内连接并不重要,但是为了可读性和一致性,你应该将它保存在where子句中。但是,对于外连接,您需要注意,放置条件的位置很重要,因为它会影响结果集。

答案 2 :(得分:2)

我认为标记为“正确”的答案是不对的。为什么?我试着解释一下:

我们有意见

  

“始终将连接条件保留在ON子句中始终放置过滤器   在where子句“

这是错的。如果你在内连接中,每次都将过滤器参数放在ON子句中,而不是放在哪里。你问为什么?尝试使用复杂的WHERE子句(例如,使用的函数或计算)来计算复杂查询,总共有10个表(例如,每个表有10k recs)连接。如果在ON子句中放置过滤条件,则不会在这10个表之间发生JOINS,WHERE子句根本不会执行。在这种情况下,您不在WHERE子句中执行10000 ^ 10计算。这是有道理的,不仅仅在WHERE子句中使用过滤参数。