postgresql-with as处理子查询速度慢的问题

本文介绍了一种使用WITH AS语法来优化SQL查询的方法,特别是在需要对聚合函数结果进行过滤的情况下,相较于传统的子查询方式,该方法能显著提高查询速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在数据库查询过程中,有时候我们会遇到一些对聚合函数的结果进行过滤的情况,如果这个聚合函数本身又是个子查询获得的结果,这个时候再把它作为 一个where 条件语句的话,会导致查询效率极其的差。而通过with as 语法先把它“暂存”一下速度则会快很多。

基本用例如下:

1 我想做一个错题统计,先把错题ID和错题数查询出来

select 
	info.answerexeid AS exeid,//习题ID
	(select count	( T.answerexeid ) AS count from	w008_answer_paper_info T 
	where T.answerexeid = info.answerexeid and  T.iscorrect = 20 ) as wrongcount//错题数 
from
	w008_answer_paper_header h
	join w008_answer_paper_info info on h.ID = info.answerheaderid
	join w008_exercise_stem_info e on info.answerexeid = e.id 
	join w008_execrise_info i on info.answerexeid = i.exerciseid 
where
	h.userlongid = 2507032645091840 
	and info.iscorrect = 20

数据量不大,查询毫无压力

2 如果我想统计错误数大于12的情况,因为这个错题数是查询之后得到的结果,所以一般的做法是把当前查询结果作为一个“表”,再次查询,这次效率就慢了很多

2.1 没有where 条件

select t1.wrongcount
from
(select 
	info.answerexeid AS exeid,
	(select count	( T.answerexeid ) AS count from	w008_answer_paper_info T 
	where T.answerexeid = info.answerexeid and  T.iscorrect = 20 ) as wrongcount 
from
	w008_answer_paper_header h
	join w008_answer_paper_info info on h.ID = info.answerheaderid
	join w008_exercise_stem_info e on info.answerexeid = e.id 
	join w008_execrise_info i on info.answerexeid = i.exerciseid 
where
	h.userlongid = 2507032645091840 
	and info.iscorrect = 20
	)t1

执行结果:

2.2 加上过滤条件,这一次执行时间在30s左右,这样的数据量,这个速度就太难以接受了

select t1.wrongcount
from
(select 
	info.answerexeid AS exeid,
	(select count	( T.answerexeid ) AS count from	w008_answer_paper_info T 
	where T.answerexeid = info.answerexeid and  T.iscorrect = 20 ) as wrongcount 
from
	w008_answer_paper_header h
	join w008_answer_paper_info info on h.ID = info.answerheaderid
	join w008_exercise_stem_info e on info.answerexeid = e.id 
	join w008_execrise_info i on info.answerexeid = i.exerciseid 
where
	h.userlongid = 2507032645091840 
	and info.iscorrect = 20
	)t1
	where t1.wrongcount>12

执行结果:

 

3 改用with as 语法,耗时不到0.1秒,明显快了很多。

with	wronttab AS ( SELECT answerexeid, COUNT ( answerexeid ) AS wrongcount 
        FROM w008_answer_paper_info T WHERE iscorrect = '20' GROUP BY answerexeid )  
select 
	info.answerexeid AS exeid,
	(select count	( T.answerexeid ) AS count from	w008_answer_paper_info T 
	where T.answerexeid = info.answerexeid and  T.iscorrect = 20 ) as wrongcount 
from
	w008_answer_paper_header h
	join w008_answer_paper_info info on h.ID = info.answerheaderid
	join w008_exercise_stem_info e on info.answerexeid = e.id 
	join w008_execrise_info i on info.answerexeid = i.exerciseid 
	JOIN wronttab ON wronttab.answerexeid = info.answerexeid  
where
	h.userlongid = 2507032645091840 
	and info.iscorrect = 20
	and wronttab.wrongcount>12

执行结果:

其实with as  语法说白了,就是建了一个临时表,起到了视图的作用。避免子查询因嵌套太多导致性能慢的问题。

 

 

 

<think>嗯,用户想了解PostgreSQL处理复杂查询时的优化原理。首先,我需要回忆一下PostgreSQL的查询处理流程。根据引用[4],查询处理分为解析、重写、规划执行几个阶段。解析阶段检查语法语义,生成解析树。重写阶段处理视图规则,比如展开视图或者应用用户定义的规则,这一步可能会让查询变得更复杂或者分解成多个简单查询。 接下来是规划与优化阶段,这个部分很关键。规划器需要生成最优的执行计划。根据引用[2],优化涉及多表联接、索引选择、统计信息使用等。统计信息包括表索引的数据分布,比如最常见的值(MCV)直方图,这些信息帮助估算不同执行计划的成本。 然后要考虑成本估算。PostgreSQL使用基于成本的优化器(CBO),每个操作都有成本估算,比如顺序扫描、索引扫描、连接方法的选择(嵌套循环、哈希连接、合并连接)。规划器会根据这些成本选择总成本最低的执行计划。 索引优化也很重要,引用[1][2]都提到索引的使用对分页查询性能的影响。比如,避免大的OFFSET可以通过游标或者条件过滤来优化,而不是使用传统的LIMIT/OFFSET,这样能减少不必要的扫描。 还有内存管理,引用[3]指出优化内存分配使用需要合理设置参数,比如work_mem,避免内存不足导致的磁盘交换,同时监控内存使用情况,定期维护数据库。 用户可能还想知道具体的优化技术,比如子查询优化、连接顺序调整、物化视图的使用,或者如何利用CTE(公共表表达式)来简化复杂查询。另外,PostgreSQL的并行查询处理也是优化的一部分,可以加速大数据量的处理。 需要确保回答覆盖这些关键点,并且结构清晰,分步骤解释每个阶段的优化策略。同时,生成的相关问题要围绕用户可能进一步关心的方面,比如索引优化方法、统计信息的作用、内存参数设置等。</think>### PostgreSQL复杂查询优化原理分步解析 PostgreSQL处理复杂查询的优化原理可分为以下六个关键阶段,结合了查询重写、统计分析执行策略优化: --- #### 一、查询解析与重写 1. **语法解析**:将SQL语句转换为**解析树**,检查语法对象权限。 2. **规则重写**:根据引用[4],系统会展开视图为基表查询,应用用户定义的规则。例如: ```sql -- 视图定义 CREATE VIEW sales_summary AS SELECT product_id, SUM(quantity) FROM orders GROUP BY product_id; -- 查询视图时会被重写为 SELECT product_id, SUM(quantity) FROM orders GROUP BY product_id; ``` --- #### 二、逻辑优化与统计信息 1. **统计信息利用**: - 使用`pg_stats`系统表中的MCV(最常见值)直方图数据,估算选择率结果集大小。 - 例如对`WHERE price > 100`的条件,通过统计信息判断过滤后剩余行数。 2. **子查询优化**: - 将相关子查询转换为`JOIN`操作,减少嵌套层级。 - 例如将`WHERE EXISTS (SELECT ...)`优化为半连接(Semi-Join)。 --- #### 三、物理计划生成 1. **成本模型计算**: - 每个操作的成本公式为: $$ \text{Cost} = \text{seq_page_cost} \times N_{\text{pages}} + \text{cpu_tuple_cost} \times N_{\text{rows}} $$ - 索引扫描成本计算: $$ \text{IndexCost} = \text{random_page_cost} \times N_{\text{index_pages}} + \text{cpu_index_cost} \times N_{\text{rows}} $$ 2. **连接策略选择**: | 连接类型 | 适用场景 | 成本特点 | |----------------|-----------------------------------|------------------------| | 嵌套循环 | 小表驱动大表 | 适合带索引的驱动表 | | 哈希连接 | 中等数据量、无索引 | 需内存构建哈希表 | | 合并连接 | 已排序数据集 | 要求输入数据有序 | --- #### 四、执行阶段优化 1. **内存控制**: - 通过`work_mem`参数限制排序哈希操作的内存使用,超限时转为磁盘临时文件[^3]。 - 示例:设置`SET work_mem = '64MB';`优化排序操作。 2. **并行查询**: - 对全表扫描或大表聚合操作,使用`max_parallel_workers`分配并行线程。 --- #### 五、高级优化技术 1. **分页优化**: - 避免`OFFSET 10000`类操作,改用`WHERE id > last_seen_id LIMIT 20`的条件分页[^1]。 2. **CTE优化**: - 使用物化CTE(`WITH MATERIALIZED`)缓存中间结果,避免重复计算。 --- #### 六、监控与调优工具 1. **执行计划分析**: ```sql EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM large_table WHERE category_id = 5; ``` - 输出包含实际行数、内存/磁盘读取量等关键指标。 2. **索引建议**: - 使用`pg_stat_all_indexes`查看索引使用频率,删除冗余索引。 --- ### 优化效果对比示例 | 优化前查询时间 | 优化手段 | 优化后查询时间 | |----------------|------------------------|----------------| | 1200ms | 添加复合索引 | 85ms | | 800ms | 改写子查询为JOIN | 210ms | | 3500ms | 使用游标分页 | 90ms | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值