我一直在尝试设置SQL函数来使用"标记"来构建描述。例如,我想从描述开始:
"This is [length] ft. long and [height] ft. high"
使用相关表格中的数据修改说明,最后得到:
"This is 75 ft. long and 20 ft. high"
如果我们有一定数量的标签,我可以使用REPLACE
函数轻松完成此操作,但我希望这些标记是用户定义的,并且每个描述可能包含也可能没有特定标记。除了使用游标为每个可用标记一次通过字符串之外,还有更好的方法吗? SQL是否具有任何内置功能来进行多次替换?类似的东西:
Replace(description,(select tag, replacement from tags))
答案 0 :(得分:0)
尝试以下查询:
SELECT REPLACE(DESCRIPTION,'[length]',( SELECT replacement FROM tags WHERE tag
= '[length]') )
答案 1 :(得分:0)
我实际上建议在应用程序代码中执行此操作。但是,你可以使用递归CTE来做到这一点:
.gitignore
答案 2 :(得分:0)
我同意Gordon的观点,最好在您的应用程序代码中处理。
如果由于某种原因该选项不可用,并且如果您不想根据Gordon的回答使用递归,则可以使用计数表方法来交换您的值。
您需要测试为每个值执行的for xml
的性能,但是......
假设您有一个Tag
替换值表:
create table TagReplacementTable(Tag nvarchar(50), Replacement nvarchar(50));
insert into TagReplacementTable values('[test]',999)
,('[length]',75)
,('[height]',20)
,('[other length]',40)
,('[other height]',50);
您可以创建一个可以使用Descriptions
的内联表格功能,并使用TagReplacementTable
作为参考来删除必要的部分:
create function dbo.Tag_Replace(@str nvarchar(4000)
,@tagstart nvarchar(1)
,@tagend nvarchar(1)
)
returns table
as
return
(
with n(n) as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))
-- Select the same number of rows as characters in @str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length.
,t(t) as (select top (select len(@str) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that starts or ends a part of the description.
-- This will be the first character (t='f'), the start of any tag (t='s') and the end of any tag (t='e').
,s(s,t) as (select 1, 'f'
union all select t+1, 's' from t where substring(@str,t,1) = @tagstart
union all select t+1, 'e' from t where substring(@str,t,1) = @tagend
)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
-- Using the t value we can determine which CHARINDEX to look for.
,l(t,s,l) as (select t,s,isnull(nullif(charindex(case t when 'f' then @tagstart when 's' then @tagend when 'e' then @tagstart end,@str,s),0)-s,4000) from s)
-- Each element of the string is returned in an ordered list along with its t value.
-- Where this t value is 's' this means the value is a tag, so append the start and end identifiers and join to the TagReplacementTable.
-- Where no replacement is found, simply return the part of the Description.
-- Finally, concatenate into one string value.
select (select isnull(r.Replacement,k.Item)
from(select row_number() over(order by s) as ItemNumber
,case when l.t = 's' then '[' else '' end
+ substring(@str,s,l)
+ case when l.t = 's' then ']' else '' end as Item
,t
from l
) k
left join TagReplacementTable r
on(k.Item = r.Tag)
order by k.ItemNumber
for xml path('')
) as NewString
);
然后outer apply
到函数的结果,对所有Description
值进行替换:
declare @t table (Descr nvarchar(100));
insert into @t values('This is [length] ft. long and [height] ft. high'),('[test] This is [other length] ft. long and [other height] ft. high');
select *
from @t t
outer apply dbo.Tag_Replace(t.Descr,'[',']') r;
输出:
+--------------------------------------------------------------------+-----------------------------------------+
| Descr | NewString |
+--------------------------------------------------------------------+-----------------------------------------+
| This is [length] ft. long and [height] ft. high | This is 75 ft. long and 20 ft. high |
| [test] This is [other length] ft. long and [other height] ft. high | 999 This is 40 ft. long and 50 ft. high |
+--------------------------------------------------------------------+-----------------------------------------+
答案 3 :(得分:0)
我不会遍历单个字符串,而是在整个字符串列上运行更新。我不确定这是不是你的意图,但这会比一次一个字符串快得多。
测试数据:
Create TABLE #strs ( mystr VARCHAR(MAX) )
Create TABLE #rpls (i INT IDENTITY(1,1) NOT NULL, src VARCHAR(MAX) , Trg VARCHAR(MAX) )
INSERT INTO #strs
( mystr )
SELECT 'hello ##color## world'
UNION ALL SELECT 'see jack ##verboftheday##! ##verboftheday## Jack, ##verboftheday##!'
UNION ALL SELECT 'on ##Date##, the ##color## StockMarket was ##MarketDirection##!'
INSERT INTO #rpls ( src ,Trg )
SELECT '##Color##', 'Blue'
UNION SELECT ALL '##verboftheday##' , 'run'
UNION SELECT ALL '##Date##' , CONVERT(VARCHAR(MAX), GETDATE(), 9)
UNION SELECT ALL '##MarketDirection##' , 'UP'
然后像这样的循环:
DECLARE @i INTEGER = 0
DECLARE @count INTEGER
SELECT @count = COUNT(*)
FROM #rpls R
WHILE @i < @count
BEGIN
SELECT @i += 1
UPDATE #strs
SET mystr = REPLACE(mystr, ( SELECT R.src
FROM #rpls R
WHERE i = @i ), ( SELECT R.Trg
FROM #rpls R
WHERE i = @i ))
END
SELECT *
FROM #strs S
产生以下
hello Blue world
see jack run! run Jack, run!
on May 19 2017 9:48:02:390AM, the Blue StockMarket was UP!
答案 4 :(得分:0)
我发现有人希望使用一系列选项来做类似here的事情:
SELECT @target = REPLACE(@target, invalidChar, '-')
FROM (VALUES ('~'),(''''),('!'),('@'),('#')) AS T(invalidChar)
我可以这样修改它:
declare @target as varchar(max) = 'This is [length] ft. long and [height] ft. high'
select @target = REPLACE(@target,'[' + tag + ']',replacement)
from tags
然后对select语句中返回的每条记录运行一次替换。
(我最初在我的问题中添加了这个,但听起来更好的协议是将其添加为答案。)