Tengo una tabla, persons
que contiene dos columnas, una id
y una data
columna basada en JSONB (esta tabla acaba de hacerse con fines demostrativos para jugar con el soporte JSON de PostgreSQL).
Ahora, se supone que contiene dos registros:
1, { name: 'John', age: 30 }
2, { name: 'Jane', age: 20 }
Ahora, supongo que quiero obtener el nombre de cada persona mayor de 25 años. Lo que he intentado es:
select data->'name' as name from persons where data->'age' > 25
Desafortunadamente, esto da como resultado un error. Puedo resolverlo usando en ->>
lugar de ->
, pero las comparaciones ya no funcionan como se esperaba, ya que no se comparan los números, sino sus representaciones como cadenas:
select data->'name' as name from persons where data->>'age' > '25'
Luego descubrí que realmente puedo resolver el problema usando ->
y un elenco para int
:
select data->'name' as name from persons where cast(data->'age' as int) > 25
Esto funciona, pero no es tan bueno que tenga que saber el tipo real (el tipo de age
en el documento JSON es de number
todos modos, entonces, ¿por qué PostgreSQL no puede resolver eso por sí mismo?).
Luego descubrí que si convierto manualmente el text
uso de la ::
sintaxis, todo funciona como se esperaba, aunque ahora estamos comparando cadenas nuevamente.
select data->'name' as name from persons where data->'age'::text > '25'
Si luego intento esto con el nombre en lugar de la edad, no funciona:
select data->'name' as name from persons where data->'name'::text > 'Jenny'
Esto da como resultado un error:
sintaxis de entrada no válida para el tipo json
Obviamente, no consigo algo aquí. Desafortunadamente, es bastante difícil encontrar ejemplos del mundo real del uso de JSON con PostgreSQL.
¿Alguna pista?
fuente
data->'name'::text
, está convirtiendo la'name'
cadena a texto, no el resultado. No obtiene un error al comparar'25'
porque25
es un literal JSON válido; peroJenny
no lo es (aunque"Jenny"
lo sería).'Jenny'
con'"Jenny"'
.Respuestas:
Esto no funciona porque está tratando de emitir un
jsonb
valorinteger
.Esto realmente funcionaría:
O más corto:
Y esto:
Parece que la confusión con los dos operadores
->
y->>
y la precedencia de operadores . El reparto se::
une más fuerte que los operadores json (b).Figura tipo de forma dinámica
Esta es la parte más interesante de tu pregunta:
SQL es un lenguaje estrictamente tipado, no permite evaluar la misma expresión
integer
en una fila ytext
en la siguiente. Pero como solo está interesado en elboolean
resultado de la prueba, puede sortear esta restricción con unaCASE
expresión que se bifurca dependiendo del resultado dejsonb_typeof()
:Un literal de cadena sin tipo a la derecha del
>
operador se coacciona al tipo respectivo del valor a la izquierda automáticamente. Si coloca un valor escrito allí, el tipo tiene que coincidir o debe emitirlo explícitamente, a menos que haya una conversión implícita adecuada registrada en el sistema.Si sabe que todos los valores numéricos son en realidad
integer
, también puede:fuente