TSQL:在一个查询中多次替换相同的字段

时间:2015-05-13 16:17:10

标签: tsql replace

给定一个替换表,是否可以将所有替换应用于同一查询中的表中的列?

免责声明:我可以使用游标或动态sql来创建嵌套的替换字符串;我想知道它是否可以简明扼要地完成。

我有一个替换表,

create table #replacements(old varchar(max), new varchar(max))
insert into #replacements values
('X','Y'),
('A','B'),
('W','V'),
('C','D')

和要替换的值表:

create table #value(value varchar(max))
insert into #value values
('XA'),
('WC')

我想在一个回复中执行这些操作:

YB

VD

有没有办法做到这一点?我试过了,

update v
set value = replace(value,old,new)
from #value v,
#replacements

但是这给出了(只完成了连接中的第一行):

YA

WC

3 个答案:

答案 0 :(得分:2)

我基于假设示例仅为了方便使用临时表而给出了这个答案。

但是,您可以使用标量函数为您执行此操作。

注意 - 如上所述。这假设函数中的表不需要是临时表。

<强>功能:

CREATE FUNCTION dbo.ReplaceCharacters (@value VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
    SELECT @value = REPLACE(@value, r.old, r.new)
    FROM replacements r --Assumes this is no longer a temp table

    RETURN @value
END

这样您就可以直接从#value

调用该函数

查看结果集:

SELECT
    v.value CurrentValue,
    dbo.ReplaceCharacters(v.value) ReplacedValue
FROM #value v

<强>输出:

+--------------+----------------+
| CurrentValue | ReplacedValue |
+--------------+----------------+
|     XA       |     YB         |
|     WC       |     VD         |
+--------------+----------------+

应用更改:

UPDATE #value
SET value = dbo.ReplaceCharacters(value)

答案 1 :(得分:2)

JBond的答案很简单,但对于任何大量的行来说它都是SLOW,因为它将是RBAR。他的功能必须逐个获取每个值,然后通过替换表运行它。如果有大量行,您会看到一些严重的性能问题。

这是一个动态查询,在我看来,比瓦西里有点简单,尽管两者都做同样的事情。代码应该对任意数量的行都能很好地执行。试试吧:

DECLARE @Replace VARCHAR(MAX);

SELECT @Replace = COALESCE('REPLACE(' + @Replace,'REPLACE(value') + ',''' + old + ''',''' + new + ''')'
FROM #replacements


EXEC
(
    'UPDATE #value
    SET Value = ' + @Replace
)

递归解决方案

WITH CTE_replacements
AS
(
    SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) row_num,
            old,
            new
    FROM #replacements
),
CTE_recursion
AS
(
    SELECT  REPLACE(value,old,new) AS value,
            1 AS cnt
    FROM #value
    CROSS APPLY (SELECT old,new FROM CTE_replacements WHERE row_num = 1) CA

    UNION ALL

    SELECT  REPLACE(value,old,new) AS value,
            cnt + 1
    FROM cte_recursion A
    CROSS APPLY (SELECT old,new FROM CTE_replacements WHERE row_num = cnt + 1) CA

)

SELECT TOP(SELECT COUNT(*) FROM #value) *
FROM CTE_recursion
ORDER BY 2 DESC
OPTION (MAXRECURSION 0)

与动态SQL解决方案相比,这并不是很好。与功能相比,它更好。递归的作用是将每个更改逐个应用于整个数据集。因此,替换行将应用于所有数据。然后第二个更改(行)应用于整个数据集等...因此,对于数量的更改,因为它通过它RBAR,然后一个不太大的值集,它将工作正好。

答案 2 :(得分:1)

数据样本的临时表

IF OBJECT_ID('Tempdb..#replacements') IS NOT NULL 
    DROP TABLE #replacements
IF OBJECT_ID('Tempdb..#value') IS NOT NULL 
    DROP TABLE #value;

CREATE TABLE #replacements
    (
      old VARCHAR(MAX) ,
      new VARCHAR(MAX)
    )
INSERT  INTO #replacements
VALUES  ( 'X', 'Y' ),
        ( 'A', 'B' ),
        ( 'W', 'V' ),
        ( 'C', 'D' )
CREATE TABLE #value ( value VARCHAR(MAX) )
INSERT  INTO #value
VALUES  ( 'XA' ),
        ( 'WC' )

更新前的数据

enter image description here

使用动态查询的解决方案

DECLARE @Query AS VARCHAR(MAX)
SET @Query = 'UPDATE #value SET Value = '
    + ( SELECT  REPLICATE('REPLACE(', ( SELECT  COUNT(*) - 1
                                        FROM    #replacements
                                      )) + 'REPLACE(value'
                + ( SELECT  ',''' + r.old + ''' ,''' + r.new + ''') '
                    FROM    #replacements AS r
                  FOR
                    XML PATH('')
                  ) AS R
      )

EXEC  (@Query)

更新后的数据

enter image description here