Bagaimana cara mendapatkan objek tertentu dari jsonb array di PostgreSQL?

15

Saya memiliki bidang bernama 'pengguna' yang menyimpan json array yang kira-kira terlihat seperti ini:

"user":

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

Sekarang saya ingin permintaan seperti:

select count from tablename where id = "1"

Saya tidak bisa mendapatkan bidang tertentu countdari array objek json di PostgreSQL 9.4.

Rabi C Shah
sumber

Jawaban:

17

Akan jauh lebih efisien untuk menyimpan nilai Anda dalam skema yang dinormalisasi. Yang mengatakan, Anda juga dapat membuatnya bekerja dengan pengaturan Anda saat ini.

Asumsi

Dengan asumsi definisi tabel ini:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"pengguna" adalah kata yang dilindungi undang - undang dan akan membutuhkan penawaran ganda untuk digunakan sebagai nama kolom. Jangan lakukan itu. Saya menggunakan usrsebagai gantinya.

Pertanyaan

Permintaannya tidak sepele seperti komentar (sekarang dihapus) membuatnya tampak:

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"}]';

Ada 3 langkah dasar :

1. Identifikasi baris yang memenuhi syarat dengan harga murah

WHERE t.usr @> '[{"_id":"1"}]'mengidentifikasi baris dengan objek yang cocok dalam array JSON. Ekspresi dapat menggunakan indeks GIN umum pada jsonbkolom, atau indeks dengan kelas operator yang lebih khusus jsonb_path_ops:

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

WHEREKlausa yang ditambahkan secara logis berlebihan , tetapi diperlukan untuk menggunakan indeks. Ekspresi dalam klausa gabungan memberlakukan kondisi yang sama tetapi hanya setelah membatalkanestest array di setiap baris yang memenuhi syarat sejauh ini. Dengan dukungan indeks, Postgres hanya memproses baris yang berisi objek yang memenuhi syarat untuk memulai. Tidak terlalu menjadi masalah dengan meja kecil, membuat besar perbedaan dengan meja besar dan hanya beberapa baris kualifikasi.

Terkait:

2. Identifikasi objek yang cocok dalam array

Unnest dengan jsonb_array_elements(). ( unnest()hanya baik untuk jenis-jenis array Postgres.) Karena kita hanya tertarik untuk benar-benar mencocokkan objek, segera saring dalam kondisi bergabung.

Terkait:

3. Ekstrak nilai untuk kunci bersarang 'count'

Setelah benda kualifikasi telah diekstraksi, hanya: obj.val->>'count'.

Erwin Brandstetter
sumber
2
Dari mana datangnya obj(value)? Apakah itu di LATERAL JOIN, jsonb_array_elementsatau di tempat lain?
Tyler DeWitt
Sepertinya formatnya mungkin sudah kacau. Apakah saya membacanya dengan benar itu JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)dan itu obj(value)adalah tabel dan kolom alias? Dalam contoh ini, jika objalias tabel, apakah alias itu? Set kembali dari jsonb_array_elements?
Tyler DeWitt
1
ya, dan ya. saya menghapus komentar acak saya.
Erwin Brandstetter
Apakah perlu menggunakan alias kolom? Dalam pengujian saya, JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'memiliki efek yang sama (setelah Anda memperbarui pernyataan pilih untuk digunakan, valuebukan val). Tampaknya jsonb_array_elements(t.usr)mengembalikan tabel dengan hanya satu kolom. Apakah postgres cerdas dan menyadari bahwa obj ->>itu sama dengan obj.val ->>?
Tyler DeWitt
Dengan hanya satu kolom, Postgres menggunakan alias yang diberikan sebagai nama tabel dan kolom. Saya hanya menjadi eksplisit karena ada banyak fungsi set-return yang mengembalikan lebih dari satu kolom.
Erwin Brandstetter