postgresql中的两个嵌套连接和数组join

我以前从来没有问过这个问题,我总是通过search最终偶然发现自己正在寻找的东西,但这里有很多事情要做,而且我的search能力已经到了尽头了……谢谢你的帮助/build议。

问题

我需要随时生成报告,说明我的组织中有多less“学生”已经进行了每个“评估”,以及与每个“评估”相关的标准。

  • 学生总数提前计算并存储在assessment_report中
    • 报告行需要join“评估”才能显示信息
  • 标准有次级标准,但评估只知道次级标准。
  • 评估有多个子标准,并将它们存储在一个ID数组中。

我试图以最高性能的方式做到这一点,最大限度地减lessPostgres的负载,但我不相信我已经实现了这个目标…

安装程序

服务器:运行Ubuntu的Amazon EC2

应用程序(服务器): Node.js(0.10.26)

应用程序(客户端): Angular.js(1.2.13)

数据库(查询): PostgreSQL(9.3.1)

数据库(Cache): MongoDB (希望将来可以caching报表)

表格

SQL FIDDLE

评估

_id int4 description text category varchar(60) sub_standards int4 ARRAY ... 

assessment_report (预先计算的总和)

 assessment_id(FK) int4 client_id(FK) int4 students int4 completed int4 incomplete int4 ... 

sub_standards

 _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

EXPLAIN你的查询会产生537的最大成本。通过创build一个索引,你可以把它降低到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)来扩展它,并且在此之后不要再扩展logging集。 针对主键的查找比复杂的联接更快。 希望这可以帮助。

最终查询计划:

 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)