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

TSQL: Replace same field multiple times in one query

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

免责声明:我可以使用游标或使用动态 sql 来创建嵌套的替换字符串;想知道能不能简明扼要

我有替代品 table,

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

和 table 个要替换的值:

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

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

是啊

WC

我给出这个答案是基于这样的假设,即示例只是为了我们的方便而使用临时 tables。

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

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

函数:

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)

数据样本的临时表

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' )

更新前数据


使用动态查询的解决方案

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)

更新后的数据

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

这是一个动态查询,在我看来,它比 Vasily 的稍微简单一些,尽管两者确实做同样的事情。该代码对于任意数量的行都应该执行得非常好。试试看:

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 解决方案相比,这不是很好。相比于功能,它更好。递归所做的是将每个更改逐一应用于整个数据集。因此,替换行适用于所有数据。然后将第二个更改(行)应用于整个数据集等...因此对于 small 数量的更改,因为它通过 RBAR,然后是一个不太大的值集,它会工作得很好。