select 1 from workdays day where day.date_day >= '2014-10-01' and day.date_day <= '2015-09-30' and day.offer_id in ( select offer.offer_day from offer inner join province on offer.id_province = province.id_province inner join center cr on cr.id_cr = province.id_cr where upper(offer.code_status) <> 'A' and province.id_region in ('10','15','21','26','31',...,'557') and province.id_cr in ('9','14','20','25','30','35','37') )
在子查询之前执行日期范围的WHERE子句吗?
为了更快地执行,是否首先放置最严格的条款以避免其他子句的大循环是一种好方法?
现在查询需要花费很多时间来执行.
Postgresql并不关心你写什么顺序
> Postgresql根本不关心WHERE子句中条目的顺序,只选择基于成本和选择性估计的索引和执行顺序.
>在配置的join_collapse_limit之前,也会忽略写入连接的顺序;如果有更多的连接,它将按照它们写入的顺序执行它们.
>子查询可以在包含它们的查询之前或之后执行,具体取决于最快的子查询,只要子查询在外部查询实际需要信息之前执行即可.实际上,子查询通常在中间执行,或与外部查询交错.
>无法保证Postgresql实际上会执行部分查询.它们可以完全优化.如果您调用具有副作用的函数,这一点很重要.
Postgresql将严重转换查询,同时保留完全相同的效果,以便在不改变结果的情况下使它们运行得更快.
>子查询之外的术语可以被推入子查询中,因此它们作为子查询的一部分执行,而不是在外部查询中编写它们的位置
>子查询中的术语可以拉到外部查询,因此它们的执行是作为外部查询的一部分完成的,而不是在子查询中写入它们的位置
>子查询可以并且通常在外表上展平为连接.像EXISTS和NOT EXISTS查询这样的事情也是如此.
>视图变得扁平化为使用视图的查询
> sql函数经常被内联到调用查询中
> …还有许多其他的查询转换,例如常量表达式预评估,一些子查询的去相关以及各种其他规划器/优化器技巧.
通常,Postgresql可以大规模地转换和重写您的查询,以至于每个查询都是:
select my_table.* from my_table left join other_table on (my_table.id = other_table.my_table_id) where other_table.id is null; select * from my_table where not exists ( select 1 from other_table where other_table.my_table_id = my_table.id ); select * from my_table where my_table.id not in ( select my_table_id from other_table where my_table_id is not null );
通常会产生完全相同的查询计划. (假设我上面没有做出任何愚蠢的错误).
尝试优化查询只是为了发现查询计划程序已经找到了您正在尝试的技巧并自动应用它们,这种情况并不少见,因此手动优化版本并不比原始版本好.
限制
规划器/优化器远非全局,并且受限于绝对确定它不能改变查询效果,可用于决策的数据,已实施的规则以及cpu时间的要求.它可以花钱思考优化.例如:
>计划员依赖于ANALYZE保存的统计数据(通常通过autovacuum).如果这些已经过时,那么计划选择可能会很糟糕.
>统计数据只是一个样本,因此可能会因采样效应而产生误导,特别是如果采样量太小.可能导致糟糕的计划选择.
>统计信息不会跟踪有关表格的某些类型的数据,例如列之间的相关性.这可能会导致规划人员在假设事情是独立的时候做出错误的决定.
>规划器依赖于诸如random_page_cost之类的成本参数来告诉它在其安装的特定系统上的各种操作的相对速度.这些只是指南.如果他们严重错误,他们可能导致糟糕的计划选择.
>任何带有LIMIT或OFFSET的子查询都不能展平或受到上拉/下推的影响.这并不意味着它将在外部查询的所有部分之前执行,或者甚至根本不执行.
> CTE术语(WITH查询中的子句)总是完整执行,如果它们完全执行的话.它们不能扁平化,并且不能在CTE术语障碍中推高或拉低术语. CTE术语始终在最终查询之前执行.这是非sql标准行为,但它记录为Postgresql的工作方式.
> Postgresql在外表,security_barrier视图和某些其他特殊关系上的查询之间进行优化的能力有限
> Postgresql不会内联除了普通sql之外的任何函数,也不会在它与外部查询之间进行pullup / pushdown.
>规划器/优化器对于选择表达式索引以及索引和表达式之间的琐碎数据类型差异非常愚蠢.
吨也是.
您的查询
如果您的查询:
select 1 from workdays day where day.date_day >= '2014-10-01' and day.date_day <= '2015-09-30' and day.offer_id in ( select offer.offer_day from offer inner join province on offer.id_province = province.id_province inner join center cr on cr.id_cr = province.id_cr where upper(offer.code_status) <> 'A' and province.id_region in ('10','37') )
没有什么可以阻止它被平铺成一个带有额外连接集的简单查询,而且很可能会.
它可能会变成像(未经测试的,显然):
select 1 from workdays day inner join offer on day.offer_id = offer.offer_day inner join province on offer.id_province = province.id_province inner join center cr on cr.id_cr = province.id_cr where upper(offer.code_status) <> 'A' and province.id_region in ('10','557') and province.id_cr in ('9','37') and day.date_day >= '2014-10-01' and day.date_day <= '2015-09-30';
然后,Postgresql将根据其选择性和行计数估计值以及可用索引来优化连接顺序和连接方法.如果这些合理地反映了现实,那么它将进行连接并以最佳顺序运行where子句条目 – 通常将它们混合在一起,所以它做了一点,然后一点,然后回到第一部分等
如何查看优化器的功能
您无法看到Postgresql优化您的查询的sql,因为它将sql转换为内部查询树表示然后修改它.您可以转储查询计划并将其与其他查询进行比较.
http://explain.depesz.com/有一个不错的查询计划助手.如果你对查询计划等完全不熟悉(在这种情况下,我很惊讶你通过这篇文章做到了这一点),那么PgAdmin有一个图形查询计划查看器,它提供的信息少得多,但更简单.
相关阅读:
> Optimizer
> Query planning parameters
> EXPLAIN
> CTEs (WITH
queries)
> My blog on CTEs as optimisation fences
下推/上拉和展平功能continue to improve in each release. Postgresql通常适用于上拉/下推/展平决策,但并非总是如此,所以偶尔你必须(ab)使用CTE或OFFSET 0 hack.如果您发现这种情况,请报告查询计划程序错误.
如果你真的非常非常热衷于使用debug_print_plans选项来查看原始查询计划,但我保证你不想阅读它.真.