SQL循环通过800万条记录并更新它们
我有一个审计表,有大约800万条记录。我最近添加了两个新列,我需要使用一些规则/条件从现有列更新它们。基本上,一开始,每当在表中更新FK时,它都会将旧的和新的FK ID存储到审计表中。比如说SQL循环通过800万条记录并更新它们,sql,sql-server,sql-update,sqlperformance,bulkupdate,Sql,Sql Server,Sql Update,Sqlperformance,Bulkupdate,我有一个审计表,有大约800万条记录。我最近添加了两个新列,我需要使用一些规则/条件从现有列更新它们。基本上,一开始,每当在表中更新FK时,它都会将旧的和新的FK ID存储到审计表中。比如说 Table A ID Name 1 First A 2 Second A 3 Third A Table B ID AID Name 1 1 First B 2 1 Second B 3 2 Third B Audi
Table A
ID Name
1 First A
2 Second A
3 Third A
Table B
ID AID Name
1 1 First B
2 1 Second B
3 2 Third B
Audit
ID TableName FieldName OldValue NewValue
现在如果我更新表B的第一条记录
从1 1 First B到1 3 First B,审核表将把更改存储为
Audit
ID TableName FieldName OldValue NewValue
1 Table B AID 1 3
现在我已经更新了审计表来存储FK的实际文本值,即上面的更改将存储为
Audit
ID TableName FieldName OldValue NewValue OldText NewText
1 Table B AID 1 3 First A Third A
问题是我已经有了大约800万条记录,我需要为这些记录创建新的专栏。我已经写了下面的问题来做这件事
declare @sql nvarchar(max);
declare @start int = 1
while @start <= 8000000
begin
select top 10000 @sql = COALESCE(@sql+'Update Audit set ','Update Audit set') +
isnull(' OldText = ('+ dbo.GetFKText(i.TableName, i.FieldName)+case when len(isnull(i.OldValue,'')) < 1 then null else i.OldValue end +'),',' OldText = OldValue, ') +
isnull(' NewText = ('+ dbo.GetFKText(i.TableName, i.FieldName)+case when len(isnull(i.NewValue,'')) < 1 then null else i.NewValue end +')',' NewText = NewValue ') +
' where AuditID = '+cast(i.AuditID as nvarchar(200))+' and lower(ltrim(rtrim(TableName))) <> ''audit'';'
from Audit i where i.AuditID >= @start
exec sp_executesql @sql
set @start = @start+10000;
end
它工作正常,但速度非常慢,它在13小时内更新了大约100万条记录。是否有人可以帮助改进此查询或建议其他更新方式
谢谢看来您没有在循环迭代之间清除@SQL。您将生成10000条语句,执行它们,然后追加10000条语句,执行这20000条语句,以此类推。此外,您还将使用TOP而不使用ORDER BY。无法知道在每个循环迭代中要处理哪10000条记录。为了帮助理解,为什么不尝试一些可以测试的更简单的方法呢。在我不知道SQLServer中的上下文切换成本时进行循环。但是您正在动态地构建一个sql字符串,然后执行该字符串。重复800万次。可能是由一个部分首先构建所有sql语句,并将它们放入一个文件(即不执行),对文件进行处理(分解为多个部分,添加提交等),然后运行脚本(即不作为动态sql)更高效,更不容易出错。或者另一种方法:构建一个csv以进行大容量加载——但这取决于处理了多少不同的表和列。感谢David找到bug,感谢Glenn找到另一种方法。既然脚本已经存在,我将首先更新脚本,修正David指出的问题。我想这可能会解决它。如果这仍然需要很长时间,那么只需创建脚本并将其保存在文件中,然后批量运行。
declare @res nvarchar(max)='';
declare @fn nvarchar(200);
declare @ttn nvarchar(200);
declare @tcn nvarchar(200);
SELECT top 1
@ttn = kcu.table_name
,@tcn = kcu.column_name
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu
INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
Where ccu.TABLE_NAME = @TableName and ccu.COLUMN_NAME = @FieldName
if isnull(@ttn,'') != '' and ISNULL(@tcn,'') != ''
begin
select @fn= COLUMN_NAME
from (SELECT top 1 COLUMN_NAME ,
case when COLUMN_NAME like (@ttn+'Name') then 0
when COLUMN_NAME like (@ttn+'%Name') then 1
when COLUMN_NAME like (@ttn+'Code') then 2
when COLUMN_NAME like (@ttn+'%Code') then 3 else 4 end as CPriority
FROM JVO.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @ttn and (COLUMN_NAME like '%Name' or COLUMN_NAME like '%Code'
)
order by CPriority) as aa;
RETURN 'select '+@fn+' from '+@ttn+' where '+@tcn+' = ';
end
return null;