Meminta JSONB dalam PostgreSQL

14

Saya punya tabel,, personsyang berisi dua kolom, kolom berbasis iddan JSONB data(tabel ini baru saja dibuat untuk tujuan demonstrasi untuk bermain-main dengan dukungan JSON PostgreSQL).

Sekarang, seharusnya berisi dua catatan:

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

Sekarang, seharusnya saya ingin mendapatkan nama setiap orang yang lebih tua dari 25. Apa yang saya coba adalah:

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

Sayangnya, ini menghasilkan kesalahan. Saya bisa menyelesaikannya dengan menggunakan ->>alih-alih ->, tetapi kemudian perbandingan tidak berfungsi seperti yang diharapkan lagi, karena bukan angka yang dibandingkan, tetapi representasi mereka sebagai string:

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

Saya kemudian menemukan bahwa saya benar-benar dapat memecahkan masalah dengan menggunakan ->dan pemeran untuk int:

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

Ini berfungsi, tetapi tidak menyenangkan karena saya harus tahu tipe yang sebenarnya (tipe agedalam dokumen JSON adalah number, jadi mengapa PostgreSQL tidak bisa mengetahuinya dengan sendirinya?).

Saya kemudian menemukan bahwa jika saya mengkonversi secara manual untuk textmenggunakan ::sintaks, semuanya berfungsi seperti yang diharapkan juga - meskipun kita sekarang membandingkan string lagi.

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

Jika saya kemudian mencoba ini dengan nama bukan umur, itu tidak berhasil:

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

Ini menghasilkan kesalahan:

sintaks input tidak valid untuk tipe json

Jelas sekali, saya tidak mendapatkan sesuatu di sini. Sayangnya, cukup sulit untuk menemukan contoh nyata menggunakan JSON dengan PostgreSQL.

Ada petunjuk?

Golo Roden
sumber
1
Di data->'name'::text, Anda melemparkan 'name'string ke teks, bukan hasilnya. Anda tidak mendapatkan kesalahan ketika membandingkan '25'karena 25JSON literal yang valid; tetapi Jennytidak (meskipun "Jenny"akan).
chirlu
Terima kasih, itu solusinya :-). Saya bingung 'Jenny'dengan '"Jenny"'.
Golo Roden

Jawaban:

14

Ini tidak berfungsi karena mencoba memberikan jsonbnilai integer.

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

Ini sebenarnya akan berfungsi:

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

Atau lebih pendek:

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

Dan ini:

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

Sepertinya kebingungan dengan dua operator ->dan->> dan prioritas operator . Para pemain ::mengikat lebih kuat dari operator json (b).

Mencari tahu tipe secara dinamis

Ini adalah bagian yang lebih menarik dari pertanyaan Anda:

jenis umur dalam dokumen JSON adalah angka, jadi mengapa PostgreSQL tidak bisa mengetahuinya dengan sendirinya?

SQL adalah bahasa yang diketik dengan ketat, tidak mengizinkan ekspresi yang sama untuk dievaluasi integerdalam satu baris dan textdi berikutnya. Tetapi karena Anda hanya tertarik pada booleanhasil tes, Anda dapat mengatasi pembatasan ini dengan CASEekspresi yang bercabang tergantung pada hasil dari 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;

Sebuah string literal yang tidak diketik di sebelah kanan >operator dipaksa untuk masing-masing jenis nilai di sebelah kiri secara otomatis. Jika Anda memasukkan nilai yang diketik di sana, jenisnya harus cocok atau Anda harus melemparkannya secara eksplisit - kecuali ada pemeran implisit yang memadai terdaftar dalam sistem.

Jika Anda tahu bahwa semua nilai numerik sebenarnya integer, Anda juga dapat:

... (data->>'age')::int > 25 ...
Erwin Brandstetter
sumber
apa ekspresi inti sqlalchemy untuk perbandingan di atas pilih pernyataan misalnya. s = select ([issues]). where (issues.c.id == mid) .select_from (issues, ..... outerjoin (issues.c.data ['type_id'] == mtypes.c.id) ) ... Berikut tipe data jsonb issues.c.data dan sedang dibandingkan dengan mtypes.c.id dari tipe integer
user956424