PostgreSQL 9.2 строка в json () с вложенными соединениями
Я пытаюсь сопоставить результаты запроса в JSON с помощью row_to_json() функция, которая была добавлена в PostgreSQL 9.2.
у меня возникли проблемы с определением лучшего способа представления Соединенных строк как вложенных объектов (1: 1 отношения)
вот что я пробовал (код установки: таблицы, Примеры данных, а затем запрос):
-- some test tables to start out with:
create table role_duties (
id serial primary key,
name varchar
);
create table user_roles (
id serial primary key,
name varchar,
description varchar,
duty_id int, foreign key (duty_id) references role_duties(id)
);
create table users (
id serial primary key,
name varchar,
email varchar,
user_role_id int, foreign key (user_role_id) references user_roles(id)
);
DO $$
DECLARE duty_id int;
DECLARE role_id int;
begin
insert into role_duties (name) values ('Script Execution') returning id into duty_id;
insert into user_roles (name, description, duty_id) values ('admin', 'Administrative duties in the system', duty_id) returning id into role_id;
insert into users (name, email, user_role_id) values ('Dan', '[email protected]', role_id);
END$$;
сам запрос:
select row_to_json(row)
from (
select u.*, ROW(ur.*::user_roles, ROW(d.*::role_duties)) as user_role
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id
) row;
я нашел, если я использую ROW(), я мог бы разделить полученные поля на ребенка объект, но он, кажется, ограничен одним уровнем. Я не могу вставить больше AS XXX заявления, как я думаю, нужно в этом случае.
мне предоставлены имена столбцов, потому что я приведу к соответствующему типу записи, например с ::user_roles, в случае результатов этой таблицы.
вот что возвращает этот запрос:
{
"id":1,
"name":"Dan",
"email":"[email protected]",
"user_role_id":1,
"user_role":{
"f1":{
"id":1,
"name":"admin",
"description":"Administrative duties in the system",
"duty_id":1
},
"f2":{
"f1":{
"id":1,
"name":"Script Execution"
}
}
}
}
что я хочу сделать, это создать JSON для соединений (опять же 1:1 отлично) таким образом, чтобы я мог добавлять соединения и представлять их как дочерние объекты родителей, к которым они присоединяются, т. е. как следующие:
{
"id":1,
"name":"Dan",
"email":"[email protected]",
"user_role_id":1,
"user_role":{
"id":1,
"name":"admin",
"description":"Administrative duties in the system",
"duty_id":1
"duty":{
"id":1,
"name":"Script Execution"
}
}
}
}
любая помощь приветствуется. Спасибо за чтение.
2 ответов:
обновление: в PostgreSQL 9.4 это значительно улучшает введение
to_json,json_build_object,json_objectиjson_build_array, хотя это многословно из-за необходимости явно называть все поля:select json_build_object( 'id', u.id, 'name', u.name, 'email', u.email, 'user_role_id', u.user_role_id, 'user_role', json_build_object( 'id', ur.id, 'name', ur.name, 'description', ur.description, 'duty_id', ur.duty_id, 'duty', json_build_object( 'id', d.id, 'name', d.name ) ) ) from users u inner join user_roles ur on ur.id = u.user_role_id inner join role_duties d on d.id = ur.duty_id;для более старых версий, Читать далее.
это не ограничивается одной строкой, это просто немного больно. Вы не можете псевдоним композитных типов строк с помощью
AS, поэтому вам нужно использовать псевдонимное выражение подзапроса или CTE для достижения эффект:select row_to_json(row) from ( select u.*, urd AS user_role from users u inner join ( select ur.*, d from user_roles ur inner join role_duties d on d.id = ur.duty_id ) urd(id,name,description,duty_id,duty) on urd.id = u.user_role_id ) row;производит, через http://jsonprettyprint.com/:
{ "id": 1, "name": "Dan", "email": "[email protected]", "user_role_id": 1, "user_role": { "id": 1, "name": "admin", "description": "Administrative duties in the system", "duty_id": 1, "duty": { "id": 1, "name": "Script Execution" } } }вы хотите использовать
array_to_json(array_agg(...))когда у вас есть 1:многим, кстати.приведенный выше запрос в идеале должен быть написан как:
select row_to_json( ROW(u.*, ROW(ur.*, d AS duty) AS user_role) ) from users u inner join user_roles ur on ur.id = u.user_role_id inner join role_duties d on d.id = ur.duty_id;... но в PostgreSQL
ROWконструктор не принимаетASпсевдонимы столбцов. Грустно.к счастью, они оптимизируют то же самое. Сравните планы:
- The вложенный подзапрос версия; vs
- последний вложенные
ROWконструктора версии с удаленными псевдонимами, поэтому он выполняетпотому что Ассоциация является оптимизация заборы, перефразируя вложенный подзапрос версия для использования прикован ОТВ (
WITHвыражения) может не работать, а также, и не приведет к тому же плану. В этом случае вы как бы застряли с уродливыми вложенными подзапросами, пока мы не получим некоторые улучшения вrow_to_jsonили способ переопределения имен столбцов вROWконструктор более непосредственно.
в любом случае, в общем, принцип заключается в том, что там, где вы хотите создать объект json со столбцами
a, b, c, и вы хотите, чтобы вы могли просто написать незаконный синтаксис:ROW(a, b, c) AS outername(name1, name2, name3)вместо этого вы можете использовать скалярные подзапросы, возвращающие значения типа строки:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outernameили:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outernameкроме того, имейте в виду, что вы можете сочинить
jsonзначения без дополнительной кавычки, например, если вы ставите вывод ajson_aggвнутриrow_to_jsonвнутреннийjson_aggрезультат не будет цитироваться как строка, он будет включен непосредственно как json.например, в качестве примера:
SELECT row_to_json( (SELECT x FROM (SELECT 1 AS k1, 2 AS k2, (SELECT json_agg( (SELECT x FROM (SELECT 1 AS a, 2 AS b) x) ) FROM generate_series(1,2) ) AS k3 ) x), true );выход:
{"k1":1, "k2":2, "k3":[{"a":1,"b":2}, {"a":1,"b":2}]}отметим, что
json_aggпродукт[{"a":1,"b":2}, {"a":1,"b":2}], не сбежал снова, какtextбудет.это означает, что вы можете написать в формате JSON операции для построения строк вам не всегда нужно создавать чрезвычайно сложные составные типы PostgreSQL, а затем вызывать
row_to_jsonна выходе.
мое предложение для ремонтопригодности в долгосрочной перспективе-использовать представление для построения грубой версии вашего запроса, а затем использовать функцию, как показано ниже:
CREATE OR REPLACE FUNCTION fnc_query_prominence_users( ) RETURNS json AS $$ DECLARE d_result json; BEGIN SELECT ARRAY_TO_JSON( ARRAY_AGG( ROW_TO_JSON( CAST(ROW(users.*) AS prominence.users) ) ) ) INTO d_result FROM prominence.users; RETURN d_result; END; $$ LANGUAGE plpgsql SECURITY INVOKER;в этом случае, объект внимания.пользователи-это представление. Так как я выбрал пользователей.* , Мне не нужно будет обновлять эту функцию, если мне нужно обновить представление, чтобы включить больше полей в запись пользователя.
Comments