Я пишу користувальницький JSON-аналізатор у T-SQL † .
Для мого аналізатора я використовую PATINDEXфункцію, яка обчислює позицію токена зі списку лексем. У моєму випадку лексеми - це єдині символи, і вони включають такі:
{} []:,
Зазвичай, коли мені потрібно знайти (перше) положення будь-якого з декількох заданих символів, я використовую цю PATINDEXфункцію:
PATINDEX('%[abc]%', SourceString)
Функція буде потім дати мені першу позицію aабо bабо c- в залежності від того , трапляється знайти перший - в SourceString.
Зараз проблема в моєму випадку, здається, пов'язана з ]характером. Щойно я вказую його у списку символів, наприклад, як:
PATINDEX('%[[]{}:,]%', SourceString)
мій задуманий зразок, мабуть, стає порушеним, оскільки функція ніколи не знаходить відповідності. Схоже, мені потрібен спосіб уникнути першого, ]щоб PATINDEXтрактувати його як один із символів пошуку, а не спеціальний символ.
Я знайшов це запитання про подібну проблему:
Однак у такому випадку ]просто не потрібно вказувати в дужках, оскільки це лише один символ, і він може бути вказаний без дужок навколо них. Альтернативне рішення, яке використовує втечу, працює лише для, LIKEа не для PATINDEX, оскільки воно використовує ESCAPEпідпункт, підтримуваний першим, а не другим.
Отже, моє запитання, чи є спосіб , щоб шукати ]з PATINDEXдопомогою [ ]підстановки? Або є спосіб емуляції цієї функції за допомогою інших інструментів Transact-SQL?
Додаткова інформація
Нижче наведено приклад запиту , коли мені потрібно використовувати PATINDEXз […]малюнком , як вказані вище. Шаблон тут працює (хоча і дещо ), оскільки він не містить ]символу. Мені це потрібно і для роботи ]:
WITH
data AS (SELECT CAST('{"f1":["v1","v2"],"f2":"v3"}' AS varchar(max)) AS ResponseJSON),
parser AS
(
SELECT
Level = 1,
OpenClose = 1,
P = p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1),
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
data AS d
CROSS APPLY (SELECT PATINDEX('%[[{]%', d.ResponseJSON)) AS p (P)
UNION ALL
SELECT
Level = ISNULL(d.OpenClose - 1, 0) + d.Level + ISNULL(oc.OpenClose, 0),
OpenClose = oc.OpenClose,
P = d.P + p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = c.C,
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
parser AS d
CROSS APPLY (SELECT PATINDEX('%[[{}:,]%' COLLATE Latin1_General_BIN2, d.ResponseJSON)) AS p (P)
CROSS APPLY (SELECT SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1)) AS c (C)
CROSS APPLY (SELECT CASE WHEN c.C IN ('[', '{') THEN 1 WHEN c.C IN (']', '}') THEN 0 END) AS oc (OpenClose)
WHERE 1=1
AND p.P <> 0
)
SELECT
*
FROM
parser
OPTION
(MAXRECURSION 0)
;
Вихід, який я отримую:
Level OpenClose P S C ResponseJSON
----- --------- -- ----- -- ---------------------------
1 1 1 { "f1":["v1","v2"],"f2":"v3"}
1 null 6 "f1" : ["v1","v2"],"f2":"v3"}
2 1 7 [ "v1","v2"],"f2":"v3"}
2 null 12 "v1" , "v2"],"f2":"v3"}
2 null 18 "v2"] , "f2":"v3"}
2 null 23 "f2" : "v3"}
2 0 28 "v3" }
Ви можете бачити, що ]символи включені як частина Sв одному з рядків. У Levelстовпці вказується рівень гніздування, тобто дужка та дужки вкладення. Як бачите, як тільки рівень стає 2, він ніколи не повертається до 1. Це було б, якби я міг зробити PATINDEXвизнання ]як маркер.
Очікуваний вихід для наведеного вище прикладу:
Level OpenClose P S C ResponseJSON
----- --------- -- ---- -- ---------------------------
1 1 1 { "f1":["v1","v2"],"f2":"v3"}
1 NULL 6 "f1" : ["v1","v2"],"f2":"v3"}
2 1 7 [ "v1","v2"],"f2":"v3"}
2 NULL 12 "v1" , "v2"],"f2":"v3"}
2 0 17 "v2" ] ,"f2":"v3"}
1 NULL 18 , "f2":"v3"}
1 NULL 23 "f2" : "v3"}
1 0 28 "v3" }
Ви можете грати з цим запитом у db <> fiddle .
† Ми використовуємо SQL Server 2014 і навряд чи незабаром оновиться до версії, яка підтримує JSON для синтаксичного розбору. Я міг би написати заявку на виконання роботи, але результати розбору потребують додаткової обробки, що передбачає більше роботи в додатку, ніж просто розбір - таку роботу, яку було б набагато простіше і, можливо, ефективніше, виконувати з сценарій T-SQL, якби тільки я міг застосувати його безпосередньо до результатів.
Дуже малоймовірно, що я можу використовувати SQLCLR як рішення цієї проблеми. Однак я не заперечую, якщо хтось вирішить опублікувати рішення SQLCLR, оскільки це може бути корисним для інших.
["foo]bar”]?