【问题标题】:Why does this speed up my SQL query?为什么这会加快我的 SQL 查询速度?
【发布时间】:2010-11-17 12:34:27
【问题描述】:

不久前,我从一位 DBA 朋友那里学到了一个技巧,可以加快某些 SQL 查询的速度。我记得他提到这与 SQL Server 如何编译查询有关,并且查询路径被强制使用索引值。

这是我的原始查询(需要 20 秒):

select Part.Id as PartId, Location.Id as LocationId
 FROM Part, PartEvent PartEventOuter, District, Location 
WHERE 
    PartEventOuter.EventType = '600'   AND PartEventOuter.AddressId = Location.AddressId  
    AND Part.DistrictId = District.Id   AND Part.PartTypeId = 15   
    AND District.SubRegionId = 11   AND PartEventOuter.PartId = Part.Id  
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'   
    AND NOT EXISTS (
            SELECT PartEventInner.EventDateTime  
            FROM PartEvent PartEventInner
            WHERE PartEventInner.PartId = PartEventOuter.PartId
                AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime 
                AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')

这是“优化”查询(不到 1 秒):

select Part.Id as PartId, Location.Id as LocationId
 FROM Part, PartEvent PartEventOuter, District, Location 
WHERE 
    PartEventOuter.EventType = '600'   AND PartEventOuter.AddressId = Location.AddressId  
    AND Part.DistrictId = District.Id   AND Part.PartTypeId = 15   
    AND District.SubRegionId = 11   AND PartEventOuter.PartId = Part.Id  
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'   
    AND NOT EXISTS (
            SELECT PartEventInner.EventDateTime  
            FROM PartEvent PartEventInner
            WHERE PartEventInner.PartId = PartEventOuter.PartId
                **AND EventType = EventType**
                AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime 
                AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')

谁能详细解释为什么它运行得这么快?我只是想更好地理解这一点。

【问题讨论】:

  • 您是否尝试过查看查询计划?
  • 因为 eventtype 上有索引?
  • 你没有这个字段的索引吗?
  • 请注意,如果 EventType 可以为 NULL,则 2 个查询不相同(因为如果 EventType 为 NULL,则 EventType = EventType 不会为真)。
  • 检查 EventType IS NOT NULL 并没有加快查询速度。

标签: sql optimization query-optimization performance


【解决方案1】:

可能是因为您在没有 EventType = EventType 的情况下获得笛卡尔积

来自维基百科:http://en.wikipedia.org/wiki/SQL

"[SQL] 使得进行笛卡尔连接(连接所有可能的组合)变得过于容易,当 WHERE 子句输入错误时,这会导致“失控”结果集。笛卡尔连接在实践中很少使用,因此需要明确的 CARTESIAN 关键字可能是有保证的。(SQL 1992 引入了 CROSS JOIN 关键字,允许用户明确表示要进行笛卡尔连接,但是没有谓词的速记“逗号连接”仍然是可接受的语法,它仍然会邀请相同的错误。)”

实际上,您在第一次查询时经历的行数超过了必要的行数。

http://www.fluffycat.com/SQL/Cartesian-Joins/

【讨论】:

  • 不可能...EventType 上没有表限定符,因此它正在将其与自身进行比较。本质上是一个无操作(即 NULLS 除外)。
  • 在我第一次尝试优化此查询时,实际上我确实将所有内容都转换为相关字段上的(左)内部连接。但是,这并没有对执行时间产生一点影响。
【解决方案2】:

是否存在大量 EventType = Null 的记录?
在添加附加限制之前,您的子查询将返回所有那些 Null 记录,然后必须通过 Not Exists 谓词对外部查询中的每一行进行扫描...因此,您对子查询返回的限制越多,减少必须扫描以验证不存在的行...

如果这是问题所在,如果您在子查询中也将记录限制为 EventType = '600' 可能会更快......

Select Part.Id as PartId, Location.Id as LocationId 
FROM Part, PartEvent PartEventOuter, District, Location 
WHERE PartEventOuter.EventType = '600'   
    AND PartEventOuter.AddressId = Location.AddressId      
    AND Part.DistrictId = District.Id   
    AND Part.PartTypeId = 15       
    AND District.SubRegionId = 11   
    AND PartEventOuter.PartId = Part.Id      
    AND PartEventOuter.EventDateTime <= '4/28/2009 4:30pm'       
    AND NOT EXISTS (SELECT PartEventInner.EventDateTime                  
                    FROM PartEvent PartEventInner
                    WHERE PartEventInner.PartId =  PartEventOuter.PartId
                       AND EventType = '600'                        
                       AND PartEventInner.EventDateTime > PartEventOuter.EventDateTime
                       AND PartEventInner.EventDateTime  <= '4/30/2009 4:00pm')

【讨论】:

  • 速度一样快,但是对于我们的结果集来说是错误的......子查询是正确的(任何“EventType”)
【解决方案3】:

当且仅当该索引的所有列都在查询中时,SQL Server 才使用索引查找。

【讨论】:

    【解决方案4】:

    您添加的每个非索引列都会执行一次表扫描。如果您在 WHERE 子句中较早地缩小查询范围,则后续扫描会更快。因此,通过添加索引扫描,您的表扫描运行的数据更少。

    【讨论】:

      【解决方案5】:

      奇怪,你有一个同时定义了EventType 和EventDateTime 的索引吗?

      编辑:
      等等,EventType 是可以为空的列吗? 如果Column = Column 的值为NULL,则Column = Column 将评估为FALSE*。至少使用默认的 SQL Server 设置。

      更安全的等价物是EventType IS NOT NULL。看到它在速度方面给出了相同的结果吗?


      *:我的 T-SQL 参考说它应该评估为 TRUE 并将 ANSI_NULLS 设置为 OFF,但我的查询窗口另有说明。 *现在很困惑*。
      有什么裁决吗? TRUE、FALSE、NULL或UNKNOWN? :) 一定喜欢 SQL 中的“二进制”逻辑 :(

      【讨论】:

      • PartEvent 视图的列上没有索引(在视图从中提取的表上)
      • 周一上班时必须尝试“EventType IS NOT NULL”
      • 检查 EventType IS NOT NULL 并没有加快查询速度 - 它仍然是 20 秒。检查 EventType = EventType 是否仍需要
      • 您是否比较了执行计划以对此有所了解?我很想知道发生了什么事..
      【解决方案6】:

      这种事情过去比现在普遍得多。例如,Oracle 6 过去对您在 WHERE 子句中设置限制的顺序很敏感。您感到惊讶的原因实际上是因为我们已经非常善于期望 DB 引擎始终能够找到最佳访问路径,无论您如何构建 SQL。 Oracle 6 和 7(之后我切换到 MSSQL)也有提示扩展,您可以使用它来告诉数据库它可能希望如何构建查询计划。

      在这种特定情况下,如果没有看到实际的查询计划,很难给出结论性的答案,但我怀疑不同之处在于您有一个使用 EventType 的复合索引,该索引未用于第一个查询,而是用于第二个查询。这将是不寻常的,因为我希望您的第一个查询无论如何都会使用它,所以我怀疑数据库统计信息可能已经过时了,所以

      重新生成统计数据

      然后再试一次并在此处发布结果。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-19
        • 2019-05-13
        • 2011-01-28
        • 1970-01-01
        • 2015-09-23
        • 2020-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多