У меня есть столбец в таблице 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, но не могу получить результат, который ищу.
Вы бы действительно хотели использовать для этого механизм регулярных выражений. Без него эта проблема становится очень запутанной очень быстро. Вы можете использовать функцию CLR Regex (см. это). Если вы хотите попробовать это в чистом tSQL, вам нужно очень хорошо определить ограничения, например. могут ли у вас быть вложенные скобки (например, [sd[,--]]) или вложенные или экранированные разделители и т. д. Если вы можете иметь вложенность, то удачи! Если нет, то рекурсивный CTE с функцией PATINDEX должен решить вашу проблему... в конце концов.
Попробуйте этот подход. Я заменяю строки, которые вам не нужны, на другую строку той же длины. Затем найдите позицию интересующей строки.
SELECT string, desired_pos,
CHARINDEX(', --', REPLACE(REPLACE(string, ''', --''', '******'), '[, --]', '******')
) start_index
FROM (VALUES (N''', --''[, --], --[, --]', 13),
(N'[, --]aaaaaaa_ , --'', --''', 16)) t(string, desired_pos)
Спасибо. К сожалению, по обе стороны от , могут быть буквы или цифры, но все же внутри разделителей. Я собираюсь обновить свой пример.
Я не знаю, имеет ли это смысл с решением 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
Просто одна из ваших попыток в вопросе, вы, вероятно, ближе, чем думаете