Come ottenere un oggetto particolare dall'array jsonb in PostgreSQL?


15

Ho un campo chiamato 'utente' che contiene un array json che assomiglia approssimativamente a questo:

"user":

[{ "_id" : "1", "count" : "4" }, { "_id" : "3", "count": "4"}]

Ora voglio una query come:

select count from tablename where id = "1"

Non sono in grado di ottenere il campo particolare countda una matrice di oggetti json in PostgreSQL 9.4.

Risposte:


17

Sarebbe molto più efficiente archiviare i tuoi valori in uno schema normalizzato. Detto questo, puoi anche farlo funzionare con la tua configurazione attuale.

ipotesi

Supponendo questa definizione di tabella:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"utente" è una parola riservata e richiederebbe una doppia virgoletta da utilizzare come nome di colonna. Non farlo. Io uso usrinvece.

domanda

La query non è banale come i commenti (ora eliminati) sembravano:

SELECT t.tbl_id, obj.val->>'count' AS count
FROM   tbl t
JOIN   LATERAL jsonb_array_elements(t.usr) obj(val) ON obj.val->>'_id' = '1'
WHERE  t.usr @> '[{"_id":"1"}]';

Ci sono 3 passaggi di base :

1. Identificare le file qualificanti a buon mercato

WHERE t.usr @> '[{"_id":"1"}]'identifica le righe con l'oggetto corrispondente nell'array JSON. L'espressione può utilizzare un indice GIN generico sulla jsonbcolonna o uno con la classe operatore più specializzata jsonb_path_ops:

CREATE INDEX tbl_usr_gin_idx ON tbl USING gin (usr jsonb_path_ops);

La WHEREclausola aggiunta è logicamente ridondante , ma è necessario per utilizzare l'indice. L'espressione nella clausola join impone la stessa condizione, ma solo dopo aver annullato l'intervallo dell'array in ogni riga fino a quel momento. Con il supporto dell'indice, Postgres elabora solo le righe che contengono un oggetto idoneo. Non importa molto con i tavoli piccoli, fa una grande differenza con i tavoli grandi e solo poche righe qualificanti.

Relazionato:

2. Identificare gli oggetti corrispondenti nell'array

Unnest with jsonb_array_elements(). ( unnest()è utile solo per i tipi di array Postgres.) Dato che siamo interessati solo alla corrispondenza effettiva degli oggetti, filtra subito la condizione di join.

Relazionato:

3. Estrarre il valore per la chiave nidificata 'count'

Dopo di ottenimento sono stati estratti, semplicemente: obj.val->>'count'.


2
Da dove obj(value)viene? È sul LATERAL JOIN, il jsonb_array_elementso da qualche altra parte?
Tyler DeWitt,

Sembra che la formattazione potrebbe essere stata rovinata. Sto leggendo correttamente quello JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)e quello obj(value)è un alias di tabella e colonna? In questo esempio, se objè un alias di tabella, a cosa serve un alias? Il set è tornato da jsonb_array_elements?
Tyler DeWitt il

1
si e si. ho rimosso il mio commento criptato.
Erwin Brandstetter,

È necessario utilizzare l'alias di colonna? Nel mio test, ho JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'avuto lo stesso effetto (una volta aggiornato l'istruzione select da utilizzare valueanziché val). Sembra che jsonb_array_elements(t.usr)restituisca una tabella con una sola colonna. Postgres è intelligente e si rende conto che obj ->>è uguale a obj.val ->>?
Tyler DeWitt,

Con una sola colonna, Postgres utilizza un determinato alias come nome di tabella e colonna. Sono solo esplicito in quanto ci sono molte funzioni di restituzione di set che restituiscono più di una colonna.
Erwin Brandstetter,
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.