Виберіть стовпці всередині json_agg


21

У мене запит типу:

SELECT a.id, a.name, json_agg(b.*) as "item"
  FROM a
  JOIN b ON b.item_id = a.id
 GROUP BY a.id, a.name;

Як я можу вибрати стовпці, bщоб у мене не було b.item_idоб'єкта JSON?

Я читав про ROW, але він повертає об'єкт JSON на зразок:

{"f1": "Foo", "f2": "Bar"}

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

Відповіді:


50

На жаль, у синтаксисі SQL не передбачено сказати "всі стовпці, крім цього одного стовпця" . Ви можете досягти своєї мети, прописавши решту списку стовпців у виразі типу рядка :

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Це коротко для більш явною форми: . ROW(b.col1, b.col2, b.col3)

Однак імена стовпців не зберігаються у виразах рядків. Таким чином ви отримуєте загальні імена ключів в об'єкті JSON. Я бачу 3 варіанти збереження оригінальних назв стовпців:

1. Актер до зареєстрованого типу

Передайте відомий (зареєстрований) тип рядка. Тип реєструється для кожної існуючої таблиці або представлення даних або з явним CREATE TYPEвисловом. Ви можете використовувати тимчасову таблицю для спеціального рішення (живе протягом тривалості сеансу):

CREATE TEMP TABLE x (col1 int, col2 text, col3 date);  -- use adequate data types!

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)::x) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

2. Використовуйте підвідбір

Використовуйте підбірку для побудови похідної таблиці та посилання на таблицю в цілому . Це також містить назви стовпців. Він більш детальний, але вам не потрібен зареєстрований тип:

SELECT a.id, a.name
     , json_agg((SELECT x FROM (SELECT b.col1, b.col2, b.col3) AS x)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

3. json_build_object()у Postgres 9.4 або пізнішої версії

SELECT a.id, a.name
     , json_agg(json_build_object('col1', b.col1, 'col2', b.col2, 'col3', b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Пов'язані:

Аналогічно для jsonbвідповідних функцій jsonb_agg()та jsonb_build_object().

Для Postgres 9.5 або пізнішої версії також дивіться відповідь a_horse з новим коротшим варіантом синтаксису: Postgres додав оператор мінус -для того,jsonb щоб сказати "всі ключі, крім цієї однієї клавіші" .
Оскільки Postgres 10 "крім декількох клавіш" реалізований з тим самим оператором, який приймає text[]як 2-й операнд - як коментує mlt.


1
> або кілька клавіш Зверніть увагу, що json (b) -text [] доступний починаючи з 10.
mlt

Рішення 3 працювало на мене як на чарівність!
Луїз Фернандо да Сілва

17

Починаючи з 9.6, ви можете просто використовувати -для вилучення ключа з JSONB:

SELECT a.id, a.name, jsonb_agg(to_jsonb(b) - 'item_id') as "item"
FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

to_jsonb(b)перетворить цілий рядок і - 'item_id'потім видалить ключ із назвою, item_idрезультат якого потім агрегується.


Ці нові функції, здається, були те, на що сподівався ОП. Я додав посилання на свою відповідь.
Ервін Брандстеттер

Коли я спробував варіант підселектора, у мене з’явилася помилка, пов’язана з json_aggфункцією:function json_agg(record) does not exist
fraxture

@fraxture: тоді ви не використовуєте Postgres 9.6
a_horse_with_no_name

Справді в цьому і була проблема. Чи є спосіб фільтрувати стовпці в v9.2?
матч

8

Насправді це можна зробити без групи, використовуючи підзапити

SELECT 
  a.id, a.name, 
  ( 
    SELECT json_agg(item)
    FROM (
      SELECT b.c1 AS x, b.c2 AS y 
      FROM b WHERE b.item_id = a.id
    ) item
  ) AS items
FROM a;

повертає

{
  id: 1,
  name: "thing one",
  items:[
    { x: "child1", y: "child1 col2"},
    { x: "child2", y: "child2 col2"}
  ]
}

Ця стаття від Джона Аттена справді цікава та має більше деталей


2

Я виявив, що найкраще створити JSON, а потім об'єднати його. напр

with base as (
select a, b, ('{"ecks":"' || to_json(x) || '","wai":"' || to_json(y) || '","zee":"' || to_json(z) || '"}"')::json c
) select (a, b, array_to_json(array_agg(c)) as c)

Зверніть увагу, що це може бути зроблено як підзапит, якщо вам не подобається CTE (або у вас є проблеми з роботою з-за його використання).

Зверніть увагу також, що якщо ви будете робити це багато, може бути корисним створити функцію, щоб обернути для вас пари ключ-значення, щоб код виглядав чистішим. Ви передасте свою функцію (наприклад), 'ecks', 'x'і вона повернеться "ecks": "x".


1

Поки досі немає можливості зробити щось щодо вибору всіх стовпців, окрім одного біта, але ви можете використовувати json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))для отримання масиву jsons json у форматі {"col_1":"col_1 value", ...}.

Отже запит виглядатиме приблизно так:

SELECT a.id, a.name, json_agg(to_json(b.col_1,b.col_2,b.col_3...)) as item
  FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

і повертає рядки як:

id, name, item
8, the_name, [{"col_1":"value_1","col_2":"value_2","col_3":"value_3"...}, {"col_1":"value_1.2","col_2":"value_2.2","col_3":"value_3.2"...},...]
9, the_next_name, [{"col_1":"value_1.3","col_2":"value_2.3","col_3":"value_3.3"...},   {"col_1":"value_1.4","col_2":"value_2.4","col_3":"value_3.4"...},...]
...

(Я зараз на Postgres 9.5.3 і не на 100% впевнений, коли ця підтримка була додана.)


1

Ви можете використовувати json_build_objectтак

SELECT 
  a.id, 
  a.name,
  json_agg(json_build_object('col1', b.col1, 'col2', b.col2) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.