Estou escrevendo um analisador JSON personalizado em T-SQL † .
Para os fins do meu analisador, estou usando a PATINDEX
função que calcula a posição de um token a partir de uma lista de tokens. Os tokens no meu caso são todos caracteres únicos e incluem:
{} []:,
Normalmente, quando preciso encontrar a (primeira) posição de qualquer um dos vários caracteres fornecidos, uso a PATINDEX
função da seguinte maneira:
PATINDEX('%[abc]%', SourceString)
A função então me dará a primeira posição de a
ou b
ou c
- o que ocorrer primeiro SourceString
.
Agora, o problema no meu caso parece estar conectado com o ]
personagem. Assim que eu especificá-lo na lista de caracteres, por exemplo:
PATINDEX('%[[]{}:,]%', SourceString)
meu padrão pretendido parece quebrado, porque a função nunca encontra uma correspondência. Parece que preciso de uma maneira de escapar do primeiro, ]
para que PATINDEX
ele seja tratado como um dos caracteres de pesquisa, e não como um símbolo especial.
Eu encontrei esta pergunta perguntando sobre um problema semelhante:
No entanto, nesse caso, o ]
simplesmente não precisa ser especificado entre colchetes, porque é apenas um caractere e pode ser especificado sem colchetes ao redor deles. A solução alternativa, que usa escape, funciona apenas para LIKE
e não para PATINDEX
, porque usa uma ESCAPE
subcláusula, suportada pelo primeiro e não pelo último.
Então, minha pergunta é, existe alguma maneira de olhar para um ]
com PATINDEX
usando o [ ]
curinga? Ou existe uma maneira de emular essa funcionalidade usando outras ferramentas Transact-SQL?
informação adicional
Aqui está um exemplo de uma consulta em que preciso usar PATINDEX
com o […]
padrão acima. O padrão aqui funciona (ainda que um pouco ) porque não inclui o ]
personagem. Eu preciso que ele trabalhe ]
também:
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)
;
A saída que recebo é:
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" }
Você pode ver que o ]
item está incluído como parte de S
uma das linhas. A Level
coluna indica o nível de aninhamento, significando aninhamento entre colchetes e chaves. Como você pode ver, uma vez que o nível se torna 2, ele nunca retorna para 1. Teria se eu pudesse PATINDEX
reconhecê-lo ]
como um token.
A saída esperada para o exemplo acima é:
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" }
Você pode jogar com esta consulta no db <> fiddle .
† Estamos usando o SQL Server 2014 e é improvável que você atualize em breve para uma versão que suporte a análise JSON nativamente. Eu poderia escrever um aplicativo para fazer o trabalho, mas os resultados da análise precisam ser processados ainda mais, o que implica mais trabalho no aplicativo do que apenas a análise - o tipo de trabalho que seria muito mais fácil e provavelmente mais eficiente, feito com um script T-SQL, se eu pudesse aplicá-lo diretamente aos resultados.
É muito improvável que eu possa usar o SQLCLR como uma solução para esse problema. No entanto, não me importo se alguém decide postar uma solução SQLCLR, pois isso pode ser útil para outras pessoas.
["foo]bar”]
?