最近我们在Update
语句中遇到了严重的性能问题,之前更新过去需要3分钟,现在更新耗时近2小时。对最近的数据库设计我们更改了存储过程代码,如下所示。
这是我的详细情况:我有超过1000万行的非常大的事实表,我需要在满足条件时更新该事实表的列。所以我们写了一个更新语句来更新该列:
declare @var varchar(max) = (select metrcikey from metricdim where metrciname ='XYZ')
Update
fopty
set
Metrickey = metrickey+','+@var
from
optyfact opty
inner join
optydim dim on opty.optyid = dim.optyid
inner join
geodim geo on geo.atukey = opty.atukey
inner join
agreementdim ag on opty.optyid = ag.optyid
inner join
account acc on acc.optyid = acc.optyid
where
dim.optytype= 'ABC'
and geo.atukey =145
and ag.agreementtype ='Sold'
and acc.accountteamManager ='XXX'
有没有办法优化上面的查询,根据我的理解,执行查询2小时的语句是由于SET语句
Metrickey = Metrickey + @var
此处Metrickey
列为varchar
,@var
变量也为varchar(max)
,要连接此字符串,需要花费更多时间。
如果可以优化上述查询,对我来说将是一个很好的帮助,因此请建议使用varchar
值更新varchar
列的最佳方法。
答案 0 :(得分:0)
没有用于更新VARCHAR列的真正秘密超快解决方案。无论如何,它都会相对缓慢,但你可以采取一些措施来减轻痛苦。
确保所有表格上都有适当的覆盖索引。
注意:我将假设你的代码中有一个拼写错误 - “Update fopty”应该是“Update opty”,对吗?
对于optyfact表(opty别名),您应该在optyid
和atukey
列上包含metrickey
的索引。
对于其他表,将所有列放在索引中 - 不要担心包括(覆盖)任何其他列。
完成所有设置之后,请通过查询分析器确保您在所有表格上执行INDEX SEEK,否则这将会运行一段时间,就像您指示的那样。
除此之外,请确保您加入狭窄的列。 INT很好,VARCHAR很糟糕。
最后一件事:确保metrickey
列不是您的聚集索引的一部分,或者表将在更新期间继续重新组织。
这就是我得到的全部。祝你好运!
答案 1 :(得分:0)
您应该考虑批量更新(例如,一次更新100,000行),而不是尝试一次更新所有行。它不一定总需要花费更少的时间,但它不会将所有内容锁定两个小时。我们可以将实际的optyid值转储到临时表中,这样我们就不必在更新中连续引用基表了。
CREATE TABLE #q(rn INT IDENTITY(1,1) PRIMARY KEY, optyid INT);
DECLARE
@rc INT,
@step INT = 1,
@chunk INT = 100000;
INSERT #q(optyid) SELECT DISTINCT opty.optyid
FROM dbo.optyfact AS opty
INNER JOIN dbo.optydim AS dim ON opty.optyid = dim.optyid
INNER JOIN dbo.geodim AS geo ON geo.atukey = opty.atukey
INNER JOIN dbo.agreementdim AS ag ON opty.optyid = ag.optyid
INNER JOIN dbo.account AS acc ON acc.optyid = acc.optyid
WHERE
dim.optytype = 'ABC';
AND geo.atukey = 145
AND ag.agreementtype = 'Sold'
AND acc.accountteamManager = 'XXX';
SET @rc = @@ROWCOUNT;
WHILE @step <= ((@rc / @chunk) + 1)
BEGIN
BEGIN TRANSACTION;
UPDATE o SET MetricKey += ',' + @var
FROM dbo.optyfact AS o
INNER JOIN #q AS q ON o.optyid = q.optyid
WHERE q.rn BETWEEN (((@step-1)*@chunk)+1) AND (@step*@chunk);
COMMIT TRANSACTION;
CHECKPOINT;
SET @step += 1;
END
DROP TABLE #q;