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”]
?Respostas:
Minha própria solução, que é mais uma solução alternativa, consistiu em especificar um intervalo de caracteres que incluía
]
e usando esse intervalo junto com os outros caracteres no[ ]
curinga. Eu usei um intervalo baseado na tabela ASCII. De acordo com essa tabela, o]
personagem está localizado no seguinte bairro:Minha faixa, por isso, tomou a forma de
[-^
, ou seja, que incluía quatro personagens:[
,\
,]
,^
. Também especifiquei que o padrão usa um agrupamento binário, para corresponder exatamente ao intervalo ASCII. APATINDEX
expressão resultante acabou assim:O problema óbvio com essa abordagem é que o intervalo no início do padrão inclui dois caracteres indesejados
\
e^
. A solução funcionou para mim simplesmente porque os caracteres extras nunca poderiam ocorrer nas sequências JSON específicas que eu precisava analisar. Naturalmente, isso não pode ser verdade em geral, então ainda estou interessado em outros métodos, esperançosamente mais universais que os meus.fonte
Provavelmente tenho uma opinião terrível disso quando tive que dividir bastante as cordas.
Se você possui um conjunto conhecido de caracteres, faça uma tabela com eles.
Então use esse mágico
CROSS APPLY
junto comCHARINDEX
:Se estou sentindo falta de algo óbvio sobre o que você precisa fazer, deixe-me saber.
fonte
Eu já vi abordagens no passado para substituir o personagem ofensivo antes de pesquisar e colocá-lo de volta depois.
Nesse caso, poderíamos fazer algo como:
Esse código retorna corretamente 5. Estou usando o caractere as, pois é improvável que apareça - se não houver caracteres ASCII que você não esteja usando, esta solução não funcionará.
Por incrível que pareça, a resposta direta à sua pergunta seria não - também não consigo pedir ao PATINDEX para procurar ']', mas se você o substituir, não será necessário.
Mesmo exemplo, mas sem o uso variável:
Usar a solução acima em seu código gera os resultados necessários:
fonte
Como
]
é apenas especial[...]
, você pode usá-loPATINDEX
duas vezes, movendo-se para]
fora do[...]
. Avalie ambosPATINDEX('%[[{}:,]%', SourceString)
ePATINDEX('%]%', SourceString)
. Se um resultado for zero, pegue o outro. Caso contrário, pegue o menor dos dois valores.No seu exemplo:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb
fonte
À esquerda '[':
Por um direito ']':
fonte