【问题标题】:SQL - Remove everything between [ and ]SQL - 删除 [ 和 ] 之间的所有内容
【发布时间】:2026-02-19 23:25:02
【问题描述】:

我正在尝试为这个规范找到一个解决方案,我有一个数据列表,存储在一个表中,同一列有 2 个视图,基本上我们有一个仅对职员可见,在特定页面中,另一个是面向最终用户、网站用户等的。

这些字段可以包含任何形式的文本,但是,当职员输入“foo [bar]”时,

我正在向职员显示“foo [bar]”,但我只需要为最终用户保留“foo”。

另一个有趣的要求是,对于长文本,职员有时可能只打开方括号而不关闭,在这种情况下它必须运行到字符串的末尾。

我尝试过并且一直在做的事情如下:

SELECT 
    [Name] = 
        CASE
        WHEN [value] LIKE '%\[%' ESCAPE '\'
            THEN SUBSTRING( [value], PATINDEX( '%[%', [value] ), LEN( [value] ) )
            ELSE [value]
        END
FROM #MyTable

非常感谢任何帮助。

【问题讨论】:

  • 这听起来像是你的应用层的东西,它希望支持 Regex,而不是 SQL Server 后端,它支持 Regex 并且不擅长字符串操作。
  • 如果你有像'foo [bar] yes [no]' 这样的值,我上面的说法就更正确了,结果需要'foo yes
  • 这不在规范中,但我不会排除,但是,我希望有一个 SQL 解决方案,因为它在存储过程中,猜测在这种情况下,我会必须添加一个层来捕获返回的数据并进行相应的处理

标签: sql tsql substring


【解决方案1】:

一个迟到的解决方案。也长。但它适用于我扔给它的所有东西......

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

【讨论】:

  • 将所有内容复制到 sql 中进行测试,由于某种原因,我得到“Msg 102,Level 15,State 1,Line 61 Incorrect syntax near '('.” for the following line:“select s. id, s.input, string_agg(s.Sub, '') 组内(按s.Starts排序) as Ret"
  • 您没有指定您的 SQL Server 版本。这就是为什么我包含了 2 个版本(第二个版本被评论)。 string_agg() 函数仅从 SQL Server 2017 开始可用。This fiddle 在 SQL Server 2016 上运行,第二个版本未注释并包括空间修剪。
最近更新 更多