一个迟到的解决方案。也长。但它适用于我扔给它的所有东西......
Fiddle 查看实际解决方案。
样本数据
create table samples
(
id int,
input nvarchar(30)
);
insert into samples (id, input) values
(1, 'foo [bar]'),
(2, 'foo [bar abc'),
(3, 'foo [bar] more'),
(4, 'foo1 ! foo2 [bar] foo3'),
(5, 'foo1 foo2'),
(6, 'foo[bar]foo[bar]foo'),
(7, '[bar] foo');
解决方案
公用表表达式万岁。 cmets 中描述了每个 CTE 的用途。
-- split on '['
with cte1 as (
select s.id, s.input, 1 as Starts, charindex('[', s.input) as Pos, 1 as CteId
from samples s
union all
select cte1.id, cte1.input, cte1.Pos + 1, charindex('[', cte1.input, cte1.Pos + 1), 1
from cte1
where cte1.Pos > 0
),
-- split on ']'
cte2 as (
select s.id, s.input, 1 as Starts, charindex(']', s.input) as Pos, 2 as CteId
from samples s
union all
select cte2.id, cte2.input, cte2.Pos + 1, charindex(']', cte2.input, cte2.Pos + 1), 2
from cte2
where cte2.Pos > 0
),
-- combine both split result sets
cte as (
select * from cte1
union all
select * from cte2
),
-- construct ranges by fetching end position of previous row
range as (
select cte.id,
cte.input,
cte.Starts,
lag(cte.Pos) over(partition by cte.Id order by cte.Starts, cte.CteId) as Ends,
cte.CteId
from cte
),
-- select substrings from Starts to just for Ends
subs as (
select r.id,
r.input,
r.Starts,
--r.Ends-1 as Ends,
substring(r.input, r.Starts, case when r.Ends > 0 then r.Ends - r.Starts else len(r.input) end) as Sub
from range r
where r.CteId = 2
)
-- concatenate and remove duplicates
-- VERSION 1 (SQL Server 2017 and later)
select s.id, s.input, string_agg(s.Sub, '') within group(order by s.Starts) as Ret
from subs s
group by s.id, s.input
order by s.id;
/*
-- VERSION 2 (before SQL Server 2017)
select distinct s.id, s.input, x.Ret
from subs s
cross apply ( select s2.Sub + ''
from subs s2
where s2.id = s.id
for xml path('') ) x(Ret)
order by s.id;
*/
结果
包含一些可以用trim() 处理的尾随/前导空格(如this)。
id |input |Ret
---|----------------------|-----------------
1 |foo [bar] |foo
2 |foo [bar abc |foo
3 |foo [bar] more |foo more
4 |foo1 ! foo2 [bar] foo3|foo1 ! foo2 foo3
5 |foo1 foo2 |foo1 foo2
6 |foo[bar]foo[bar]foo |foofoofoo
7 |[bar] foo | foo