PostgreSQL 9.2 row_to_json()与嵌套连接
我试图使用在PostgreSQL 9.2中添加的row_to_json()
函数将查询的结果映射到JSON。
我无法找出将连接行表示为嵌套对象的最佳方式(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', 'someemail@gmail.com', 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
语句,因为我认为我应该在这种情况下需要。
我提供了列名称,因为我转换为相应的loggingtypes,例如对于该表的结果,使用::user_roles
。
以下是查询返回的内容:
{ "id":1, "name":"Dan", "email":"someemail@gmail.com", "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":"someemail@gmail.com", "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" } } } }
任何帮助表示赞赏。 谢谢阅读。
更新:在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
别名复合行types,所以你需要使用别名子查询expression式或者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": "someemail@gmail.com", "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
列别名。 可悲的是。
值得庆幸的是,他们对此也进行了优化。 比较计划:
- 嵌套子查询版本 ; VS
- 后者嵌套的
ROW
构造函数版本被移除,以便执行
由于CTE是优化栅栏,因此,对嵌套的子查询版本进行重新编译以使用链式CTE( WITH
expression式)可能无法执行,并且不会产生相同的计划。 在这种情况下,你会遇到丑陋的嵌套子查询,直到我们对row_to_json
一些改进,或者更直接地重写ROW
构造函数中的列名。
无论如何,一般来说,原则是,你要创build一个JSON对象与列a, b, c
,你希望你可以只写非法语法:
ROW(a, b, c) AS outername(name1, name2, name3)
您可以改为使用返回行types值的标量子查询:
(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_agg
json
值,例如,如果将json_agg
的输出放在row_to_json
,那么内部的json_agg
结果将不会被引用为string,而是直接作为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操作来构造行,您并不总是必须创build非常复杂的PostgreSQL复合types,然后在输出上调用row_to_json
。
我长期以来对可维护性的build议是使用VIEW来构build查询的粗略版本,然后使用如下的函数:
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;
在这种情况下,对象突出。用户是一个视图。 由于我select了用户。*,如果需要更新视图以在用户logging中包含更多字段,则不必更新此function。