dwe*_*ner 73 postgresql json postgresql-9.2 scalar-subquery
我正在尝试使用row_to_json()PostgreSQL 9.2中添加的函数将查询结果映射到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$$;
Run Code Online (Sandbox Code Playgroud)
查询本身:
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;
Run Code Online (Sandbox Code Playgroud)
我发现如果我使用了ROW(),我可以将结果字段分成一个子对象,但它似乎仅限于一个级别.我不能插入更多的AS XXX语句,因为我认为在这种情况下我应该需要.
我获得了列名,因为我转换为适当的记录类型,例如::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"
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想要做的是为连接生成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"
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
任何帮助表示赞赏.谢谢阅读.
Cra*_*ger 131
更新:在PostgreSQL 9.4 中to_json,json_build_objectjson_objectjson_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;
Run Code Online (Sandbox Code Playgroud)
对于旧版本,请继续阅读.
它不仅限于一排,它只是有点痛苦.您不能使用别名复合行类型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;
Run Code Online (Sandbox Code Playgroud)
通过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"
}
}
}
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
...但PostgreSQL的ROW构造函数不接受AS列别名.可悲的是.
值得庆幸的是,他们优化了相同的.比较计划:
ROW构造函数版本,删除了别名,以便执行由于CTE是优化围栏,因此将嵌套子查询版本重新构造为使用链式CTE(WITH表达式)可能效果不佳,并且不会产生相同的计划.在这种情况下,你会遇到丑陋的嵌套子查询,直到我们得到一些改进row_to_json或ROW更直接地覆盖构造函数中的列名.
无论如何,一般来说,原则是你想用列创建一个json对象a, b, c,你希望你可以写出非法的语法:
ROW(a, b, c) AS outername(name1, name2, name3)
Run Code Online (Sandbox Code Playgroud)
您可以改为使用返回行类型值的标量子查询:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Run Code Online (Sandbox Code Playgroud)
要么:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Run Code Online (Sandbox Code Playgroud)
另外,请记住,您可以在json没有额外引用的情况下编写值,例如,如果您将a的输出json_agg放在a中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
);
Run Code Online (Sandbox Code Playgroud)
输出是:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Run Code Online (Sandbox Code Playgroud)
请注意,json_agg产品[{"a":1,"b":2}, {"a":1,"b":2}]尚未再次转义text.
这意味着你可以编写 json操作来构造行,你不必总是创建非常复杂的PostgreSQL复合类型然后调用row_to_json输出.
小智 5
我添加此解决方案是因为接受的响应未考虑 N:N 关系。又名:对象集合的集合
如果你有 N:N 关系,那么这个条款with就是你的朋友。在我的示例中,我想构建以下层次结构的树视图。
A Requirement - Has - TestSuites
A Test Suite - Contains - TestCases.
Run Code Online (Sandbox Code Playgroud)
以下查询代表连接。
SELECT reqId ,r.description as reqDesc ,array_agg(s.id)
s.id as suiteId , s."Name" as suiteName,
tc.id as tcId , tc."Title" as testCaseTitle
from "Requirement" r
inner join "Has" h on r.id = h.requirementid
inner join "TestSuite" s on s.id = h.testsuiteid
inner join "Contains" c on c.testsuiteid = s.id
inner join "TestCase" tc on tc.id = c.testcaseid
GROUP BY r.id, s.id;
Run Code Online (Sandbox Code Playgroud)
由于不能进行多次聚合,因此需要使用“WITH”。
with testcases as (
select c.testsuiteid,ts."Name" , tc.id, tc."Title" from "TestSuite" ts
inner join "Contains" c on c.testsuiteid = ts.id
inner join "TestCase" tc on tc.id = c.testcaseid
),
requirements as (
select r.id as reqId ,r.description as reqDesc , s.id as suiteId
from "Requirement" r
inner join "Has" h on r.id = h.requirementid
inner join "TestSuite" s on s.id = h.testsuiteid
)
, suitesJson as (
select testcases.testsuiteid,
json_agg(
json_build_object('tc_id', testcases.id,'tc_title', testcases."Title" )
) as suiteJson
from testcases
group by testcases.testsuiteid,testcases."Name"
),
allSuites as (
select has.requirementid,
json_agg(
json_build_object('ts_id', suitesJson.testsuiteid,'name',s."Name" , 'test_cases', suitesJson.suiteJson )
) as suites
from suitesJson inner join "TestSuite" s on s.id = suitesJson.testsuiteid
inner join "Has" has on has.testsuiteid = s.id
group by has.requirementid
),
allRequirements as (
select json_agg(
json_build_object('req_id', r.id ,'req_description',r.description , 'test_suites', allSuites.suites )
) as suites
from allSuites inner join "Requirement" r on r.id = allSuites.requirementid
)
select * from allRequirements
Run Code Online (Sandbox Code Playgroud)
它的作用是在小的项目集合中构建 JSON 对象,并将它们聚合到每个项目上with子句中。
结果:
[
{
"req_id": 1,
"req_description": "<character varying>",
"test_suites": [
{
"ts_id": 1,
"name": "TestSuite",
"test_cases": [
{
"tc_id": 1,
"tc_title": "TestCase"
},
{
"tc_id": 2,
"tc_title": "TestCase2"
}
]
},
{
"ts_id": 2,
"name": "TestSuite",
"test_cases": [
{
"tc_id": 2,
"tc_title": "TestCase2"
}
]
}
]
},
{
"req_id": 2,
"req_description": "<character varying> 2 ",
"test_suites": [
{
"ts_id": 2,
"name": "TestSuite",
"test_cases": [
{
"tc_id": 2,
"tc_title": "TestCase2"
}
]
}
]
}
]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
56498 次 |
| 最近记录: |