SQL循环多子串替换
我需要构建一个函数来替换表中所有行的多个子字符串 性能不是一个大问题,因为这是一个一次性操作,但有48个映射和大约30000行。我知道在整个数据库上循环48次是相当愚蠢的,但SQL不是我的舵手。如果是java或C++,那就是蛋糕。 基本上,我需要以下函数的SQL模拟。如果SQL不能短路循环,那就好了。我见过SQLReplace函数,但将其正确封装在用户定义的函数中是我的主要障碍 我使用的是微软SQL Server,如果这会产生任何特殊的怪癖的话SQL循环多子串替换,sql,sql-server,Sql,Sql Server,我需要构建一个函数来替换表中所有行的多个子字符串 性能不是一个大问题,因为这是一个一次性操作,但有48个映射和大约30000行。我知道在整个数据库上循环48次是相当愚蠢的,但SQL不是我的舵手。如果是java或C++,那就是蛋糕。 基本上,我需要以下函数的SQL模拟。如果SQL不能短路循环,那就好了。我见过SQLReplace函数,但将其正确封装在用户定义的函数中是我的主要障碍 我使用的是微软SQL Server,如果这会产生任何特殊的怪癖的话 mapping[] maps = { {" st
mapping[] maps = { {" st ", " Street "}, {" st. ", " Street "}, ...};
for(row r : table) {
String orig = r.data(colName);
for(mapping m : maps) {
r.data(colName).replace(m.first, m.second);
if(r.data(colName) != orig)
break;
}
}
@霍根的想法是对的。此语法应该更接近于工作:
WITH map as (
SELECT v.*
FROM (VALUES (' st ', ' Street ', 1),
(' st. ', ' Street ', 2)
) v(str, repstr, n)
),
cte as (
SELECT replace(t.field, map.str, map.repstr) as field, map.n as n
FROM t JOIN
map
ON map.n = 1
UNION ALL
SELECT replace(cte.field, map.str, map.repstr) as field, map.n + 1
FROM cte JOIN
map
ON map.n = cte.n + 1
)
SELECT field
FROM (SELECT cte.*, MAX(cte.n) OVER (PARTITION BY cte.field) as maxn
FROM cte
) x
WHERE n = maxn;
您可能希望在原始表的CTE中包含更多字段
CREATE FUNCTION [dbo].[StandardizeAddress](@address varchar(123))
RETURNS varchar(250)
WITH SCHEMABINDING
AS
BEGIN
RETURN
REPLACE(REPLACE(
@address + ' '
, ' st ', ' Street')
, ' st. ', ' Street ')
END
我们就是这样创建一个标量函数的。使用上面的代码从一个171000行的表中计算地址需要240毫秒。使用我们的实际函数,它有80多个替换,并且对171000行执行一些其他操作需要5秒钟。但是,我们存储地址的标准化版本,因为为了性能起见,我们正在进行复杂的人员搜索和预计算标准值。因此,当添加行或修改地址时,该函数只运行一次,因此该函数的速度不是问题
相比之下,Gordon的解决方案对同一数据集需要4.5秒(而链式替换则需要240毫秒)。更换4次而不是两次,CTE解决方案需要7.8秒,而更换需要275毫秒
我需要添加一个警告,即可以嵌套多少个函数调用是有限制的。根据stackOverflow的另一个问题,限制是244,这比递归CTE的默认最大递归限制大很多
另一个比嵌套替换函数慢一点(大约多75%的时间)的选项如下:
select c3.address from (select REPLACE(@address, ' st ', ' Street ') address) c1
cross apply (select REPLACE(c1.address, ' st. ', ' Street ') address) c2
cross apply (select REPLACE(c2.address, ' dr ', ' Drive ') address) c3
但我看不出有什么好处。您也可以编写一个c#CLR函数,但我怀疑调用开销可能会使它比只使用嵌套的REPLACE调用慢
编辑-
自从发布这篇文章后,我发布了一个类似于嵌套替换的速度标准,但代码更干净(更易于维护)。您在maps中拥有的东西是常量(即文字)还是动态的,比如从另一个表中?您试图更新的模式是什么?你能把表格定义和一些样本数据放在前后吗?这里没有足够的信息来真正回答您的问题。如果您不关心性能,为什么需要在“更好的”sql中这样做?您知道有一个循环可以完全满足您的需要--只需使用它。在不了解更多信息的情况下,只需将调用链接到
REPLACE
,ex)REPLACE(REPLACE(colName,'街','街','街')
@hatchet-我不认为用多重替换对其进行硬编码是他所要求的——你也没有——这就是为什么你要添加注释而不是回答。既然速度不是一个很大的问题,那么使用映射表和递归清理的解决方案不是更容易维护吗?我们的版本很容易维护。而且我们不必担心递归限制。大多数开发人员理解REPLACE。没有那么多人理解递归通用表表达式。没错,SQL Server有递归限制。您不需要人们查看CTE——只需要一个带有REPLACE\u target
和REPLACE\u和REPLACE\u的映射表。嗯,REPLACE版本n是一个数量级,使用少量映射(可能使用大量映射)更快,易于维护,易于理解,并且可以工作。这对我来说已经足够好了。当您从Gordon的解决方案中的cte order BY n DESC将最终选择更改为select TOP 1字段时?