Sql 子查询与传统的WHERE子句连接?

Sql 子查询与传统的WHERE子句连接?,sql,sql-server,Sql,Sql Server,当连接到表的子集时,有什么理由选择其中一种格式而不是另一种格式 子查询版本: SELECT ... FROM Customers AS c INNER JOIN (SELECT * FROM Classification WHERE CustomerType = 'Standard') AS cf ON c.TypeCode = cf.Code INNER JOIN SalesReps s ON cf.SalesRepID = s.SalesRepID vs结尾的WHERE子句: SE

当连接到表的子集时,有什么理由选择其中一种格式而不是另一种格式

子查询版本:

SELECT ...
FROM Customers AS c
INNER JOIN (SELECT * FROM Classification WHERE CustomerType = 'Standard') AS cf
    ON c.TypeCode = cf.Code
INNER JOIN SalesReps s ON cf.SalesRepID = s.SalesRepID
vs结尾的WHERE子句:

SELECT ...
FROM Customers AS c
INNER JOIN Classification AS cf ON c.TypeCode = cf.Code
INNER JOIN SalesReps AS s ON cf.SalesRepID = s.SalesRepID
WHERE cf.CustomerType = 'Standard'
结尾的WHERE子句感觉更为“传统”,但第一个可以说更为明确,尤其是在连接变得越来越复杂的情况下


我能想到的选择第二个的另一个原因是,第一个上的“SELECT*”可能会返回以后不用的列(在这种情况下,我可能只需要返回cf.Code和cf.SalesRepID)

第二个子句肯定更清楚,我怀疑优化器也会更喜欢它。理想情况下,您应该指定所需的列。

我总是喜欢第二个变量,因为如果使用第一个变量,查询将变得非常复杂
性能可能有负面影响。

我想说的是,在同等的联接/子查询情况下,优化器将生成类似的执行计划,您使用的路径应该由在查询中提供最清晰意图的内容决定。(例如,根据可维护性进行选择)

我总是选择第二种,直到我被迫使用另一种


在FROM中保留连接,在WHERE中保留条件。

第三个选项如何

SELECT ...
FROM Customers AS c
INNER JOIN Classification AS cf 
    ON cf.CustomerType = 'Standard' 
    AND c.TypeCode = cf.Code
INNER JOIN SalesReps AS s 
    ON cf.SalesRepID = s.SalesRepID
就我个人而言,我更喜欢使用
JOIN
语法来指示定义了整个集合的语句、外键或其他指示两行应合并以在结果集中形成一行的条件

WHERE
子句包含筛选我的结果集的条件。可以说,当您执行大量联接时,这可能会变得非常臃肿和复杂,但是当您在集合中思考时,它遵循一种逻辑:

  • 选择我想要的列
  • JOIN
    表以定义我要从中获取行的集合
  • 筛选出不符合我的条件的行
按照这种逻辑,我总是选择第二种语法以保持一致的可读性。

run

SET SHOWPLN_ALL ON
然后是每个查询


我认为第一个可能在简单查询中运行相同的计划,但第二个总是运行相同或更好的计划,特别是在更复杂的查询中。

我只在需要不同的查询时使用子查询,例如group by或过于复杂的查询

我还将对第二个查询执行一个变体,如下所示:

SELECT ...
FROM Customers AS c
INNER JOIN Classification AS cf
 ON c.TypeCode = cf.Code
 AND cf.CustomerType = 'Standard'
INNER JOIN SalesReps AS s 
ON cf.SalesRepID = s.SalesRepID

这将在查询的连接部分删除“多余的行”。对于这个查询,可能不会对优化器产生影响,但在其他查询(外部联接、进一步的子查询等)中肯定会产生影响。

正如其他人所说,第二个是更好的选择。但是,如果您移动到外部连接,则还要考虑过滤器位置的含义。如果您可能想要查看所有客户,并且对于那些被分类为“标准”的客户,您想要销售代表信息,那么请查看下面的SQL

    SELECT ...
      FROM Customers AS c
 LEFT JOIN Classification AS cf
        ON c.TypeCode      = cf.Code
       AND cf.CustomerType = 'Standard'
 LEFT JOIN SalesReps s 
        ON cf.SalesRepID   = s.SalesRepID
下面的代码不会产生与上面相同的结果。它将有更少的行并且是不正确的

    SELECT ...
      FROM Customers AS c
 LEFT JOIN Classification AS cf
        ON c.TypeCode      = cf.Code
 LEFT JOIN SalesReps s 
        ON cf.SalesRepID   = s.SalesRepID
     WHERE cf.CustomerType = 'Standard'
但特别是对于你的问题,我希望看到以下版本。我相信这个版本的意图是明确的

    SELECT ...
      FROM Customers AS c
      JOIN Classification AS cf
        ON c.TypeCode      = cf.Code
       AND cf.CustomerType = 'Standard'
      JOIN SalesReps s 
        ON cf.SalesRepID   = s.SalesRepID

第一个版本是派生表。不要将其与子查询混淆

我会检查各种版本的性能(并确保它们都提供相同的结果,您会惊讶地发现人们在优化代码时经常忘记相同的结果是重要的!)。我怀疑编写第一个版本是为了模拟连接到的记录的数量,以提高性能(派生表通常比其他结构提高性能,并且它可能已经取代了相关子查询,而它几乎肯定会比其他结构具有更好的性能)。不管它有没有,我都必须在你的数据库中运行才能看到

基本上,当两个结构具有相同的结果时,我倾向于选择执行速度更快的结构。是的,这可能有点难以理解(您可以随时添加注释,解释您做了什么以及为什么要帮助维护人员)。但性能是所有数据库访问中必须考虑的三个关键因素之一(安全性和数据完整性是另外两个)。在数据库中,特别是对于频繁运行的查询,性能应该优于维护的方便性。当您需要查看某个内容时(大多数查询的访问时间都少于10分钟),避免一年多花10分钟来理解该内容对于每个用户来说都是不值得的,尤其是当它每天运行数千次时

SELECT ...
FROM Customers AS c
INNER JOIN (SELECT * FROM Classification WHERE CustomerType = 'Standard') AS cf
    ON c.TypeCode = cf.Code
INNER JOIN SalesReps s ON cf.SalesRepID = s.SalesRepID


SELECT ...
FROM Customers AS c
INNER JOIN Classification AS cf ON c.TypeCode = cf.Code
INNER JOIN SalesReps AS s ON cf.SalesRepID = s.SalesRepID
WHERE cf.CustomerType = 'Standard'
SQL Server
将对两个查询进行相同的处理

这些查询在性能方面是相同的。您可以在
上自由交换
WHERE
和内联视图条件:
SQL Server
的优化器足够智能,可以找出最佳方案。 当需要时,第一个查询更容易转换为
外部联接
,但是,在这种情况下,它的措辞可能更好,如下所示:

SELECT  ...
FROM    Customers AS c
INNER JOIN -- or OUTER JOIN
        Classification AS cf
ON      cf.Code = c.TypeCode 
        AND cf.CustomerType = 'Standard'
INNER JOIN -- or OUTER JOIN
        SalesReps AS s
ON      s.SalesRepID = cf.SalesRepID 
在编写查询时,我尝试编写它们,以便从查询中清楚地看到关键性质

如果
cf.code
上有一个单列键,我会使用这个:

SELECT  ...
FROM    Customers AS c
INNER JOIN
        Classification AS cf
ON      cf.Code = c.TypeCode
INNER JOIN
        SalesReps AS s
ON      s.SalesRepID = cf.SalesRepID
WHERE   cf.CustomerType = 'Standard'
如果键是
cf(code,CustomerType)
,则此键:

SELECT  ...
FROM    Customers AS c
INNER JOIN
        Classification AS cf
ON      cf.Code = c.TypeCode
        AND cf.CustomerType = 'Standard'
INNER JOIN
        SalesReps AS s
ON      s.SalesRepID = cf.SalesRepID
SELECT  ...
FROM    Customers AS c
INNER JOIN
        (
        SELECT  *
        FROM    Classification
        WHERE   CustomerType = 'Standard'
        ) AS cf
ON      cf.Code = c.TypeCode
INNER JOIN
        SalesReps s
ON      s.SalesRepId = cf.SalesRepID
,如果键是
cf(CustomerType,code)
,则此键:

SELECT  ...
FROM    Customers AS c
INNER JOIN
        Classification AS cf
ON      cf.Code = c.TypeCode
        AND cf.CustomerType = 'Standard'
INNER JOIN
        SalesReps AS s
ON      s.SalesRepID = cf.SalesRepID
SELECT  ...
FROM    Customers AS c
INNER JOIN
        (
        SELECT  *
        FROM    Classification
        WHERE   CustomerType = 'Standard'
        ) AS cf
ON      cf.Code = c.TypeCode
INNER JOIN
        SalesReps s
ON      s.SalesRepId = cf.SalesRepID
还有一点需要注意的是:在
MySQL
中,内联视图的效率远远低于连接,因此我不会在
MySQL
中使用它们


但是,SQL Server的情况并非如此。

我在Oracle DBMS上运行了4种变体,它们在运行时或多或少都是等效的。我没有使用复杂的子查询,只是从表中选择一列(使用子查询方法时),并以各种方式筛选该表(即直接在子选择/派生表中或在main where cl中)