SQL循环多子串替换

时间:2017-07-19 23:15:10

标签: sql sql-server

我需要构建一个函数来替换表中所有行的多个子字符串。

性能不是一个大问题,因为这是一次性操作,但有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;
    }
}

2 个答案:

答案 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的速度球场,但代码更清晰(更易于维护)。