如何减少使用包含数百万行的表的sql server数据库查询的执行时间

eiee3dmh  于 2022-12-26  发布在  SQL Server
关注(0)|答案(1)|浏览(233)

我正在尝试执行基于某些参数获取计数的查询。截至目前,该查询所操作的表包含3800万条数据。执行该查询需要6秒。我希望将执行时间缩短到1秒以内,因为我们正在使用结果在Web应用程序上显示。

SELECT Policy id,Policy name, Count(Policy) count
from (SELECT ItemID, OUID, ItemType, ItemGeneratedBy,  CreatedDateTime GeneratedDate,
Month, OU Agency FROM ItemMaster) IM
left JOIN (SELECT ItemId, PolicyId FROM ItemPolicy) IP ON IP.ItemId = IM.ItemId
left JOIN (SELECT PolicyId, PolicyName Policy FROM Policies) P ON P.PolicyId = IP.PolicyID
left JOIN (SELECT ItemID, ActualActionStr ActionTaken FROM ItemExtension_McAfee) IEM ON IEM.ItemId = IM.ItemId
left JOIN (SELECT Id, ItemType Channel FROM ItemType) IT ON IT.Id = IM.ItemType
INNER JOIN (SELECT ID, LoginName Violator FROM ItemADUser) IAU ON IAU.ID = IM.ItemGeneratedBy
WHERE IM.OUId is NOT NULL AND Violator = 'sandy' AND ActionTaken='affirm'
AND Policy='dashmagiq network sharing policy'  and GeneratedDate >= '2022-06-11 00:00:00'
Group By   Policy,Policy Order By Count(Policy) desc

https://drive.google.com/file/d/1y0C6wPW2yXKxxYtbkGcnY4JgkTVJAjdE/view?usp=share_link
上面的链接是这个查询的执行计划,您对如何实现这个目标有什么想法吗?

gcuhipw9

gcuhipw91#

在执行计划中,一个好的起点是

  • 根据返回的行数查找执行过多读取的位置。
  • 查看每个步骤在实际执行计划中花费的时间,找出运行时间最长的步骤
  • 查找带有!感叹号的所有步骤,感叹号表示存在潜在问题。

然后,这些问题就成为需要改进的候选问题--通过改进查询和/或更改索引。
对于索引,最好不要只是采纳下面的建议,而是要查看当前的索引和表的使用情况,并检查应该更新和/或添加哪些索引。我在下面的索引中的目标是提供全面覆盖的索引。
作为参考,这里是Paste The Plan link,但我只是在SSMS中查看它。
我认为第一个主要问题是在ItemPolicy和ItemMaster中。这两个似乎都读取了所有行,并且是时间的主要贡献者。基于数据,我建议使用以下索引(注意,为了简单起见,我将它们命名为IX_1、IX_2等)

CREATE NONCLUSTERED INDEX IX_1 ON ItemPolicy (PolicyID, ItemID);
CREATE NONCLUSTERED INDEX IX_2 ON ItemMaster (Item_ID, OUID) INCLUDE (ItemType, ItemGeneratedBy,  CreatedDateTime, Month);

如果您的查询往往总是在一个很窄的时间范围内,则可以使用上述IX_2的替代方法,在CreatedDateTime上创建索引,例如:

CREATE NONCLUSTERED INDEX IX_2v2 ON ItemMaster (CreatedDateTime) INCLUDE (Item_ID, OUID, ItemType, ItemGeneratedBy, Month)

然后是对ItemExtension_McAfee表的完全扫描-该表被标识为缺少索引。

CREATE NONCLUSTERED INDEX IX_3 ON ItemExtension_McAfee (ItemID, ActualActionStr)

但是,索引建议是另一种方式,因此您可以按相反的顺序尝试

CREATE NONCLUSTERED INDEX IX_3v2 ON ItemExtension_McAfee (ActualActionStr, ItemID)

最后,这不会对总时间产生很大影响,但是您完全读取了ItemADUser表以返回一行,并且它使用了LoginName。

CREATE NONCLUSTERED INDEX IX_4 ON ItemADUser (LoginName);

如果可能的话,试着加入索引,看看什么在您的情况下运行良好(不仅仅是查询--尝试使用其他过滤选项)。
关于查询本身-似乎没有任何“陷阱”,但为什么要使用子查询,而不只是使用表?例如,而不是

from (SELECT ItemID, OUID, ItemType, ItemGeneratedBy,  CreatedDateTime GeneratedDate, Month, OU Agency FROM ItemMaster) IM
left JOIN (SELECT ItemId, PolicyId FROM ItemPolicy) IP ON IP.ItemId = IM.ItemId

为什么不使用以下方法?

from ItemMaster IM
left JOIN ItemPolicy IP ON IP.ItemId = IM.ItemId

最后-在某个阶段,我会审查你的索引:哪些没有被使用或者只是很少被使用;有两个非常相似的索引,可以合并成一个以及缺少索引的位置(例如,列出的缺少索引,以及正在执行大量聚集索引扫描的表,这些扫描可以通过索引来改进)。

相关问题