我试图在大型数据库中的字段上执行数据清理。我有一个参考表,其中包含带有替换的单词,如果您愿意,还包含宏。我想以最有效的方式将这些更改应用于包含数百万行的表。话虽如此,让我在下面提供一些虚拟数据,以便您可以看到过程:
Street_Addresses表:
Street_Name | Expanded_Name
------------------+--------------
100 Main St Ste 5 | NULL
25 10th Ave Apt 2 | NULL
75 Bridge Rd | NULL
Word_Substitutions表:
Word | Replacement
-----+------------
St | Street
Ave | Avenue
Rd | Road
Ste | Suite
Apt | Apartment
因此最终结果将是更新后的结果:
Street_Name | Expanded_Name
------------------+--------------
100 Main St Ste 5 | 100 Main Street Suite 5
25 10th Ave Apt 2 | 25 10th Avenue Apartment 2
75 Bridge Rd | 75 Bridge Road
这里的挑战是需要进行的大量替换,实际上是单个值的多次替换。想到的最初的想法是使用标量函数来封装这个逻辑。但是你可以想象,这不是数百万行的表现。
CREATE FUNCTION Substitute_Words (@Text varchar(MAX))
RETURNS varchar(MAX) AS
BEGIN
SELECT @Text = REPLACE(' ' + @Text + ' ', ' ' + Word + ' ',
' ' + Replacement + ' ') FROM Word_Substitutions
RETURN LTRIM(RTRIM(@Text))
END
我决定改为查看基于集合的操作,并提出以下建议:
WHILE (1 = 1)
BEGIN
UPDATE A SET Expanded_Name = LTRIM(RTRIM(REPLACE(
' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ',
' ' + W.Word + ' ', ' ' + W.Replacement + ' ')))
FROM Street_Addresses AS A
CROSS APPLY (SELECT TOP 1 Word, Replacement
FROM Word_Substitutions WHERE CHARINDEX(' ' + Word + ' ',
' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ') > 0) AS W
IF (@@ROWCOUNT = 0)
BREAK
END
现在,根据我的实际数据集大约需要2个小时,如果可能,我想减少这个数据 - 是否有人有优化建议?
更新
通过仅使用内部联接,我能够将执行时间减少到大约5分钟。我最初认为使用带有返回多行的内连接的更新是行不通的。似乎更新仍然有效,但源行将获得单个而不是多个更新。显然,SQL Server为更新选择一个随机结果行,丢弃其他行。
WHILE (1 = 1)
BEGIN
UPDATE A SET Expanded_Name = LTRIM(RTRIM(REPLACE(
' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ',
' ' + W.Word + ' ', ' ' + W.Replacement + ' ')))
FROM Street_Addresses AS A
INNER JOIN Word_Substitutions AS W ON CHARINDEX(' ' + W.Word + ' ',
' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ') > 0
IF (@@ROWCOUNT = 0)
BREAK
END
答案 0 :(得分:2)
我认为这里最好的方法是将修改后的数据存储在数据库中。您可以使用ID和格式化地址创建单独的表,也可以在当前表中添加其他列。
然后,因为你已经有很多记录,你应该更新它们。在这里,我需要选择,创建内部函数并将其用于更新当前记录(可能很慢,但一旦结束,您将在表中已有数据)或创建CLR过程和使用正则表达式的力量。
然后,对于新插入的记录,创建AFTER INSERT TRIGGER将非常灵活,它将调用您的SQL或CLR函数并更新当前插入的记录。
答案 1 :(得分:2)
你总是可以做一些荒谬的事情并将其作为动态SQL运行,并且所有替换内联:
declare @sql nvarchar(max)
set @sql = 'Street_Name'
select @sql = 'replace(' + @sql + ', '' ' + Word + ' '', '' ' + Replacement + ' '')'
from Word_Substitutions
set @sql = 'update Street_Addresses set Expanded_Name = ' + @sql
exec sp_executesql @sql
是的,我完全期待一个或两个downvote,但是这种方法在某些情况下可以正常工作,因为UDF和递归CTE在大型数据集上有时会非常慢。并且不时发布离墙解决方案很有趣。
无论如何,我很想知道这会如何运行,特别是如果结合@gotqn存储和基于触发器的更新的建议(我同意并且已经投票)。
我目前正在运行大约3秒钟,在一个适度的盒子上有275个替换单词和100k地址。