postgresql 中的两个嵌套连接和数组连接
Posted
技术标签:
【中文标题】postgresql 中的两个嵌套连接和数组连接【英文标题】:postgresql two nested joins and arrays in join 【发布时间】:2014-05-31 22:12:02 【问题描述】:我以前从来没有问过这样的问题,我最终总是通过搜索偶然发现我正在寻找的东西,但是这里发生了很多事情,我已经到了搜索能力的尽头。 . 感谢您的帮助/建议。
问题
我需要即时生成报告,说明我的组织中有多少“学生”参加了每个“评估”,以及与每个“评估”相关的标准。
提前计算学生总数并存储在评估报告中 报告行需要与“评估”连接才能显示信息 标准有子标准,但评估只知道子标准。 评估有多个子标准,并将它们存储在一个 id 数组中。我正在尝试以最高效的方式来最小化 Postgres 的负载,但我不相信我已经实现了这个目标......
设置
服务器:运行 Ubuntu 的 Amazon EC2
应用(服务器): Node.js (0.10.26)
应用程序(客户端): Angular.js (1.2.13)
数据库(查询): PostgreSQL (9.3.1)
数据库(缓存): MongoDB (希望以后缓存报告)
桌子
SQL FIDDLE
评估
_id int4
description text
category varchar(60)
sub_standards int4 ARRAY
...
assesments_report(预先计算的总和)
assessment_id(FK) int4
client_id(FK) int4
students int4
completed int4
incomplete int4
...
子标准
_id
standard_id(FK) int4
description varchar(255)
...
标准
_id int4
name varchar(60)
description varchar(255)
...
查询
//Stored as array for readability in Node-Postgres use
SELECT r.*, as.* FROM assessments_report r
INNER JOIN (
SELECT a._id AS assessment_id, a.description, a.category, a.states,
array_to_json(array_agg(ss.*)) AS standards
FROM assessments a LEFT JOIN (
SELECT ss.*, s.name AS parent_name, s.description AS parent_description
FROM sub_standards ss
INNER JOIN standards s ON ss.standard_id = s._id
) ss ON ss._id = ANY (a.sub_standards) GROUP BY a._id
) as
ON as.assessment_id = r.assessment_id
WHERE r.client_id = $1
每行所需的输出(显示为 JSON)
assessment_id: 2,
students: 2,
complete: 1
incomplete: 1,
description: "...",
category: "...",
states: ["AL","AZ",...],
standards: [
_id: 1,
standard_id: 3,
description: "...",
parent_name: "...",
parent_description: "..."
,
_id: 2,
standard_id: 4,
description: "...",
parent_name: "...",
parent_description: "..."
,
]
SQL FIDDLE
【问题讨论】:
为了有效地帮助优化查询,可用模式 + 示例数据(理想情况下是 sqlfiddle.com)将是非常可取的。您还应该为当前查询显示explain analyze
输出。
@CraigRinger 感谢您的来电!我创建了the fiddle 并将其添加到问题中。在其中,我更具体地了解了我的设置和未来的改进/更改,如果这有帮助,请告诉我。
【参考方案1】:
EXPLAIN
的查询产生的最大成本为 537。您可以通过创建索引将其降低到 59(提高 10 倍):
CREATE INDEX idx_sstandard_sid ON sub_standards (standard_id);
但这更快,成本为 21:
WITH r AS (SELECT * FROM assessments_report WHERE client_id = $1)
, ss AS (SELECT r.*, a.description, a.category, a.states , a.sub_standards
FROM r INNER JOIN assessments a ON a._id = r.assessment_id)
, ss3 AS (SELECT ss2.*, s.name parent_name, s.description parent_description
FROM sub_standards ss2
INNER JOIN standards s
ON ss2.standard_id = s._id)
, ss1 AS (SELECT ss.*, array_to_json(array_agg(ss3.*)) standards
FROM ss
LEFT JOIN ss3
ON ss3._id = ANY (ss.sub_standards)
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14 )
SELECT _id, client_id, region_id, group_id, assessment_id, students, basic,
proficient, advanced, in_progress, description, category, states, standards
FROM ss1;
您可能会摆脱分组并比这更快。
基本思想是从最有限的约束 (client_id) 开始,通过下一个标准 (sub_standards) 对其进行扩展,之后不再扩展记录集。针对主键的查找比复杂的连接要快。 希望这会有所帮助。
最终查询计划:
CTE Scan on ss1 (cost=21.79..21.81 rows=1 width=434)
CTE r
-> Seq Scan on assessments_report (cost=0.00..1.06 rows=1 width=40)
Filter: (client_id = 4)
CTE ss
-> Nested Loop (cost=0.14..8.19 rows=1 width=434)
-> CTE Scan on r (cost=0.00..0.02 rows=1 width=40)
-> Index Scan using assesments_pkey on assessments a (cost=0.14..8.16 rows=1 width=398)
Index Cond: (_id = r.assessment_id)
CTE ss3
-> Hash Join (cost=1.07..12.34 rows=3 width=1346)
Hash Cond: (s._id = ss2.standard_id)
-> Seq Scan on standards s (cost=0.00..10.90 rows=90 width=818)
-> Hash (cost=1.03..1.03 rows=3 width=532)
-> Seq Scan on sub_standards ss2 (cost=0.00..1.03 rows=3 width=532)
CTE ss1
-> HashAggregate (cost=0.19..0.20 rows=1 width=462)
-> Nested Loop Left Join (cost=0.00..0.15 rows=1 width=462)
Join Filter: (ss3._id = ANY (ss.sub_standards))
-> CTE Scan on ss (cost=0.00..0.02 rows=1 width=434)
-> CTE Scan on ss3 (cost=0.00..0.06 rows=3 width=32)
【讨论】:
感谢您的帮助!自从我最初提出这个问题以来,查询已经发生了一些变化,但我会尽快评估您的解决方案,并在确认我的预期输出后接受答案。还有我没有在我的 SQL Fiddle 上创建索引的错误,我的本地数据库中确实有该索引。以上是关于postgresql 中的两个嵌套连接和数组连接的主要内容,如果未能解决你的问题,请参考以下文章
如何从 postgresql 10.3 中的这个多重连接查询中删除嵌套循环
TransactionScope:具有不同数据库连接的嵌套事务(SQL Server 和 Postgresql)