Hasil pengembalian PostgreSQL ditetapkan sebagai array JSON?


135

Saya ingin agar PostgreSQL mengembalikan hasil kueri sebagai satu array JSON. Diberikan

create table t (a int primary key, b text);

insert into t values (1, 'value1');
insert into t values (2, 'value2');
insert into t values (3, 'value3');

Saya mau membeli sesuatu yang mirip dengan

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

atau

{"a":[1,2,3], "b":["value1","value2","value3"]}

(sebenarnya akan lebih berguna untuk mengetahui keduanya). Saya telah mencoba beberapa hal seperti

select row_to_json(row) from (select * from t) row;
select array_agg(row) from (select * from t) row;
select array_to_string(array_agg(row), '') from (select * from t) row;

Dan saya merasa saya dekat, tetapi tidak benar-benar di sana. Haruskah saya melihat dokumentasi lain kecuali untuk 9.15. Fungsi dan Operator JSON ?

Ngomong-ngomong, saya tidak yakin dengan ide saya. Apakah ini keputusan desain biasa? Pemikiran saya adalah bahwa saya dapat, tentu saja, mengambil hasil (misalnya) dari 3 kueri pertama di atas dan memanipulasinya sedikit dalam aplikasi sebelum menyajikannya kepada klien, tetapi jika PostgreSQL dapat membuat objek JSON terakhir secara langsung, itu akan lebih sederhana, karena saya masih belum menyertakan ketergantungan pada pustaka JSON mana pun dalam aplikasi saya.


1
PG 9.4, sekarang tersedia dalam rilis beta 1, telah meningkatkan dukungan untuk JSON, termasuk I / O biner. Jika Anda menggunakan mesin pengembangan, Anda mungkin ingin memeriksanya.
Patrick

@ Patrick: terima kasih, sepertinya json_object () adalah fungsi baru di 9.4 dan saya akan mencoba sesuatu seperti SELECT json_object (array_agg (ta), array_agg (tb)) FROM t, jika saya memilikinya
engineerX

Jawaban:


266

TL; DR

SELECT json_agg(t) FROM t

untuk larik objek JSON, dan

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

untuk objek array JSON.

Daftar objek

Bagian ini menjelaskan cara membuat larik objek JSON, dengan setiap baris diubah menjadi satu objek. Hasilnya terlihat seperti ini:

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

9.3 dan lebih tinggi

The json_aggFungsi menghasilkan hasil ini keluar dari kotak. Ini secara otomatis mencari cara untuk mengubah inputnya menjadi JSON dan menggabungkannya menjadi sebuah array.

SELECT json_agg(t) FROM t

Tidak ada jsonb(diperkenalkan pada 9.4) versi json_agg. Anda dapat menggabungkan baris menjadi array dan kemudian mengonversinya:

SELECT to_jsonb(array_agg(t)) FROM t

atau gabungkan json_aggdengan pemeran:

SELECT json_agg(t)::jsonb FROM t

Pengujian saya menunjukkan bahwa menggabungkan mereka ke dalam array terlebih dahulu sedikit lebih cepat. Saya menduga bahwa ini karena pemeran harus mengurai seluruh hasil JSON.

9.2

9.2 tidak memiliki json_aggatau to_jsonfungsi, jadi Anda perlu menggunakan yang lebih lama array_to_json:

SELECT array_to_json(array_agg(t)) FROM t

Anda secara opsional dapat menyertakan row_to_jsonpanggilan dalam kueri:

SELECT array_to_json(array_agg(row_to_json(t))) FROM t

Ini mengonversi setiap baris menjadi objek JSON, menggabungkan objek JSON sebagai larik, lalu mengonversi larik menjadi larik JSON.

Saya tidak dapat melihat perbedaan kinerja yang signifikan antara keduanya.

Objek daftar

Bagian ini menjelaskan cara membuat objek JSON, dengan setiap kunci menjadi kolom di tabel dan setiap nilai menjadi larik nilai kolom. Hasilnya terlihat seperti ini:

{"a":[1,2,3], "b":["value1","value2","value3"]}

9.5 dan lebih tinggi

Kami dapat memanfaatkan json_build_objectfungsinya:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

Anda juga bisa menggabungkan kolom, membuat satu baris, lalu mengubahnya menjadi objek:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

Perhatikan bahwa aliasing array mutlak diperlukan untuk memastikan bahwa objek memiliki nama yang diinginkan.

Mana yang lebih jelas adalah soal opini. Jika menggunakanjson_build_object fungsi ini, saya sangat menyarankan untuk meletakkan satu pasangan kunci / nilai pada satu baris untuk meningkatkan keterbacaan.

Anda juga bisa menggunakan array_aggsebagai pengganti json_agg, tetapi pengujian saya menunjukkan bahwa json_aggsedikit lebih cepat.

Tidak ada jsonbversi dari json_build_objectfungsi tersebut. Anda dapat menggabungkan menjadi satu baris dan mengonversi:

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

Berbeda dengan query lain untuk jenis hasil ini, array_aggsepertinya menjadi sedikit lebih cepat saat digunakan to_jsonb. Saya menduga ini karena penguraian overhead dan memvalidasi hasil JSON dari json_agg.

Atau Anda dapat menggunakan pemeran eksplisit:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )::jsonb
FROM t

The to_jsonbVersi memungkinkan Anda untuk menghindari pemain dan lebih cepat, menurut pengujian saya; sekali lagi, saya menduga ini karena overhead parsing dan memvalidasi hasilnya.

9.4 dan 9.3

The json_build_objectfungsi baru untuk 9,5, sehingga Anda harus agregat dan mengkonversi ke objek di versi sebelumnya:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

atau

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

tergantung pada apakah Anda ingin jsonatau jsonb.

(9.3 tidak memiliki jsonb.)

9.2

Di 9.2, bahkan tidak to_jsonada. Anda harus menggunakan row_to_json:

SELECT row_to_json(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

Dokumentasi

Temukan dokumentasi untuk fungsi JSON dalam fungsi JSON .

json_aggada di halaman fungsi agregat .

Rancangan

Jika kinerja itu penting, pastikan Anda mengukur kueri Anda terhadap skema dan data Anda sendiri, daripada mempercayai pengujian saya.

Apakah itu desain yang bagus atau tidak sangat tergantung pada aplikasi spesifik Anda. Dalam hal pemeliharaan, saya tidak melihat ada masalah khusus. Ini menyederhanakan kode aplikasi Anda dan berarti lebih sedikit yang harus dipertahankan di bagian aplikasi tersebut. Jika PG dapat memberikan hasil yang Anda butuhkan di luar kotak, satu-satunya alasan yang dapat saya pikirkan untuk tidak menggunakannya adalah pertimbangan kinerja. Jangan menemukan kembali roda dan semuanya.

Nulls

Fungsi agregat biasanya memberikan kembali NULLketika mereka beroperasi pada baris nol. Jika ini adalah kemungkinan, Anda mungkin ingin menggunakan COALESCEuntuk menghindarinya. Beberapa contoh:

SELECT COALESCE(json_agg(t), '[]'::json) FROM t

Atau

SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t

Kredit ke Hannes Landeholm untuk menunjukkan ini


3
Terima kasih atas jawaban Anda. Anda menginspirasi saya untuk menemukan jawaban untuk pertanyaan kedua saya, SELECT row_to_json (row (array_agg (ta), array_agg (tb))) FROM t, meskipun hasilnya memiliki "f1" dan "f2" sebagai label, bukan a dan b.
engineerX

@engineerX Saya telah memperluas jawaban saya.
jpmc26

3
Dalam beberapa kasus, mungkin tidak diinginkan untuk mendapatkan NULL kembali daripada array JSON kosong ketika seleksi dalam (dari t) mengembalikan baris nol. Hal ini disebabkan oleh fungsi agregat yang selalu mengembalikan NULL saat memilih tanpa baris dan dapat diselesaikan dengan penggabungan: array_to_json (coalesce (array_agg (t), array [] :: record [])).
Hannes Landeholm

3
Anda dapat menggunakan to_jsonsebagai pengganti row_to_jsondanarray_to_json
itsnikolay

Untuk memilih (beberapa) kolom tertentu, Anda harus meneruskannya sebagai satu argumen - daftar kurung bulat seperti SELECT json_agg((column1, column2, ...)) FROM t - perhatikan tanda kurung tambahan. Ini mungkin tidak terlihat jelas "di luar kotak".
jave.web

19

Juga jika Anda ingin bidang yang dipilih dari tabel dan digabungkan kemudian sebagai array.

SELECT json_agg(json_build_object('data_a',a,
                                  'data_b',b,
))  from t;

Hasilnya akan datang.

 [{'data_a':1,'data_b':'value1'}
  {'data_a':2,'data_b':'value2'}]
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.