Я пишу користувальницький 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”]
?