我从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')
任何人都可以详细解释为什么这样运行得更快?我只是想更好地理解这一点。
答案 0 :(得分:3)
可能是因为您在没有EventType = EventType
的情况下获得了笛卡尔积来自WikiPedia:http://en.wikipedia.org/wiki/SQL
“[SQL]使得进行笛卡尔连接(加入所有可能的组合)变得太容易了,当WHERE子句输入错误时会导致”失控“结果集。笛卡尔连接在实践中很少用到需要可以保证显式的CARTESIAN关键字。(SQL 1992引入了CROSS JOIN关键字,允许用户明确表示笛卡尔连接是有意的,但是没有谓词的简写“逗号连接”仍然是可接受的语法,它仍然会引用它错误。)“
在第一次查询时,您实际上要经历的行数超过了必要的数量。
答案 1 :(得分:1)
EventType = Null是否有大量记录? 在添加aditional限制之前,子查询将返回所有那些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')
答案 2 :(得分:0)
奇怪,你有一个同时定义了EventType
和EventDateTime
的索引吗?
修改强>
等等,EventType是一个可以为空的列吗?
如果Column = Column
的值为FALSE
,NULL
将评估为EventType IS NOT NULL
*。至少使用默认的SQL Server设置。
更安全的等价物是TRUE
。看到它可以快速提供相同的结果吗?
*:我的T-SQL参考说它应该评估为ANSI_NULLS
,OFF
设置为TRUE
,但我的查询窗口则另有说明。 *现在不知所措* 。
任何裁决? FALSE
,NULL
,UNKNOWN
或{{1}}? :)得到SQL中的'二进制'逻辑:(
答案 3 :(得分:0)
当且仅当此索引的所有列都在查询中时,SQL Server才使用索引。
答案 4 :(得分:0)
您添加的每个非索引列都会执行表扫描。如果先在WHERE子句中缩小查询范围,则后续扫描速度会更快。因此,通过添加索引扫描,您的表扫描将以较少的数据运行。
答案 5 :(得分:0)
这种事情过去比现在更常见。例如,Oracle 6过去对您在WHERE子句中放置限制的顺序很敏感。您之所以感到惊讶,实际上是因为无论您如何构建SQL,我们都非常期待数据库引擎始终能够找到最佳的访问路径。 Oracle 6&amp; 7(之后我切换到MSSQL)也有提示扩展,你可以用来告诉数据库如何构建查询计划。
在这个特定情况下,如果没有看到实际的查询计划,很难给出确定的答案,但我怀疑不同之处在于你有一个使用EventType的复合索引,该索引不用于第一个查询但是用于第二个查询。这是不寻常的,因为我希望您的第一个查询仍然使用它,所以我怀疑数据库统计信息可能已过时,所以
重新统计
然后再试一次并在此处发布结果。