【问题标题】:SQL Server multiple REPLACE with #temp tableSQL Server 用#temp 表多次替换
【发布时间】:2016-10-05 18:33:20
【问题描述】:

我正在尝试在 SQL Server 查询中替换多个字符,并希望通过 #temp 表而不是嵌套的 REPLACE 来实现这一点。我得到了下面的 SQL 代码,想要达到类似的结果

ABC
DEF
GHI

IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2

CREATE TABLE #temp
(
    STRING_TO_REPLACE NVARCHAR(5)
)
INSERT INTO #temp (STRING_TO_REPLACE)
VALUES            (' ')
                 ,('/')
                 ,('_') 

CREATE TABLE #temp2
(
    STRING_NAME NVARCHAR(5)
)

INSERT INTO #temp2 (STRING_NAME)
VALUES            ('A BC')
                 ,('D/EF')
                 ,('G_HI')

SELECT REPLACE(t2.STRING_NAME,(SELECT t1.STRING_TO_REPLACE   
                               FROM #temp t1),'') 
 FROM #temp2 t2

IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2

我可以通过嵌套替换实现结果

SELECT REPLACE(REPLACE(REPLACE(t2.STRING_NAME,'_',''),'/',''),' ','')  FROM #temp2 t2

但真的很想通过#temp 表执行此操作。请有人可以帮助我。 当我尝试运行我的第一个代码时,我收到以下错误

消息 512,级别 16,状态 1,第 23 行子查询返回超过 1 个 价值。当子查询跟随 =、!=、 时,这是不允许的

、>= 或当子查询用作表达式时。

【问题讨论】:

  • 您是尝试动态生成嵌套选择,还是尝试生成 3 个(在这种情况下)选择语句以连续运行。 (或其他)

标签: sql sql-server tsql


【解决方案1】:

这是使用CROSS APPLY的一种方法

SELECT result 
FROM   #temp2 t2 
       CROSS apply (SELECT Replace(string_name, t1.string_to_replace, '') AS 
                           result 
                    FROM   #temp t1) cs 
WHERE  result <> string_name 

结果:

result
-----
ABC
DEF
GHI

注意:这只有在每个string_name只有一个string_to_replace时才有效

更新:要在单个string_name 中处理多个string_to_replace,这是使用Dynamic sql 的一种方法

我对@9​​87654329@ 表做了一个小改动,将identity 属性添加到循环中

IF Object_id('tempdb..#temp') IS NOT NULL 
  DROP TABLE #temp 

IF Object_id('tempdb..#temp2') IS NOT NULL 
  DROP TABLE #temp2 

CREATE TABLE #temp 
  ( 
     id                INT IDENTITY(1, 1), 
     string_to_replace NVARCHAR(5) 
  ) 

INSERT INTO #temp 
            (string_to_replace) 
VALUES      (' '), 
            ('/'), 
            ('_') 

CREATE TABLE #temp2 
  ( 
     string_name NVARCHAR(5) 
  ) 

INSERT INTO #temp2 
            (string_name) 
VALUES      ('A BC'), 
            ('D/EF'), 
            ('G_HI'), 
            ('A BD_') 

DECLARE @col_list          VARCHAR(8000)= '', 
        @sql               VARCHAR(max), 
        @cntr              INT, 
        @inr               INT =1, 
        @STRING_TO_REPLACE NVARCHAR(5) 

SELECT @cntr = Max(id) 
FROM   #temp 

SET @sql = 'select ' 

WHILE @inr < = @cntr 
  BEGIN 
      SELECT @STRING_TO_REPLACE = string_to_replace 
      FROM   #temp 
      WHERE  id = @inr 

      IF @inr = 1 
        SET @col_list = 'replace (STRING_NAME,''' 
                        + @STRING_TO_REPLACE + ''','''')' 
      ELSE 
        SET @col_list = 'replace (' + @col_list + ',''' 
                        + @STRING_TO_REPLACE + ''','''')' 

      SET @inr+=1 
  END 

SET @sql += ' from #temp2' 
--print @col_list 
SET @sql = 'select ' + @col_list + ' as Result from #temp2' 

--print @sql 
EXEC (@sql) 

结果:

Result
------
ABC
DEF
GHI
ABD

【讨论】:

  • 我认为具有多个分隔符的值的问题。
  • @AlexK。在样本数据中,每一行都有一个 string_to_replace。在 OP 的查询问题中,replace 函数中因为sub-query
  • Prdp - 您的代码就是代码,但是如果我需要替换一个字符串中的多个字符怎么办。例如,如果字符串是 A/B_C,那么我该如何制作 ABC?
  • @kami - 刚刚添加了一条说明它不起作用:D。等待将编辑我的答案
  • @kami - 更新了我的答案以替换一个字符串中的多个字符
【解决方案2】:

可以通过递归 CTE 实现多重替换,如下例所示:

IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2

CREATE TABLE #temp
(
    STRING_TO_REPLACE NVARCHAR(10)
    ,Pattern NVARCHAR(10)
)
INSERT INTO #temp (STRING_TO_REPLACE, Pattern)
VALUES            (' ', '% %')
                 ,('/', '%/%')
                 ,('_', '%[_]%') ;

CREATE TABLE #temp2
(
    STRING_NAME NVARCHAR(10)
);

INSERT INTO #temp2 (STRING_NAME)
VALUES            ('A BC')
                 ,('D/EF_F E')
                 ,('G_HI')
                 ,('XYZ');

WITH CTE_Replace AS
(
    SELECT   STRING_NAME AS OriginalString
            ,CAST(STRING_NAME AS NVARCHAR(10)) AS ReplacedString
            ,CAST('' AS NVARCHAR(10)) AS StringToReplace
            ,1 AS ReplaceCount
    FROM    #temp2 ancor
    UNION ALL
    SELECT   CTE_Replace.OriginalString
            ,CAST(REPLACE(CTE_Replace.ReplacedString, rep.STRING_TO_REPLACE, '') AS NVARCHAR(10)) AS ReplacedString 
            ,CAST(rep.STRING_TO_REPLACE AS NVARCHAR(10)) AS StringToReplace
            ,CTE_Replace.ReplaceCount + 1 AS ReplaceCount
    FROM    #temp rep
    INNER JOIN CTE_Replace ON CTE_Replace.ReplacedString LIKE rep.Pattern
)
,CTE_FinalReplacedString AS
(
    SELECT  OriginalString
            ,ReplacedString
            ,ReplaceCount
            ,ROW_NUMBER() OVER (PARTITION BY OriginalString ORDER BY ReplaceCount DESC) AS [Rank]
    FROM    CTE_Replace
)
SELECT *
FROM    CTE_FinalReplacedString
WHERE   [Rank] = 1

请注意,#temp 表已更新为包含一个名为 Pattern 的额外列,该列包含用于查找必须替换的特定字符串的搜索模式。这样做也是为了简化递归 CTE 中的连接语句。另请注意,为了找到_ 字符,必须将搜索模式更新为'%[_]%'。这是因为 SQL Server 会将 _ 字符解释为通配符,而不是我们试图查找的特定字符。

【讨论】:

  • 我一开始写了这样的东西,但后来我记得_ 表示任何单个字符。 对于 LIKE 并放在那里 CHARINDEX,它工作得很好。不需要 2 个 CTE。来自我的 +1。
  • @gofr1 我没有想到 CHARINDEX - 绝对是更好的选择 :)
【解决方案3】:

在这里替换表格可能更容易

update t2 
set t2.string_name = Replace(t2.string_name, t1.string_to_replace, '')
from       #temp2 t2 
cross join #temp1 t1 

【讨论】:

  • 没有错误,如果运行该更新然后SELECT * FROM #temp2 它只会更改#temp 的第一个字符(删除空格)。
  • 有兴趣的我去看看。如果我无法修复,我将删除。
【解决方案4】:

解决此问题的一种简单方法是下载PatExclude8K 的副本,这是专为此类任务设计的 T-SQL 函数。这里有几个例子:

-- remove all non-aplphabetical characters
SELECT NewString FROM #temp2 CROSS APPLY dbo.PatExclude8K(STRING_NAME,'[^A-Z]');
-- remove all spaces, forward slashes and underscores
SELECT NewString FROM #temp2 CROSS APPLY dbo.PatExclude8K(STRING_NAME,'[ /_]');

两个查询都产生这个结果集:

NewString
------------
ABC
DEF
GHI

【讨论】:

    【解决方案5】:

    我在 stackoverflow 上找到了下面的代码,这似乎更接近我想要实现的目标,但我正在苦苦思考如何在我的代码中使用它

        declare @String varchar(max) = '(N_100-(6858)*(6858)*N_100/0_2)%N_35'
    
            --table containing values to be replaced
            create table #Replace 
            (
                StringToReplace varchar(100) not null primary key clustered
                ,ReplacementString varchar(100) not null    
            )
    
            insert into #Replace (StringToReplace, ReplacementString)
            values ('+', '~')
                ,('-', '~')
                ,('*', '~')
                ,('/', '~')
                ,('%', '~')
                ,('(', '~')
                ,(')', '~')
    
            select @String = replace(@String, StringToReplace, ReplacementString)
            from #Replace a
    
            select @String
    
            drop table #Replace
    

    编辑 by gofr1

    CREATE FUNCTION replacement
    (
        @String nvarchar(max)
    )
    RETURNS nvarchar(max) 
    AS
    BEGIN
    
        DECLARE @Replace TABLE (
            StringToReplace nvarchar(100),
            ReplacementString nvarchar(100)
        )
    
        INSERT INTO @Replace (StringToReplace, ReplacementString)
        VALUES ('+', '~')
            ,('-', '~')
            ,('*', '~')
            ,('/', '~')
            ,('%', '~')
            ,('(', '~')
            ,(')', '~')
    
        SELECT @String = replace(@String, StringToReplace, ReplacementString)
        FROM @Replace
    
        RETURN @String
    
    END
    GO
    

    然后调用它:

    SELECT dbo.replacement ('A B-C/d')
    

    输出:

    A B~C~d
    

    【讨论】:

    • 这个方案只对一个字符串有效,所以需要放入函数中。
    • 我在你的问题中添加一个例子
    • @gofr1 - 这很酷,但唯一的问题是我获得了对服务器的只读访问权限并且不允许创建函数。有什么东西可以创建 TEMPORARY FUNCTIONS 吗?
    • 没办法。如果您是只读的,请尝试我的解决方案。它的作用几乎相同,但在 CTE 中。
    • 好的,没问题。谢谢你的回答
    【解决方案6】:

    递归 CTE 的另一种方式(以下是全批次):

    --Create a sample table, you should use YourTable
    CREATE TABLE #temp2 (
        STRING_NAME NVARCHAR(max)
    )
    
    INSERT INTO #temp2 (STRING_NAME)
    VALUES ('A BC'),('D/EF'),('G_HI'),('J_K/L_'),('MNO')
    --I add some more objects here
    

    主要查询:

    ;WITH replacement AS (
    SELECT *
    FROM (VALUES (' '),('/'),('_')
    ) as t(STRING_TO_REPLACE)
    ), cte AS (
    SELECT  STRING_NAME,
            STRING_NAME as OriginalString,
            ROW_NUMBER() OVER (ORDER BY STRING_NAME) as rn,
            1 as [Level]
    FROM #temp2 t2
    UNION ALL
    SELECT  REPLACE(c.STRING_NAME,t.STRING_TO_REPLACE,'~'),
            c.OriginalString,
            c.rn,
            [Level]+1
    FROM cte c
    INNER JOIN replacement t
        ON CHARINDEX(t.STRING_TO_REPLACE,c.STRING_NAME,0) > 0
    )
    
    SELECT TOP 1 WITH TIES  OriginalString,
                            STRING_NAME
    FROM cte 
    ORDER BY ROW_NUMBER() OVER (PARTITION BY rn ORDER BY [Level] DESC)
    OPTION (MAXRECURSION 0)
    

    输出:

    OriginalString  STRING_NAME
    A BC            A~BC
    D/EF            D~EF
    J_K/L_          J~K~L~
    G_HI            G~HI
    MNO             MNO
    

    【讨论】:

    • 据我了解,您需要更新一些表格列。为此,您需要替换一些字符。我猜对了吗?
    • 嗯,它并不是真正的更新。基本上我已经有一个表,其中一列有引用字符串。在这些字符串中,各种用户都出现了各种字符,例如 ' ' , '_' , '/' , '\' , '-' 等。在我的查询中,我需要在此列中显示另一列,这将是原始列的修改视图。因此,在修改后的列中,我应该使用上面提到的任何这些字符来清理引用字符串。
    • 我希望不要去嵌套的 REPLACE 路线,因为那时我需要做很多 REPLACE 输入:(
    • 有什么方法可以在我的代码中使用这个代码吗? stackoverflow.com/a/39870142/1696286
    • 我又修改了一次查询。我认为没有理由不尝试。最好的问候!
    猜你喜欢
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    相关资源
    最近更新 更多