在t-sql的替换函数中使用正则表达式的方法?

时间:2015-06-24 18:18:42

标签: sql-server regex tsql replace

我有以下测试字符串显示在我的表格列中。

<B99_9>TEST</B99_9><LastDay>TEST</LastDay>

我想删除特定标记之间的值。例如:

REPLACE( <B99_9>TEST</B99_9><LastDay>TEST</LastDay>, <B99_9>TEST</B99_9>, <B99_9></B99_9> )

这应该给出以下输出:

<B99_9></B99_9><LastDay>TEST</LastDay>

这种做了我需要做的事情,但是对于我不知道标签之间的价值是什么的情况,我能做些什么呢?有没有办法在Replace函数的搜索中实现一些正则表达式,这样我可以搜索开始/结束标记,而不管它们之间的值是什么?

我知道这可能适用于CLR函数,但我真的很好奇,看看我是否可以通过使用t-sql来完成这项工作。提前谢谢!

2 个答案:

答案 0 :(得分:2)

虽然很容易部署执行此操作的CLR代码,但SQL Server中没有开箱即用的正则表达式支持。

但是你可以在这种情况下使用标准的字符串函数

DECLARE @T TABLE
  (
     X VARCHAR(MAX)
  );

INSERT INTO @T
VALUES      ('<B99_9>TEST</B99_9><LastDay>TEST</LastDay>'),
            ('Some prefix <B99_9>TEST</B99_9><LastDay>TEST</LastDay>')

UPDATE [@T]
SET    X = CASE
             WHEN 0 IN ( StartIndex, EndIndex )
                   OR EndIndex <= StartIndex THEN X
             ELSE STUFF(X, AdjStartIndex, EndIndex - AdjStartIndex, '')
           END
FROM   @T [@T]
       CROSS APPLY (VALUES ('<B99_9>','</B99_9>')) V(StartString, EndString)
       CROSS APPLY (VALUES (PATINDEX('%' + StartString + '%', X),PATINDEX('%' + EndString + '%', X))) V2(StartIndex, EndIndex)
       CROSS APPLY (VALUES (StartIndex + LEN(StartString))) V3(AdjStartIndex)

SELECT *
FROM   @T 

但是,由于字符串是XML,将其存储在XML列中将允许您使用内置的,语义感知的XML方法而不是字符串解析来更新它。

DECLARE @T TABLE(X XML);

INSERT INTO @T 
VALUES ('<B99_9>TEST</B99_9><LastDay>TEST</LastDay>'),  
       ('Some prefix <B99_9>TEST</B99_9><LastDay>TEST</LastDay>')


UPDATE @T
SET  X.modify('
  replace value of (/B99_9/text())[1]
  with "" ')  

SELECT *
FROM @T

答案 1 :(得分:1)

如果由于安全原因你不习惯或者不能使用CLR,那么在标准t-sql中使用CTE有一种简单的方法。

这是一个完整的示例包含演示结构。你可以在整张桌子上运行它。

CREATE TABLE #dummyData(id int identity(1,1), teststring nvarchar(255))

INSERT INTO #dummyData(teststring)
VALUES(N'<B99_9>TEST</B99_9><LastDay>TEST</LastDay>, <B99_9>TEST</B99_9>, <B99_9></B99_9>')

DECLARE @starttag nvarchar(10) = N'<B99_9>', @endtag nvarchar(10) = N'</B99_9>'

;WITH cte AS(
    SELECT id, STUFF(
                teststring,
                PATINDEX(N'%'+@starttag+N'[a-z0-9]%',teststring)+LEN(@starttag),
                (PATINDEX(N'%[a-z0-9]'+@endtag+N'%',teststring)+1)-(PATINDEX(N'%'+@starttag+N'[a-z0-9]%',teststring)+LEN(@starttag)),
                N''
            ) as teststring, 1 as iteration
    FROM #dummyData
    -- iterate until everything is replaced
    UNION ALL
    SELECT id, STUFF(
                teststring,
                PATINDEX(N'%'+@starttag+N'[a-z0-9]%',teststring)+LEN(@starttag),
                (PATINDEX(N'%[a-z0-9]'+@endtag+N'%',teststring)+1)-(PATINDEX(N'%'+@starttag+N'[a-z0-9]%',teststring)+LEN(@starttag)),
                N''
            ) as teststring, iteration+1 as iteration
    FROM cte
    WHERE PATINDEX(N'%'+@starttag+N'[a-z0-9]%',teststring) > 0
)
SELECT c.id, c.teststring 
FROM cte as c
-- Join to get only the latest iteration
INNER JOIN (
            SELECT id, MAX(iteration) as maxIteration
            FROM cte 
            GROUP BY id
        ) as onlyMax
    ON c.id = onlyMax.id
    AND c.iteration = onlyMax.maxIteration

-- Cleanup
DROP TABLE #dummyData

如果要在更新中使用CTE的结果。您只需使用以下代码替换CTE - 定义后的部分:

UPDATE dd
SET teststring = c.teststring
FROM #dummyData as dd -- rejoin the base table for later update usage
INNER JOIN cte as c
        ON dd.id = c.id
-- Join to get only the latest iteration
INNER JOIN (
            SELECT id, MAX(iteration) as maxIteration
            FROM cte 
            GROUP BY id
        ) as onlyMax
    ON c.id = onlyMax.id
    AND c.iteration = onlyMax.maxIteration

如果您不想在完整的表集上运行它,可以为单个变量运行以下代码:

DECLARE @string nvarchar(max) = N'<B99_9>TEST</B99_9><LastDay>TEST</LastDay>, <B99_9>TEST</B99_9>, <B99_9></B99_9>'
DECLARE @starttag nvarchar(10) = N'<B99_9>', @endtag nvarchar(10) = N'</B99_9>'

WHILE PATINDEX(N'%'+@starttag+N'[a-z0-9]%',@string) > 0 BEGIN
    SELECT @string = STUFF(
                    @string,
                    PATINDEX(N'%'+@starttag+N'[a-z0-9]%',@string)+LEN(@starttag),
                    (PATINDEX(N'%[a-z0-9]'+@endtag+N'%',@string)+1)-(PATINDEX(N'%'+@starttag+N'[a-z0-9]%',@string)+LEN(@starttag)),
                    N''
                )
END

SELECT @string