Результат повернення PostgreSQL встановлений як масив JSON?


134

Я хотів би, щоб PostgreSQL повернув результат запиту як один масив JSON. Дано

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');

Мені б хотілося щось подібне

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

або

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

(насправді було б корисніше знати обидва). Я спробував такі речі, як

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;

І я відчуваю, що я поруч, але не дуже. Чи варто дивитись на іншу документацію, крім 9.15. Функції та оператори JSON ?

До речі, я не впевнений у своїй ідеї. Це звичайне дизайнерське рішення? Думаю, що я, звичайно, міг би взяти результат (наприклад) першого з вищезгаданих 3 запитів і трохи маніпулювати ним у додатку, перш ніж подавати його клієнту, але якщо PostgreSQL може створити кінцевий об'єкт JSON безпосередньо, це було б простіше, тому що я досі не включив будь-яку залежність від будь-якої бібліотеки JSON у своїй програмі.


1
PG 9.4, тепер доступний у версії бета-1, покращив підтримку JSON, включаючи двійкові введення-виведення. Якщо ви працюєте на розроблювальній машині, ви можете перевірити це.
Патрік

@Patrick: дякую, схоже, json_object () - це нова функція в 9.4, і я б спробував щось на зразок SELECT json_object (array_agg (ta), array_agg (tb)) З t, якби у мене був
engineerX

Відповіді:


266

TL; DR

SELECT json_agg(t) FROM t

для масиву об'єктів JSON та

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

для об'єкта масивів JSON.

Список об’єктів

У цьому розділі описано, як генерувати масив об'єктів JSON, при цьому кожен рядок перетворюється на один об'єкт. Результат виглядає приблизно так:

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

9.3 і вище

json_aggФункція виробляє цей результат з коробки. Він автоматично з'ясовує, як перетворити його вхід у JSON та агрегує його у масив.

SELECT json_agg(t) FROM t

Немає jsonb(введено в 9.4) версії json_agg. Ви можете або об'єднати рядки в масив, а потім перетворити їх:

SELECT to_jsonb(array_agg(t)) FROM t

або поєднати json_aggз акторським складом:

SELECT json_agg(t)::jsonb FROM t

Моє тестування свідчить про те, що спочатку їх об’єднання в масив відбувається трохи швидше. Я підозрюю, що це тому, що акторський склад повинен проаналізувати весь результат JSON.

9.2

9.2 не має функцій json_aggабо to_jsonфункцій, тому вам потрібно скористатися старішою array_to_json:

SELECT array_to_json(array_agg(t)) FROM t

Ви можете додатково включити row_to_jsonвиклик у запит:

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

Це перетворює кожен рядок в об'єкт JSON, агрегує об'єкти JSON у вигляді масиву, а потім перетворює масив у масив JSON.

Я не зміг помітити будь-якої суттєвої різниці в роботі між ними.

Об'єкт списків

У цьому розділі описано, як генерувати об’єкт JSON, при цьому кожен ключ є стовпцем таблиці, а кожне значення - масивом значень стовпця. Це результат виглядає приблизно так:

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

9.5 і вище

Ми можемо використовувати json_build_objectфункцію:

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

Ви також можете об'єднати стовпці, створивши один рядок, а потім перетворити їх у об’єкт:

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

Зауважте, що згладжування масивів абсолютно необхідне для того, щоб об’єкт отримав потрібні імена.

Який з них зрозуміліший - це питання думки. Якщо ви використовуєтеjson_build_object функцію, я настійно рекомендую розміщувати одну лінію ключ / значення для поліпшення читабельності.

Ви також можете використовувати array_aggзамість json_agg, але моє тестування показує, що json_aggце трохи швидше.

Немає jsonbверсії json_build_objectфункції. Ви можете об'єднатись в один рядок і перетворити:

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

На відміну від інших запитів щодо такого результату, array_aggздається, трохи швидше при використанні to_jsonb. Я підозрюю, що це пов'язано з накладними синтаксичними розборами та підтвердженням результату JSON json_agg.

Або ви можете використовувати чіткий склад:

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

to_jsonbВерсія дозволяє уникнути кидання і швидше, на мою тестування; знову ж таки, я підозрюю, що це пов’язано з накладними розборами та валідизацією результату.

9.4 та 9.3

json_build_objectФункція була новою для 9.5, так що ви повинні агрегувати і звернену до об'єкта в попередніх версіях:

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

або

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

залежно від того, хочете ви jsonчи jsonb.

(9.3 не має jsonb.)

9.2

У 9.2 навіть не to_jsonіснує. Ви повинні використовувати 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

Документація

Знайдіть документацію для функцій JSON у функціях JSON .

json_aggзнаходиться на сторінці зведених функцій .

Дизайн

Якщо продуктивність важлива, переконайтеся, що ви орієнтуєте свої запити щодо вашої власної схеми та даних, а не довіряйте моїм тестуванням.

Чи це хороший дизайн, чи ні, насправді залежить від вашої конкретної програми. Щодо ремонту, я не бачу жодної особливої ​​проблеми. Це спрощує код вашого додатка і означає, що в цій частині програми менше підтримувати. Якщо PG може дати тобі саме той результат, який тобі потрібен поза коробкою, єдиною причиною, яку я можу вважати, не використовувати його, були б міркування щодо ефективності. Не винаходити колесо і все.

Нулі

Функції сукупності зазвичай віддають, NULLколи вони працюють над нульовими рядами. Якщо це можливість, ви можете скористатися, COALESCEщоб їх уникнути. Кілька прикладів:

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

Або

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

Кредит Ханнес Landeholm для вказуючи на це


3
Спасибі за вашу відповідь. Ви надихнули мене знайти відповідь на моє друге запитання: ВИБІРАТИ row_to_json (row (array_agg (ta), array_agg (tb))) FROM t, хоча результат має "f1" і "f2" як мітки замість a і b.
engineerX

@engineerX Я розширив свою відповідь.
jpmc26

3
У деяких випадках може бути небажаним повернути NULL замість порожнього масиву JSON, коли внутрішній вибір (з t) повертає нульові ряди. Це викликано сукупними функціями, які завжди повертають NULL, коли вибір над рядками не вирішується і вирішується coalesce: array_to_json (coalesce (array_agg (t), array [] :: record [])).
Hannes Landeholm

3
ви можете використовувати to_jsonзамість row_to_jsonіarray_to_json
itnikolay

Для вибору (декількох) конкретних стовпців ви повинні передати їх як єдиний аргумент - список круглих дужок на зразок SELECT json_agg((column1, column2, ...)) FROM t - помітити додаткові дужки. Це може бути не очевидно "поза коробкою".
jave.web

19

Також якщо ви хочете, щоб вибране поле з таблиці та зведене тоді було як масив.

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

Результат прийде.

 [{'data_a':1,'data_b':'value1'}
  {'data_a':2,'data_b':'value2'}]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.