J'écris un analyseur JSON personnalisé en T-SQL † .
Aux fins de mon analyseur, j'utilise la PATINDEX
fonction qui calcule la position d'un jeton à partir d'une liste de jetons. Les jetons dans mon cas sont tous des caractères uniques et ils comprennent ceux-ci:
{} []:,
Habituellement, lorsque j'ai besoin de trouver la (première) position de l'un des caractères donnés, j'utilise la PATINDEX
fonction comme ceci:
PATINDEX('%[abc]%', SourceString)
La fonction me donnera alors la première position de a
ou b
ou c
- selon ce qui se trouve en premier - dans SourceString
.
Maintenant, le problème dans mon cas semble être lié au ]
personnage. Dès que je le précise dans la liste des personnages, par exemple comme ceci:
PATINDEX('%[[]{}:,]%', SourceString)
mon modèle prévu devient apparemment cassé, car la fonction ne trouve jamais de correspondance. Il semble que j'ai besoin d'un moyen d'échapper au premier, ]
ce qui le PATINDEX
traite comme l'un des caractères de recherche plutôt que comme un symbole spécial.
J'ai trouvé cette question posant sur un problème similaire:
Cependant, dans ce cas, le ]
simplement n'a pas besoin d'être spécifié entre crochets, car il s'agit d'un seul caractère et il peut être spécifié sans crochets autour d'eux. La solution alternative, qui utilise l'échappement, ne fonctionne que pour LIKE
et non pour PATINDEX
, car elle utilise un ESCAPE
sous-paragraphe, pris en charge par le premier et non par le second.
Donc, ma question est, existe-t-il un moyen de rechercher un ]
avec l' PATINDEX
utilisation du [ ]
caractère générique? Ou existe-t-il un moyen d'émuler cette fonctionnalité à l'aide d'autres outils Transact-SQL?
Information additionnelle
Voici un exemple de requête que je dois utiliser PATINDEX
avec le […]
modèle ci-dessus. Le modèle ici fonctionne (quoique quelque peu ) car il n'inclut pas le ]
personnage. J'en ai aussi besoin pour travailler avec ]
:
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)
;
La sortie que j'obtiens est:
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" }
Vous pouvez voir que le ]
est inclus dans le cadre de l' S
une des lignes. La Level
colonne indique le niveau d'imbrication, c'est-à-dire l'imbrication des crochets et des accolades. Comme vous pouvez le voir, une fois que le niveau devient 2, il ne revient jamais à 1. Il l'aurait fait si je pouvais faire PATINDEX
reconnaître ]
comme un jeton.
La sortie attendue pour l'exemple ci-dessus est:
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" }
Vous pouvez jouer avec cette requête sur db <> fiddle .
† Nous utilisons SQL Server 2014 et il est peu probable que nous procédions à une mise à niveau prochaine vers une version qui prend en charge l'analyse JSON en natif. Je pourrais écrire une application pour faire le travail mais les résultats de l'analyse doivent être traités plus loin, ce qui implique plus de travail dans l'application que simplement l'analyse - le type de travail qui serait beaucoup plus facile, et probablement plus efficace, effectué avec un script T-SQL, si seulement je pouvais l'appliquer directement aux résultats.
Il est très peu probable que je puisse utiliser SQLCLR comme solution à ce problème. Cependant, cela ne me dérange pas si quelqu'un décide de publier une solution SQLCLR, car cela pourrait être utile pour d'autres.
["foo]bar”]
?Réponses:
Ma propre solution, qui est plus une solution de contournement, consistait à spécifier une plage de caractères qui incluait le
]
et à utiliser cette plage avec les autres caractères du[ ]
caractère générique. J'ai utilisé une plage basée sur la table ASCII. Selon ce tableau, le]
personnage se situe dans le quartier suivant:Ma gamme donc, a pris la forme de
[-^
, à savoir qu'il comprenait quatre caractères:[
,\
,]
,^
. J'ai également spécifié que le modèle utilise un classement binaire, pour correspondre exactement à la plage ASCII. L'PATINDEX
expression résultante a fini par ressembler à ceci:Le problème évident de cette approche est que la plage au début du modèle comprend deux caractères indésirables,
\
et^
. La solution a fonctionné pour moi simplement parce que les caractères supplémentaires ne pouvaient jamais apparaître dans les chaînes JSON spécifiques que j'avais besoin d'analyser. Naturellement, cela ne peut pas être vrai en général, donc je suis toujours intéressé par d'autres méthodes, espérons-le plus universelles que les miennes.la source
J'ai une prise de position probablement terrible de l'arrière quand j'ai dû faire beaucoup de fractionnement de cordes.
Si vous avez un jeu de caractères connu, faites-en un tableau.
Ensuite, utilisez cette magie
CROSS APPLY
avecCHARINDEX
:Si je manque quelque chose d'évident sur ce que vous devez faire, laissez-moi le savoir.
la source
J'ai vu des approches dans le passé pour remplacer le personnage incriminé avant de chercher et de le remettre après.
Dans ce cas, nous pourrions faire quelque chose comme:
Ce code renvoie correctement 5. J'utilise le caractère ¬ car il est peu probable qu'il apparaisse - s'il n'y a pas de caractères ASCII que vous n'utiliserez pas, cette solution ne fonctionnera pas.
Curieusement, la réponse directe à votre question serait non - je ne peux pas non plus demander à PATINDEX de rechercher «]», mais si vous le remplacez, vous n'en avez pas besoin.
Même exemple mais sans l'utilisation variable:
L'utilisation de la solution ci-dessus dans votre code donne les résultats requis:
la source
Étant donné
]
que ce n'est spécial que dans[...]
, vous pouvez l'utiliserPATINDEX
deux fois, en vous déplaçant à l']
extérieur du[...]
. Évaluez les deuxPATINDEX('%[[{}:,]%', SourceString)
etPATINDEX('%]%', SourceString)
. Si un résultat est nul, prenez l'autre. Sinon, prenez la moindre des deux valeurs.Dans votre exemple:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb
la source
Pour un '[' gauche:
Pour un droit ']':
la source