有两个表,Costs
和Logs
。 Costs
表中的数据可以在数百万行中,而在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
表中进行表扫描或如何优化更新语句?
提前感谢。
您可能想要尝试以下操作。
首先,在日志上创建索引,包括所有相关列:
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;