Estoy escribiendo un analizador JSON personalizado en T-SQL † .
Para el propósito de mi analizador, estoy usando la PATINDEX
función que calcula la posición de un token de una lista de tokens. Los tokens en mi caso son caracteres únicos e incluyen estos:
{} []:,
Por lo general, cuando necesito encontrar la (primera) posición de cualquiera de varios caracteres, uso la PATINDEX
función de esta manera:
PATINDEX('%[abc]%', SourceString)
La función me dará la primera posición de a
or b
o c
, lo que ocurra primero, en SourceString
.
Ahora el problema en mi caso parece estar relacionado con el ]
personaje. Tan pronto como lo especifique en la lista de caracteres, por ejemplo, así:
PATINDEX('%[[]{}:,]%', SourceString)
mi patrón deseado aparentemente se rompe, porque la función nunca encuentra una coincidencia. Parece que necesito una forma de escapar del primero ]
para que lo PATINDEX
trate como uno de los caracteres de búsqueda en lugar de un símbolo especial.
He encontrado esta pregunta preguntando sobre un problema similar:
Sin embargo, en ese caso, ]
simplemente no es necesario especificarlo entre paréntesis, ya que es solo un carácter y se puede especificar sin paréntesis. La solución alternativa, que usa escape, funciona solo para LIKE
y no para PATINDEX
, porque usa una ESCAPE
subcláusula, respaldada por la primera y no por la segunda.
Por lo tanto, mi pregunta es, ¿hay alguna manera de buscar una ]
con PATINDEX
el uso del [ ]
comodín? ¿O hay una manera de emular esa funcionalidad usando otras herramientas de Transact-SQL?
Información Adicional
Aquí hay un ejemplo de una consulta en la que necesito usar PATINDEX
con el […]
patrón como el anterior. El patrón aquí funciona (aunque de alguna manera ) porque no incluye el ]
carácter. También lo necesito para trabajar ]
:
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)
;
El resultado que obtengo es:
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" }
Puede ver que ]
se incluye como parte de S
una de las filas. La Level
columna indica el nivel de anidamiento, lo que significa anidamiento entre paréntesis y llaves. Como puede ver, una vez que el nivel se convierte en 2, nunca vuelve a 1. Lo habría hecho si pudiera hacer que PATINDEX
reconozca ]
como una ficha.
El resultado esperado para el ejemplo anterior es:
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" }
Se puede jugar con esta consulta en db <> violín .
† Estamos utilizando SQL Server 2014 y es poco probable que pronto se actualice a una versión que admita el análisis JSON de forma nativa. Podría escribir una solicitud para hacer el trabajo, pero los resultados del análisis deben procesarse aún más, lo que implica más trabajo en la aplicación que solo el análisis, el tipo de trabajo que sería mucho más fácil y probablemente más eficiente. un script T-SQL, si solo pudiera aplicarlo directamente a los resultados.
Es muy poco probable que pueda usar SQLCLR como solución para este problema. Sin embargo, no me importa si alguien decide publicar una solución SQLCLR, ya que eso podría ser útil para otros.
["foo]bar”]
?Respuestas:
Mi propia solución, que es más una solución alternativa, consistió en especificar un rango de caracteres que incluía el
]
uso de ese rango junto con los otros caracteres en el[ ]
comodín. Usé un rango basado en la tabla ASCII. Según esa tabla, el]
personaje se encuentra en el siguiente barrio:Mi rango, por lo tanto, tomó la forma de
[-^
, es decir, que incluyó cuatro caracteres:[
,\
,]
,^
. También especifiqué que el patrón usa una intercalación binaria, para que coincida exactamente con el rango ASCII. LaPATINDEX
expresión resultante terminó así:El problema obvio con este enfoque es que el rango al comienzo del patrón incluye dos caracteres no deseados,
\
y^
. La solución funcionó para mí simplemente porque los caracteres adicionales nunca podrían aparecer en las cadenas JSON específicas que necesitaba analizar. Naturalmente, esto no puede ser cierto en general, por lo que todavía estoy interesado en otros métodos, espero que sean más universales que los míos.fuente
Tengo una opinión probablemente terrible de esto cuando tuve que dividir muchas cuerdas.
Si tiene un conjunto de caracteres conocido, haga una tabla con ellos.
Luego usa ese mágico
CROSS APPLY
junto conCHARINDEX
:Si me falta algo obvio sobre lo que debe hacer, déjeme saber.
fuente
He visto enfoques en el pasado para reemplazar el personaje ofensivo antes de buscar y volver a colocarlo después.
En este caso podríamos hacer algo como:
Este código devuelve correctamente 5. Estoy usando el carácter ¬ ya que es poco probable que aparezca; si no hay caracteres ASCII que no usará, esta solución no funcionará.
Sin embargo, por extraño que parezca, la respuesta directa a su pregunta sería no: tampoco puedo hacer que PATINDEX busque ']', pero si lo reemplaza no es necesario.
Mismo ejemplo pero sin el uso variable:
Usar la solución anterior en su código produce los resultados requeridos:
fuente
Como
]
solo es especial[...]
, puedes usarPATINDEX
dos veces, moviéndote]
fuera del[...]
. Evaluar ambosPATINDEX('%[[{}:,]%', SourceString)
yPATINDEX('%]%', SourceString)
. Si un resultado es cero, tome el otro. De lo contrario, tome el menor de los dos valores.En tu ejemplo:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb
fuente
Para una izquierda '[':
Por un derecho ']':
fuente