Interrogazione di JSONB in ​​PostgreSQL


14

Ho una tabella, personsche contiene due colonne, una ide una basata su JSONB data(questa tabella è stata appena creata a scopo dimostrativo per giocare con il supporto JSON di PostgreSQL).

Ora, suppongo che contenga due record:

1, { name: 'John', age: 30 }
2, { name: 'Jane', age: 20 }

Ora, suppongo che voglio ottenere il nome di ogni persona di età superiore ai 25 anni. Quello che ho provato è:

select data->'name' as name from persons where data->'age' > 25

Sfortunatamente, questo provoca un errore. Posso risolverlo usando ->>invece di ->, ma poi i confronti non funzionano più come previsto, dal momento che non vengono confrontati i numeri, ma le loro rappresentazioni come stringhe:

select data->'name' as name from persons where data->>'age' > '25'

Ho quindi capito che posso effettivamente risolvere il problema utilizzando ->e un cast per int:

select data->'name' as name from persons where cast(data->'age' as int) > 25

Funziona, ma non è così bello che devo conoscere il tipo reale (il tipo di agenel documento JSON è numbercomunque, quindi perché PostgreSQL non riesce a capirlo da solo?).

Ho quindi capito che se convertivo manualmente in textusando la ::sintassi, anche tutto funziona come previsto, anche se ora stiamo confrontando nuovamente le stringhe.

select data->'name' as name from persons where data->'age'::text > '25'

Se poi provo questo con il nome anziché l'età, non funziona:

select data->'name' as name from persons where data->'name'::text > 'Jenny'

Ciò provoca un errore:

sintassi di input non valida per il tipo json

Abbastanza ovviamente, non capisco qualcosa qui. Sfortunatamente, è abbastanza difficile trovare esempi reali di utilizzo di JSON con PostgreSQL.

Qualche suggerimento?


1
In data->'name'::text, stai lanciando la 'name'stringa in testo, non nel risultato. Non si ottiene un errore se confrontato con '25'perché 25è un valore letterale JSON valido; ma Jennynon lo è (anche se "Jenny"sarebbe).
Chirlu,

Grazie, questa è la soluzione :-). Ho confuso 'Jenny'con '"Jenny"'.
Golo Roden,

Risposte:


14

Questo non funziona perché sta cercando di lanciare un jsonbvalore integer.

select data->'name' as name from persons where cast(data->'age' as int) > 25

Questo effettivamente funzionerebbe:

SELECT data->'name' AS name FROM persons WHERE cast(data->>'age' AS int) > 25;

O più breve:

SELECT data->'name' AS name FROM persons WHERE (data->>'age')::int > 25;

E questo:

SELECT data->'name' AS name FROM persons WHERE data->>'name' > 'Jenny';

Sembra confusione con i due operatori ->ed->> e precedenza degli operatori . Il cast si ::lega più forte degli operatori json (b).

Capire il tipo in modo dinamico

Questa è la parte più interessante della tua domanda:

il tipo di età nel documento JSON è comunque il numero, quindi perché PostgreSQL non può capirlo da solo?

SQL è un linguaggio tipizzato in modo rigoroso, non consente alla stessa espressione di valutare integerin una riga e textin quella successiva. Ma poiché sei interessato solo al booleanrisultato del test, puoi aggirare questa restrizione con CASEun'espressione che biforca in base al risultato di jsonb_typeof():

SELECT data->'name'
FROM   persons
WHERE  CASE jsonb_typeof(data->'age')
        WHEN 'number'  THEN (data->>'age')::numeric > '25' -- treated as numeric
        WHEN 'string'  THEN data->>'age' > 'age_level_3'   -- treated as text
        WHEN 'boolean' THEN (data->>'age')::bool           -- use boolean directly (example)
        ELSE FALSE                                         -- remaining: array, object, null
       END;

Una stringa non tipizzata letterale a destra >dell'operatore viene forzata automaticamente al rispettivo tipo di valore a sinistra. Se inserisci un valore digitato lì, il tipo deve corrispondere o devi lanciarlo in modo esplicito, a meno che non ci sia un cast implicito adeguato registrato nel sistema.

Se sai che tutti i valori numerici sono effettivamente integer, puoi anche:

... (data->>'age')::int > 25 ...

qual è l'espressione di base sqlalchemy per il confronto sopra riportato dell'istruzione select ad es. s = select ([issue]). where (issues.c.id == mid) .select_from (issue, ..... outerjoin (issues.c.data ['type_id'] == mtypes.c.id) ) ... Qui issue.c.data di tipo jsonb e viene confrontato con mtypes.c.id di tipo intero
user956424
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.