替换子字符串的交替出现
Replace the alternate occurances of a substring
我的输入字符串如下:
A or B OR C or D OR E or F
A OR B OR C OR D OR E OR F
预期输出:'A or B' OR 'C or D' OR 'E or F'
outputString = '''' + REPLACE(@inputValue COLLATE Latin1_General_CS_AS, ' OR ' COLLATE Latin1_General_CS_AS, ''' OR ''') + ''''
我尝试使用 SQL 替换函数,上面的语句对第一个字符串正常工作,我得到了所需的输出,但是对于第二个字符串,因为我们的所有 OR 都是大写的,所以它失败了并且 returns'A' OR 'B' OR 'C' OR 'D' OR 'E' OR 'F'
我正在使用 SSMS 15.0。
我该如何解决这个问题?任何帮助将不胜感激。
由于使用了 while,此解决方案在服务器负载方面成本很高。
declare @input varchar(100)
set @input = 'A or B or C or D or E or F or G or'
declare @inc int = 1, @end int = 1
,@final varchar(100) = '', @part varchar(100)
,@nextposition varchar(100), @or varchar(10)= ''
,@last varchar(10), @ifendsOR varchar(10)
select @nextposition = case when @input like '%or' then substring(@input,1,len(@input)-2) else @input end
select @ifendsOR = case when @input like '%or' then ' or' else '' end
select @last = ltrim(rtrim(right(@nextposition,2)))
while @end <> 0
begin
select @part = substring(@nextposition,1,charindex('or',@nextposition)-2)
select @nextposition = replace(@nextposition,concat(@part,' or '),'')
set @end = charindex('or',@nextposition)
select @or = case when @inc%2 = 0 then ' OR ' else ' or ' end
set @inc = @inc+1
set @final = concat(@final,@part,@or)
end
select @ifendsOR = case when @inc%2 = 0 then upper(@ifendsOR) else @ifendsOR end
select concat(@final,@last,@ifendsOR)
这是一个使用 UDF 的解决方案。
该函数将模式上的字符串拆分为结果集。
(类似于STRING_SPLIT函数,但有一个模式)
然后使用 FOR XML
技巧从拆分的部分构造字符串,并添加引号。
DECLARE @vchNewValue VARCHAR(100), @result VARCHAR(100);
SET @vchNewValue = 'A OR B or C OR D or E OR F';
SET @result = LTRIM(RTRIM((
SELECT
CASE WHEN match = 1
THEN ' '+quotename(ltrim(rtrim(replace(value,' OR ',' or ') )),'''')+' '
ELSE UPPER(value)
END
FROM dbo.fnPattern_Split(' '+@vchNewValue+' ', ' % OR % ') AS spl
ORDER BY ordinal
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)')
));
SELECT @result AS result;
result
'A or B' OR 'C or D' OR 'E or F'
测试db<>fiddlehere
UDF
使用 PATINDEX 查找字符串中给定模式的每个下一个开始位置。
然后找到模式仍然有效的最近结束位置。
所以它有点像在正则表达式中的惰性搜索。
然后使用这些位置将零件插入返回的 table。
CREATE FUNCTION dbo.fnPattern_Split
(
@str VARCHAR(MAX),
@pattern VARCHAR(100)
)
RETURNS @tbl TABLE (
ordinal INT,
value VARCHAR(MAX),
match BIT
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @value NVARCHAR(MAX)
, @splitvalue NVARCHAR(MAX)
, @startpos INT = 0
, @endpos INT = 0
, @ordinal INT = 0
, @foundend BIT = 0
, @patminlen INT = ISNULL(NULLIF(LEN(REPLACE(@pattern,'%','')),0),1);
WHILE (LEN(@str) > 0)
BEGIN
SET @startpos = ISNULL(NULLIF(PATINDEX('%'+@pattern+'%', @str),0), LEN(@str)+1);
IF @startpos < LEN(@str)
BEGIN
SET @foundend = 0;
SET @endpos = @startpos+@patminlen-1;
WHILE @endpos < LEN(@str) AND @foundend = 0
BEGIN
IF SUBSTRING(@str, @startpos, 1+@endpos-@startpos) LIKE @pattern
SET @foundend = 1;
ELSE
SET @endpos += 1;
END
END
ELSE SET @endpos = LEN(@str);
IF @startpos > 1
BEGIN
SET @ordinal += 1;
SET @value = LEFT(@str, @startpos-1);
INSERT INTO @tbl (ordinal, value, match)
VALUES (@ordinal, @value, 0);
END
IF @endpos >= @startpos
BEGIN
SET @ordinal += 1;
SET @splitvalue = SUBSTRING(@str, @startpos, 1+@endpos-@startpos);
INSERT INTO @tbl (ordinal, value, match)
VALUES (@ordinal, @splitvalue, 1);
END
SET @str = SUBSTRING(@str, @endpos+1, LEN(@str));
END;
RETURN;
END;
填充引号的递归解决方案。
递归 CTE 循环遍历字符串,同时找到 ' 或 ' 模式的起始位置。
因为 ' or ' 有 4 个字符,有一个开始位置意味着你也有结束位置。
STUFF
函数可以在字符串中的位置插入字符。
因此这些位置用于在需要的地方填充引号。
这是每个偶数出现 (lvl 的模数 2 为 0).
declare @input varchar(100)
, @result varchar(100);
set @input = 'A OR B or C OR D or E OR F';
set @result = @input;
with rcte as (
select 1 as lvl
, charindex(' or ', @input) as pos
, len(@input) as max_pos
union all
select lvl+1
, isnull(nullif(charindex(' or ', @input, pos+4), 0), max_pos)
, max_pos
from rcte
where pos < max_pos
)
select @result = stuff(stuff(@result,pos+4,0,''''),pos,0,'''')
from rcte
where lvl%2 = 0 and pos+4 < max_pos
order by lvl desc;
SET @result = ''''+@result+'''';
SET @result = REPLACE(REPLACE(@result,' OR ',' or '),''' or ''',''' OR ''');
select @result as result;
result
'A or B' OR 'C or D' OR 'E or F'
在 db<>fiddle here
上测试
我的输入字符串如下:
A or B OR C or D OR E or F
A OR B OR C OR D OR E OR F
预期输出:'A or B' OR 'C or D' OR 'E or F'
outputString = '''' + REPLACE(@inputValue COLLATE Latin1_General_CS_AS, ' OR ' COLLATE Latin1_General_CS_AS, ''' OR ''') + ''''
我尝试使用 SQL 替换函数,上面的语句对第一个字符串正常工作,我得到了所需的输出,但是对于第二个字符串,因为我们的所有 OR 都是大写的,所以它失败了并且 returns'A' OR 'B' OR 'C' OR 'D' OR 'E' OR 'F'
我正在使用 SSMS 15.0。
我该如何解决这个问题?任何帮助将不胜感激。
由于使用了 while,此解决方案在服务器负载方面成本很高。
declare @input varchar(100)
set @input = 'A or B or C or D or E or F or G or'
declare @inc int = 1, @end int = 1
,@final varchar(100) = '', @part varchar(100)
,@nextposition varchar(100), @or varchar(10)= ''
,@last varchar(10), @ifendsOR varchar(10)
select @nextposition = case when @input like '%or' then substring(@input,1,len(@input)-2) else @input end
select @ifendsOR = case when @input like '%or' then ' or' else '' end
select @last = ltrim(rtrim(right(@nextposition,2)))
while @end <> 0
begin
select @part = substring(@nextposition,1,charindex('or',@nextposition)-2)
select @nextposition = replace(@nextposition,concat(@part,' or '),'')
set @end = charindex('or',@nextposition)
select @or = case when @inc%2 = 0 then ' OR ' else ' or ' end
set @inc = @inc+1
set @final = concat(@final,@part,@or)
end
select @ifendsOR = case when @inc%2 = 0 then upper(@ifendsOR) else @ifendsOR end
select concat(@final,@last,@ifendsOR)
这是一个使用 UDF 的解决方案。
该函数将模式上的字符串拆分为结果集。
(类似于STRING_SPLIT函数,但有一个模式)
然后使用 FOR XML
技巧从拆分的部分构造字符串,并添加引号。
DECLARE @vchNewValue VARCHAR(100), @result VARCHAR(100); SET @vchNewValue = 'A OR B or C OR D or E OR F'; SET @result = LTRIM(RTRIM(( SELECT CASE WHEN match = 1 THEN ' '+quotename(ltrim(rtrim(replace(value,' OR ',' or ') )),'''')+' ' ELSE UPPER(value) END FROM dbo.fnPattern_Split(' '+@vchNewValue+' ', ' % OR % ') AS spl ORDER BY ordinal FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)') )); SELECT @result AS result;
result |
---|
'A or B' OR 'C or D' OR 'E or F' |
测试db<>fiddlehere
UDF
使用 PATINDEX 查找字符串中给定模式的每个下一个开始位置。
然后找到模式仍然有效的最近结束位置。
所以它有点像在正则表达式中的惰性搜索。
然后使用这些位置将零件插入返回的 table。
CREATE FUNCTION dbo.fnPattern_Split ( @str VARCHAR(MAX), @pattern VARCHAR(100) ) RETURNS @tbl TABLE ( ordinal INT, value VARCHAR(MAX), match BIT ) WITH SCHEMABINDING AS BEGIN DECLARE @value NVARCHAR(MAX) , @splitvalue NVARCHAR(MAX) , @startpos INT = 0 , @endpos INT = 0 , @ordinal INT = 0 , @foundend BIT = 0 , @patminlen INT = ISNULL(NULLIF(LEN(REPLACE(@pattern,'%','')),0),1); WHILE (LEN(@str) > 0) BEGIN SET @startpos = ISNULL(NULLIF(PATINDEX('%'+@pattern+'%', @str),0), LEN(@str)+1); IF @startpos < LEN(@str) BEGIN SET @foundend = 0; SET @endpos = @startpos+@patminlen-1; WHILE @endpos < LEN(@str) AND @foundend = 0 BEGIN IF SUBSTRING(@str, @startpos, 1+@endpos-@startpos) LIKE @pattern SET @foundend = 1; ELSE SET @endpos += 1; END END ELSE SET @endpos = LEN(@str); IF @startpos > 1 BEGIN SET @ordinal += 1; SET @value = LEFT(@str, @startpos-1); INSERT INTO @tbl (ordinal, value, match) VALUES (@ordinal, @value, 0); END IF @endpos >= @startpos BEGIN SET @ordinal += 1; SET @splitvalue = SUBSTRING(@str, @startpos, 1+@endpos-@startpos); INSERT INTO @tbl (ordinal, value, match) VALUES (@ordinal, @splitvalue, 1); END SET @str = SUBSTRING(@str, @endpos+1, LEN(@str)); END; RETURN; END;
填充引号的递归解决方案。
递归 CTE 循环遍历字符串,同时找到 ' 或 ' 模式的起始位置。
因为 ' or ' 有 4 个字符,有一个开始位置意味着你也有结束位置。
STUFF
函数可以在字符串中的位置插入字符。
因此这些位置用于在需要的地方填充引号。
这是每个偶数出现 (lvl 的模数 2 为 0).
declare @input varchar(100) , @result varchar(100); set @input = 'A OR B or C OR D or E OR F'; set @result = @input; with rcte as ( select 1 as lvl , charindex(' or ', @input) as pos , len(@input) as max_pos union all select lvl+1 , isnull(nullif(charindex(' or ', @input, pos+4), 0), max_pos) , max_pos from rcte where pos < max_pos ) select @result = stuff(stuff(@result,pos+4,0,''''),pos,0,'''') from rcte where lvl%2 = 0 and pos+4 < max_pos order by lvl desc; SET @result = ''''+@result+''''; SET @result = REPLACE(REPLACE(@result,' OR ',' or '),''' or ''',''' OR '''); select @result as result;
result |
---|
'A or B' OR 'C or D' OR 'E or F' |
在 db<>fiddle here
上测试