Можно ли найти первое вхождение строки, которая НЕ входит в набор разделителей в SQL Server 2016+?

У меня есть столбец в таблице 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, но не могу получить результат, который ищу.

Просто одна из ваших попыток в вопросе, вы, вероятно, ближе, чем думаете

Dale K 08.02.2023 03:06

Вы бы действительно хотели использовать для этого механизм регулярных выражений. Без него эта проблема становится очень запутанной очень быстро. Вы можете использовать функцию CLR Regex (см. это). Если вы хотите попробовать это в чистом tSQL, вам нужно очень хорошо определить ограничения, например. могут ли у вас быть вложенные скобки (например, [sd[,--]]) или вложенные или экранированные разделители и т. д. Если вы можете иметь вложенность, то удачи! Если нет, то рекурсивный CTE с функцией PATINDEX должен решить вашу проблему... в конце концов.

Alex 08.02.2023 05:16
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
2
90
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Попробуйте этот подход. Я заменяю строки, которые вам не нужны, на другую строку той же длины. Затем найдите позицию интересующей строки.

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

Спасибо. К сожалению, по обе стороны от , могут быть буквы или цифры, но все же внутри разделителей. Я собираюсь обновить свой пример.

Mike S 08.02.2023 03:52

Я не знаю, имеет ли это смысл с решением C#, но этот класс для CVS — приятный маленький парсер: TextFieldParser

Затем вы просто определяете Delimeters и т. д., и если ввод последовательно экранируется, тогда все хорошо.

Ответ принят как подходящий

Прежде чем я запишу свое решение, я должен предупредить вас: НЕ ИСПОЛЬЗУЙТЕ ЭТО. Используйте функцию или сделайте это на другом языке. Этот код, вероятно, глючит. Он не обрабатывает такие вещи, как экранированные кавычки и т. д.

Идея состоит в том, чтобы сначала удалить материал внутри скобок [] и кавычек '', а затем просто сделать "простой" 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

Я опаздываю, но это просто в SQL Server при использовании NGrams8k. Вам не только не нужен REGEX, требуется CLR, C#. Кроме того, NGrams8k будет самым быстрым на сегодняшний день. За 8 лет никто не производил ничего даже отдаленно так быстро. Кроме того, этот код будет быстрее и намного проще, чем рекурсивное решение CTE (которое почти всегда работает медленно в SQL Server).

;--==== Sample Data
DECLARE @T Table (String VARCHAR(100))
INSERT @T
VALUES (N'''a, --''[, --], --[, --]'),
       (N'[a, —-b]aaaaaaa_ , --'', --''');

;--==== Solution
SELECT
  t.String, ng.Position
FROM        @t                                       AS t
CROSS APPLY (VALUES(REPLACE(t.String,'[',CHAR(1))))  AS f(S)
CROSS APPLY samd.NGrams8k(f.S,4)                     AS ng
CROSS APPLY (VALUES(SUBSTRING(f.S,ng.Position-2,7))) AS g(String)
WHERE ng.Token = ', --'
 AND  g.String NOT LIKE '%''%''%'
 AND  g.String NOT LIKE '%'+CHAR(1)+'%]%';

Полученные результаты:

String                        Position
----------------------------- --------------------
'a, --'[, --], --[, --]       14
[a, —-b]aaaaaaa_ , --', --'   18

Другие вопросы по теме