SQL替换文本

时间:2016-06-06 13:36:35

标签: sql sql-server regex

我正在寻找一种在字符串中实现替换的方法。

我们说我的文字是(([343]+([509])*[1584]))/25。我希望使用值plus 2000更新此文本中的每个数字(括在括号中)。

因此,此(([343]+([509])*[1584]))/25应该成为(([2343]+([2509])*[3584]))/25

我应该通过MS SQL来做到这一点。谁能让我开始?

4 个答案:

答案 0 :(得分:3)

DECLARE @val VARCHAR(100) = '(([343]+([509])*[1584]))/25', 
        @xml xml

SELECT @xml = CAST('<a>'+REPLACE(REPLACE(@val,']','</a><a>'),'[','</a><a>')+'</a>' as xml)

SELECT @val = CAST ((
SELECT CASE WHEN try_cast(t.v.value('.','nvarchar(max)') as int) is not null 
            THEN QUOTENAME(try_cast(t.v.value('.','nvarchar(max)') as int)+2000) 
            ELSE t.v.value('.','nvarchar(max)') END
FROM @xml.nodes('/a') as t(v)
FOR XML PATH('')) as nvarchar(max))

SELECT @val

输出:

(([2343]+([2509])*[3584]))/25

答案 1 :(得分:2)

DECLARE @val VARCHAR(100) = '(([343]+([509])*[1584]))/25'

SELECT STUFF((
SELECT '[' + ISNULL(REPLACE(val, token, token + 2000), val)
FROM (
    SELECT token = PARSENAME(REPLACE(t.val, ']', '.'), 2), *
    FROM (
        SELECT val = t.c.value('(./text())[1]', 'VARCHAR(100)')
        FROM ( 
            SELECT x = CONVERT(XML, '<i>' + REPLACE(@val, '[', '</i><i>') + '</i>').query('.')
        ) a
        CROSS APPLY x.nodes('i') t(c)
    ) t
) t
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')

答案 2 :(得分:1)

我会使用以下方法之一:

1)XQuery

DECLARE @s VARCHAR(100) = '(([2343]+([2509])*[3584]))/25'

SELECT  CONVERT(XML, '<r><i>' + REPLACE(REPLACE(@s, '[', '</i><n>'), ']', '</n><i>') + '</i></r>').query('
for $e in r/*
return 
    <r><i>{$e[local-name(.) eq "i"]/text()}</i>
       <n>{number(($e[local-name(.) eq "n"]/text())[1]) + 2000}</n></r>
').value('.', 'VARCHAR(100)')

2)非XQuery

i)它转换源字符串 (([2343]+([2509])*[3584]))/25 因此:[]之间的值分配到XML元素<n>number<n>,所有其他子字符串分配给XML元素<i>...</i> <r><i>((</i><n>2343</n><i>+(</i><n>2509</n><i>)*</i><n>3584</n><i>))/25</i></r>。这样我们就可以确定只有n个元素具有源[number]子字符串。根元素是<r>。注意:[]之间的值假定为INT egers。如果不是,我将使用TRY_PARSE而不是CONVERT和更多的检查。

ii)它解析<r><n><i>的所有子节点(请参阅node('r/*')方法)。如果当前节点为<n>local-name(.)n),则会将内部文本(text())转换为INT,并且会增加2000。

iii)它将所有值转换回XML(FOR XML),然后从XML转换为字符串/ VARCHAR.value(..., 'VARCHAR(100)')):(([4343]+([4509])*[5584]))/25

DECLARE @s VARCHAR(100) = '(([2343]+([2509])*[3584]))/25'

DECLARE @x XML = CONVERT(XML, '<r><i>' + REPLACE(REPLACE(@s, '[', '</i><n>'), ']', '</n><i>') + '</i></r>')
SELECT (
SELECT y.n, y.i
FROM (
    SELECT  IIF(x.XmlCol.value('local-name(.)', 'VARCHAR(128)') = 'n', QUOTENAME(CONVERT(INT, x.XmlCol.value('.', 'VARCHAR(128)')) + 2000), NULL) AS n,
            IIF(x.XmlCol.value('local-name(.)', 'VARCHAR(128)') = 'i', x.XmlCol.value('.', 'VARCHAR(128)'), NULL) AS i,
            ROW_NUMBER() OVER(ORDER BY x.XmlCol) AS rn
    FROM    @x.nodes('r/*') x(XmlCol)) y
ORDER BY y.rn
FOR XML PATH, ROOT('r'), TYPE).value('.', 'VARCHAR(100)')

答案 3 :(得分:0)

使用用户定义的标量函数的替代方法:

CREATE FUNCTION [dbo].[fnReplaceNumbers]
(
    @string varchar(max),
    @valueToAdd int
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    declare @i int
    declare @len int
    declare @char varchar(1);
    declare @inToken bit;
    declare @num varchar(32);
    declare @result varchar(max);

    set @i = 1
    set @len = LEN(@string);
    set @inToken = 0;
    set @num = '';
    set @result = '';

    declare @table table(x varchar(128));


    WHILE @i <= @len
    BEGIN
        set @char = SUBSTRING(@string, @i, 1);

        if (@char = ']') set @inToken = 0;

        if (@inToken = 1) begin
            set @num = @num + @char;
        end else if (@inToken = 0) begin
            if (len(@num) > 0) begin
                set @result = @result + cast((cast(@num as int) + @valueToAdd) as varchar(max));
                set @num = '';
            end;
            set @result = @result + @char;
        end;

        if (@char = '[') set @inToken = 1;


        set @i = @i + 1;
    END

    return @result;
END

..然后只需按以下方式调用:

select dbo.fnReplaceNumbers('(([343]+([509])*[1584]))/25', 2000)