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

时间:2009-08-13 13:31:23

标签: 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

结束时的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)

11 个答案:

答案 0 :(得分:5)

第三种选择呢?

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语法来指示定义整个集合的语句,外键或其他条件,指示应该连接两行以在结果集中创建一行。 / p>

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

  • SELECT我想要的列。
  • JOIN表来定义我想从中获取行的集合。
  • 过滤掉行WHERE我的条件不符合。

通过这种逻辑,我总是选择第二种语法以保持一致的可读性。

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

我会检查各种版本的性能(并确保它们都提供相同的结果,你会惊讶于人们忘记优化代码时,相同的结果很重要!)。我怀疑第一个版本的编写是为了减少加入的记录数量,以提高性能(派生表通常会提高性能而不是其他结构,它可能已经取代了相关的子查询,它几乎肯定会比)。无论是否,我都必须在您的数据库中运行才能看到。

基本上,当两个结构具有相同的结果时,我的偏好是选择性能更快的结构。是的,它可能有点难以理解(您可以随时添加注释,解释您所做的以及为什么要帮助维护者)。但性能是所有数据库访问必须考虑的三个关键事项之一(安全性和数据完整性是另外两个)。性能应该优先于数据库中的维护,特别是对于经常运行的查询。每个用户每次运行时,每隔一段时间避免额外十分钟来理解某些内容(并且大多数查询都会重新查看),每次运行时都不值得花费额外的时间,特别是当它运行数千次时一天一次。

答案 3 :(得分:2)

运行

SET SHOWPLN_ALL ON

然后是每个查询。

我认为第一个可能在简单查询中运行相同的计划,但第二个将始终运行相同或更好,尤其是在更复杂的查询中。

答案 4 :(得分:2)

我只在需要不同查询时使用子查询 - 例如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

这将在查询的joing部分中删除“额外行”。可能不会对此查询的优化器产生影响,但它肯定会在其他查询中(外连接,其他子查询等等)

答案 5 :(得分:1)

正如其他人所说,第二个是更好的选择。但是,如果移动到外部联接,还要考虑过滤器位置的含义。如果您想要查看所有客户以及那些被归类为“标准”的客户,您需要销售代表信息,请查看下面的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

答案 6 :(得分:1)

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会同时处理两个查询。

这些查询在性能方面完全相同。您可以自由地交换ONWHERE和内联视图条件:SQL Server的优化程序非常智能,可以找出最佳计划。

第一个查询在需要时更容易转换为OUTER JOIN,但是,在这种情况下,可能会更好地表达:

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

,如果密钥是cf (CustomerType, Code),那么这个:

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并非如此。

答案 7 :(得分:1)

我在Oracle DBMS上运行了4个变体,它们在运行时或多或少都相同。我没有唱一个复杂的子查询,只是从表中选择一个列(当使用子查询方法时)并以各种方式过滤该表(即直接在subselect / derived表或main where子句中或在连接表达式条件中。

答案 8 :(得分:0)

我总是喜欢第二种变体,因为如果你先使用,那么查询会变得非常复杂 表现可能会有负面影响。

答案 9 :(得分:0)

我想说,在等效的连接/子查询情况下,优化器将生成类似的执行计划,并且您使用的路由应该由提供查询中最明确意图的内容驱动。 (例如,根据可维护性选择)

答案 10 :(得分:0)

我会一直跟着第二个,直到我被迫使用替代品。

保持连接在WHERE中的FROM和条件。