在SQL Server 2016+中,是否可以找到不在一组分隔符内的字符串的第一个匹配项?

t0ybt7op  于 2023-02-15  发布在  SQL Server
关注(0)|答案(3)|浏览(85)

我在SQL Server表中有一列,其中包含长度不等的字符串。我需要找到字符串, --第一次出现的位置,该字符串没有用单引号或方括号括起来。
例如,在下面的两个字符串中,我用粗体显示了我想获取其位置的部分。注意,在第一个字符串中,, --第一次单独出现(没有位于单引号或方括号分隔符之间)时位于位置13,而在第二个字符串中,它位于位置16。

'a, --'[, --]**, --**[, --]

[a, --b]aaaaaaa_ **, --**', --'

我还应该提到, --本身可以在字符串中多次出现。
下面是一个简单的查询,它显示了字符串和所需的输出。

SELECT 
    t.string, t.desired_pos
FROM
    (VALUES (N'''a, --''[, --], --[, --]', 14),
            (N'[a, —-b]aaaaaaa_ , --'', --''', 18)) t(string, desired_pos)

有没有什么方法可以使用SELECT查询(或多个查询)而不使用函数来实现这一点?
先谢谢你!
我尝试过各种SUBSTRING、CHARINDEX,甚至一些CROSS APPLY,但似乎无法得到我想要的结果。

gt0wga4j

gt0wga4j1#

在我写下我的解决方案之前,我必须警告你:不要使用它。使用一个函数,或者用其他语言。这段代码可能有错误。它不能处理转义引号等。
我们的想法是首先删除括号[]和引号''中的内容,然后只做一个“简单”的charindex。为了删除括号,我使用了一个递归CTE,它循环匹配引号的任何部分,并将其内容替换为占位符字符串。
重要的一点是引号可能会相互嵌入,因此您必须尝试两种变体,并选择最早的变体。

WITH CTE AS (
    SELECT  *
    FROM
    (VALUES (N'''a, --''[, --], --[, --]', 14),
            (N'[a, —-b]aaaaaaa_ , --'', --''', 18)) t(string, desired_pos)
           )
, cte2 AS (
    select  x.start
    ,   x.finish
    ,   case when x.start > 0 THEN STUFF(string, x.start, x.finish - x.start + 1, REPLICATE('a', x.finish - x.start + 1)) ELSE string END AS newString
    ,   1 as level
    ,   string as orig
    ,   desired_pos
    from    cte
    CROSS APPLY (
        SELECT  *
        ,   ROW_NUMBER() OVER(ORDER BY case when start > 0 THEN 0 ELSE 1 END, start) AS sortorder
        FROM    (
            SELECT  charindex('[', string) AS start
            ,   charindex(']', string) AS finish
            UNION ALL
            SELECT  charindex('''', string) AS startQ
            ,   charindex('''', string, charindex('''', string) + 1) AS finishQ
        ) x
    ) x
    WHERE   x.sortorder = 1
    
    UNION ALL
    select  x.start
    ,   x.finish
    ,   STUFF(newString, x.start, x.finish - x.start + 1, REPLICATE('a', x.finish - x.start + 1))
    ,   1 as level
    ,   orig
    ,   desired_pos
    from    cte2
    CROSS APPLY (
        SELECT  *
        ,   ROW_NUMBER() OVER(ORDER BY case when start > 0 THEN 0 ELSE 1 END, start) AS sortorder
        FROM    (
            SELECT  charindex('[', newString) AS start
            ,   charindex(']', newString) AS finish
            UNION ALL
            SELECT  charindex('''', newString) AS startQ
            ,   charindex('''', newString, charindex('''', newString) + 1) AS finishQ
        ) x
    ) x
    WHERE   x.sortorder = 1
    AND x.start > 0
    AND cte2.start > 0 -- Must have been a match
)

SELECT  PATINDEX('%, --%', newString), *
from (
    select *, row_number() over(partition by orig order by level desc) AS sort
    from cte2
    ) x
where x.sort = 1
mtb9vblg

mtb9vblg2#

试试这个方法。我用另一个相同长度的字符串替换你不需要的字符串。然后寻找你感兴趣的字符串的位置。

SELECT string, desired_pos, 
         CHARINDEX(', --', REPLACE(REPLACE(string, ''', --''', '******'), '[, --]', '******') 
         ) start_index
  FROM (VALUES (N''', --''[, --], --[, --]', 13),
               (N'[, --]aaaaaaa_ , --'', --''', 16)) t(string, desired_pos)
svmlkihl

svmlkihl3#

我不知道这对于C#解决方案是否有意义,但这个CVS类是一个不错的小parcer:TextFieldParser
然后您只需定义Delimeters等,并假设输入始终转义,那么一切都很好。

相关问题