【问题标题】:What's the difference between "where" clause and "on" clause when table left join?表左连接时where子句和on子句有什么区别?
【发布时间】:2012-01-08 19:29:57
【问题描述】:

SQL1:

select t1.f1,t2.f2 
from t1 
   left join t2 on t1.f1 = t2.f2 and t1.f2=1 and t1.f3=0 

SQL2:

select t1.f1,t2.f2 
from t1 
  left join t2 on t1.f1 = t2.f2 
where t1.f2=1 and t1.f3=0

区别是where和on子句,返回结果一样吗?有什么区别? DBMS 是否以相同的方式运行它们?谢谢。

【问题讨论】:

标签: sql where-clause


【解决方案1】:

在考虑 SQL 语法时,理解the logical order of SQL operations 很重要。 JOINFROM 子句中的运算符(并且ON 属于相关的JOIN)。 FROM 子句是第一个在逻辑上执行的操作(优化器仍然可以选择重新排序)。

在您的示例中,没有真正的区别,但很容易构造一个,as I've shown in this blog post about the difference between ON and WHERE in OUTER JOIN(博客文章中的示例使用Sakila database):

第一次查询

SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
WHERE fa.film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;

产量:

ACTOR_ID  FIRST_NAME  LAST_NAME  COUNT
--------------------------------------
194       MERYL       ALLEN      1
198       MARY        KEITEL     1
30        SANDRA      PECK       1
85        MINNIE      ZELLWEGER  1
123       JULIANNE    DENCH      1

因为我们在WHERE 子句中过滤了外部联接表,所以LEFT JOIN 有效地变成了INNER JOIN。为什么?因为如果我们有一个没有在电影中演出的演员,那么该演员的唯一行将有fa.film_id IS NULL,因此fa.film_id &lt; 10 谓词将产生NULL。这样的参与者被排除在结果之外,就像INNER JOIN一样。

第二次查询

SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
  AND fa.film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;

产量:

ACTOR_ID  FIRST_NAME  LAST_NAME     COUNT
-----------------------------------------
3         ED          CHASE         0
4         JENNIFER    DAVIS         0
5         JOHNNY      LOLLOBRIGIDA  0
6         BETTE       NICHOLSON     0
...
1         PENELOPE    GUINESS       1
200       THORA       TEMPLE        1
2         NICK        WAHLBERG      1
198       MARY        KEITEL        1

现在,结果中包含没有电影的演员,因为fa.film_id &lt; 10 谓词是LEFT JOINON 谓词的一部分

结论

始终将谓词放在它们逻辑上最有意义的地方

  • 它们是您的JOIN 操作的一部分吗?把它们放在ON
  • 它们是否对您的整个JOIN 产品进行过滤?把它们放在WHERE

【讨论】:

    【解决方案2】:

    这两个查询完全相同。

    Mark Ba​​nnister 正确地指出 where 子句适用于整个结果集,但 on clause 适用于联接。

    在您的情况下,对于 SQL 1 LEFT JOIN 条件,过滤器连接在右侧,但左侧总是在任何 WHERE 过滤之前返回。由于没有 WHERE 条件,所有 t1 总是返回。

    在 SQL 2 中,LEFT JOIN 条件过滤了一些显示在右侧的结果,但又返回了所有 t1。但是这次 WHERE 条件可能会过滤掉一些 t1 的记录。

    INSERT INTO `t1` (`f1`,`f2`,`f3`) VALUES (1,1,1); INSERT INTO `t2` (`f3`) VALUES (1);

    由于它们指向不同的逻辑,因此必须基于该逻辑编写查询,这为我们提供了强大的功能和灵活性。

    然而,INNER JOIN 返回相同的结果,所以是的,请检查优化器。

    【讨论】:

      【解决方案3】:

      where 子句适用于整个结果集; on clause 仅适用于有问题的联接。

      在提供的示例中,所有附加条件都与连接内侧的字段相关 - 因此在本示例中,两个查询实际上是相同的。

      但是,如果您在联接的外部侧的表中包含值的条件,则会产生显着差异。

      您可以从此链接获得更多信息:http://ask.sqlservercentral.com/questions/80067/sql-data-filter-condition-in-join-vs-where-clause

      例如:

      select t1.f1,t2.f2 from t1 left join t2 on t1.f1 = t2.f2 and t2.f4=1
      
      select t1.f1,t2.f2 from t1 left join t2 on t1.f1 = t2.f2 where t2.f4=1
      

      - 做不同的事情 - 前者将左连接到 f4 为 1 的 t2 记录,而后者已有效地转回到 t2 的内连接。

      【讨论】:

      • 完成图片:如果 where 子句将引用 t2 中的列,那么它将把外连接变成内连接
      • @a_horse_with_no_name:GMTA。 :)
      • 那个链接现在已经失效了,很遗憾
      【解决方案4】:

      1)

      SQL1: select t1.f1,t2.f2 from t1 left join t2 on t1.f1 = t2.f2 **and** t1.f2=1 and t1.f3=0 
      

      在此,解析器将使用这 3 个条件检查 t1 的每一行和 t2 的每一行。获得更快的结果。

      2)SQL2: select t1.f1,t2.f2 from t1 left join t2 on t1.f1 = t2.f2 **where** t1.f2=1 and t1.f3=0

      在此,join 仅采用第一个条件,然后使用这 2 个条件过滤从 join 获得的结果。并且会比第一次查询花费更多的时间。

      您可以从此链接获得更多信息:http://ask.sqlservercentral.com/questions/80067/sql-data-filter-condition-in-join-vs-where-clause

      【讨论】:

      • 任何体面的优化器都会以完全相同的方式处理这两个语句(Oracle 和 PostgreSQL 会这样做)
      • 与 Massimiliano 的答案基本相同,因此出于相同的原因不一定正确。
      • 见上面马克的评论;性能取决于数据库提供者。但是,对于所有现代 RDBMS,此示例的输出应该相同。
      • 大多数 RDBMS 产品将同样优化这两个查询。在 Peter Gulutzan 和 Trudy Pelzer 的“SQL 性能调优”中,他们测试了多个品牌的 RDBMS,没有发现性能差异。我更喜欢将连接条件与查询限制条件分开。如果您使用 OUTER JOIN,有时需要在 join 子句中添加条件。
      • 这两个查询的主要区别是语义。如果两个查询不做同样的事情,任何潜在的性能差异(很难笼统地说)都是无关紧要的。
      【解决方案5】:

      关系代数允许 WHERE 子句和 INNER JOIN 中的谓词互换,因此即使是带有 WHERE 子句的 INNER JOIN 查询也可以让优化器重新排列谓词,以便它们在 JOIN 过程中可能已经被排除。

      我建议您以最易读的方式编写查询。

      有时这包括使 INNER JOIN 相对“不完整”,并将一些条件放在 WHERE 中,只是为了使过滤条件列表更易于维护。

      您可以从此链接获得更多信息: http://ask.sqlservercentral.com/questions/80067/sql-data-filter-condition-in-join-vs-where-clause

      例如,而不是:

      SELECT *
      FROM Customers c
      INNER JOIN CustomerAccounts ca
          ON ca.CustomerID = c.CustomerID
          AND c.State = 'NY'
      INNER JOIN Accounts a
          ON ca.AccountID = a.AccountID
          AND a.Status = 1
      

      写:

      SELECT *
      FROM Customers c
      INNER JOIN CustomerAccounts ca
          ON ca.CustomerID = c.CustomerID
      INNER JOIN Accounts a
          ON ca.AccountID = a.AccountID
      WHERE c.State = 'NY'
          AND a.Status = 1
      

      但这当然取决于。

      【讨论】:

      • 大多数 RDBMS 产品将同样优化这两个查询。在 Peter Gulutzan 和 Trudy Pelzer 的“SQL 性能调优”中,他们测试了多个品牌的 RDBMS,没有发现性能差异。我更喜欢将连接条件与查询限制条件分开。如果您使用 OUTER JOIN,有时需要在 join 子句中添加条件。
      • 虽然谓词确实可以在INNER JOIN .. ONWHERE 之间移动,但OP 的问题是使用LEFT JOIN,这两个谓词位置绝对不是一回事。
      【解决方案6】:

      第一个查询比第二个查询快,因为连接条件比第二个更具体:返回将使用 where 子句过滤的记录是没有意义的(最好不要在all- 查询1)

      无论如何,这取决于查询优化器。

      请看下面:

      Is a JOIN faster than a WHERE?

      【讨论】:

      • 不一定——这取决于数据库的优化器。他们中的许多人会将这些优化为相同的查询。
      • 一个不错的优化器很可能会为两条语句创建相同的执行计划,所以我怀疑会有性能差异。
      • 你们都是对的,但这真的取决于优化器stackoverflow.com/questions/1129923/…
      • @MassimilianoPeluso:我建议将该链接添加到您的答案中 - 我认为它会更有用。
      • 关注语义上的潜在差异更为重要。建议一种方法比另一种方法更快,而实际上它可能完全是错误的,这是一个糟糕的建议。
      猜你喜欢
      • 2012-10-19
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 2015-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多