我需要构建一个函数来替换表中所有行的多个子字符串。
性能不是一个大问题,因为这是一次性操作,但有48个映射和大约30,000行。我知道循环整个数据库48次是非常愚蠢的,但SQL不是我的驾驶室。如果这是Java或C ++,它就是蛋糕。
基本上,我需要以下函数的SQL模拟。如果SQL不能进行短路循环,那很好。我已经看过SQL替换函数,但是在用户定义的函数中正确封装它是我的主要障碍。
我使用的是Microsoft SQL Server,如果它产生任何特殊的怪癖。
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;
}
}
答案 0 :(得分:2)
@Hogan有正确的想法。这种语法应该更接近于工作:
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中包含更多字段。
答案 1 :(得分:1)
CREATE FUNCTION [dbo].[StandardizeAddress](@address varchar(123))
RETURNS varchar(250)
WITH SCHEMABINDING
AS
BEGIN
RETURN
REPLACE(REPLACE(
@address + ' '
, ' st ', ' Street')
, ' st. ', ' Street ')
END
创建这样的标量函数就是我们这样做的方式。使用上面的代码计算171,000行表中的地址需要240 ms。使用我们的实际函数,有超过80个替换,并做一些其他操作需要5秒,171,000行。但是,我们存储地址的标准化版本,因为我们正在进行复杂的人员搜索并为了性能而预先计算标准化值。因此,该函数仅在添加行或修改地址时运行一次,因此此函数的速度不是问题。
为了进行比较,Gordon的解决方案针对相同的数据集需要4.5秒(对于链式REPLACE而言为240毫秒)。有4个替换而不是2个,CTE解决方案需要7.8秒,而REPLACE需要275毫秒。
我需要添加一个警告,即可以嵌套多少函数调用是有限制的。根据stackOverflow的另一个问题,限制是244,这是一个比递归CTE的默认最大递归限制更大的数量。
另一个选项比嵌套的REPLACE函数慢一点(大约多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调用更慢。
修改强> - 自发布以来,我发布了answer to a similar question,似乎是嵌套REPLACE的速度球场,但代码更清晰(更易于维护)。