在这种情况下如何避免在SQL Server上进行表扫描

问题描述 投票:0回答:1

有两个表,CostsLogsCosts表中的数据可以在数百万行中,而在Logs表中的数据可以在数十亿行中。

我需要在生产环境中的服务任务中更新CostBy表中的Costs列,每次运行不超过100条记录。

CREATE TABLE Cost
(
    C_PK uniqueidentifier primary key not null,
    C_CostBy varchar(3) not null
)

CREATE TABLE Logs
(
    L_PK uniqueidentifier primary key not null,
    L_ParentTable varchar(255) not null,  -- Table Cost and other table's name
    L_ParentID uniqueidentifier not null, -- Cost's pk and other table's pk
    L_Event varchar(3) not null, -- Part are 'ADD' and other event types
    L_User varchar(3) not null 
 )

CREATE NONCLUSTERED INDEX [L_ParentID] 
    ON [dbo].[Costs] ([L_ParentID] ASC)

这是原始的更新语句:

UPDATE TOP(100) Costs
SET CostBy = ISNULL(L_User, '~UK')
FROM Costs
LEFT JOIN Logs ON L_ParentID = C_PK AND L_Event = 'ADD'
WHERE CostBy = ''

但是,该语句引入了巨大的性能问题,Costs表中的表扫描成本很高。

我的问题是如何避免在Costs表中进行表扫描或如何优化更新语句?

提前感谢。

sql-server database-performance query-performance full-table-scan
1个回答
0
投票

您可能想要尝试以下操作。

首先,在日志上创建索引,包括所有相关列:

CREATE INDEX ix ON Logs 
(
  L_Parent_ID -- join condition, variable
)
INCLUDE 
(
  L_User -- no filter condition, but you use it your update
)
WHERE 
(
  L_Event = 'ADD' -- join condition, constant
)

如果这是唯一索引,即对于给定的父ID,只有ADD事件存在的一行将存在,请确保将其设为唯一索引,因为它可以显着提高性能。

第二,这是一个命中注定的情况,您可以尝试使用Costs(CostBy)的索引,因为您只是在寻找要更新的空CostBy值。该索引将根据您的查询进行更新,因为它正在更新,因此它可能会使查询变慢而不是加快查询速度。这取决于许多因素。

[如果您具有企业许可证,请同时使用WITH (DATA_COMPRESSION = PAGE)进行尝试,这可以大大缩短IO时间,但会浪费CPU。这取决于您的瓶颈。

此外,根据数据的性质,更新统计信息可能会改善您的查询。如果其中CostBy =''的行数与其中的其他值不成比例,那么您可以从该字段的完整统计信息中受益。如果只一次需要此查询,请考虑NORECOMPUTE

CREATE STATISTICS st_Costs_CostBy
ON Costs (CostBy)  
WITH FULLSCAN, NORECOMPUTE;  
© www.soinside.com 2019 - 2024. All rights reserved.