4

不久前,我从一位 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')

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

4

6 回答 6

3

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

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

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

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

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

于 2009-08-06T22:02:44.877 回答
1

是否有大量 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')
于 2009-08-06T22:37:35.350 回答
0

奇怪的是,您是否有一个同时定义了EventType和的索引EventDateTime

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

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


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

于 2009-08-06T22:02:50.833 回答
0

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

于 2009-08-06T22:14:07.940 回答
0

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

于 2009-08-06T22:21:18.753 回答
0

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

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

重新生成统计数据

然后重试并在此处发布结果。

于 2009-08-07T08:10:56.180 回答