我正在寻找一种在字符串中实现替换的方法。
我们说我的文字是(([343]+([509])*[1584]))/25
。我希望使用值plus 2000
更新此文本中的每个数字(括在括号中)。
因此,此(([343]+([509])*[1584]))/25
应该成为(([2343]+([2509])*[3584]))/25
我应该通过MS SQL来做到这一点。谁能让我开始?
答案 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)