查询优化中最核心的问题就是精确估算不同查询计划的成本。优化器在估算查询计划的成本时,会使用一个数学模型,该模型又依赖于对每个查询计划中涉及的最大数据量的基数性(或者叫重数)的估算。而对基数性的估算又依赖于对查询中谓词选择因数(selection factor of predicates)的估算。过去,数据库系统在估算选择性时,要使用每个字段中值的分布情况的详尽统计信息,比如直方图。这种技术对于估算孤立谓词的选择符效果很好。然而,很多查询的谓词是相互关联的,例如 select count(*) from R where R.make='Honda' and R.model='Accord'。查询谓词经常会高度关联(比如,model='Accord'的前提条件是make='Honda'),而估计这种关联的选择性非常困难。查询优化器之所以会选择低劣的查询计划,一方面是因为对基数性估算不准,另一方面就是因为遗漏了很多关联性。而这也是为什么数据库管理员应该经常更新数据库统计信息(特别是在重要的数据加载和卸载之后)的原因。(译自维基百科:http://en.wikipedia.org/wiki/Query_optimizer。)
你知道PostgreSQL支持表的部分索引吗?这使得读取部分索引是非常快速的,几乎没有什么索引开销。如果你想重复地分析那些匹配给定的 WHERE 子句的行数据,那部分索引是最好的索引数据的方式。这使得PostgreSQL非常适合于那种涉及预聚集额外的特别分析的工作流。在这一点上,我将通过一个非常适合使用部分索引的查询优化示例来实践下。
若 A 和 B 两表进行左外连接, 那么结果表中将包含”左表”(即表 A)的所有记录, 即使那些记录在”右表” B 没有符合连接条件的匹配. 这意味着即使 ON 语句在 B 中的匹配项是0条, 连接操作还是会返回一条记录, 只不过这条记录的中来自于 B 的每一列的值都为 NULL. 这意味着左外连接会返回左表的所有记录和右表中匹配记录的组合(如果右表中无匹配记录, 来自于右表的所有列的值设为 NULL). 如果左表的一行在右表中存在多个匹配行, 那么左表的行会复制和右表匹配行一样的数量, 并进行组合生成连接结果.
右外连接
右外连接, 亦简称右连接, 它与左外连接完全类似, 只不过是作连接的表的顺序相反而已. 如果 A 表右连接 B 表, 那么”右表” B 中的每一行在连接表中至少会出现一次. 如果 B 表的记录在”左表” A 中未找到匹配行, 连接表中来源于 A 的列的值设为 NULL.
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (orderby cost descrowsbetweencurrentrowand2 following ) asrowfrom tcost;
注意,上面那条SQL没有写 partition by ,那默认情况下就是以整个表来表示窗口化的,即只有一个窗口。现在试着,添加上partition by 语句的结果看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (partitionby path orderby cost descrowsbetweencurrentrowand2 following ) asrowfrom tcost;
path | cost | sum_cost | row
------+-------+----------+-------
32 | 3.4 | 433.7 | 3.84
32 | 0.4 | 434.1 | 0.44
32 | 0.04 | 434.14 | 0.04
111 | 33.4 | 400.2 | 60.1
111 | 23.3 | 423.5 | 26.7
111 | 3.4 | 433.7 | 3.4
222 | 333.4 | 333.4 | 370.2
222 | 33.4 | 400.2 | 36.8
222 | 3.4 | 433.7 | 3.4
(9 rows)
test=#
当 partition by path时,可以看到,这些ROWS 模式,都是在当前所在的窗口来进行的,并不会跨窗口来进行。 所以,在这里强调一下,ROWS表示的是物理行。
RANGE
看看,当是RANGE时的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (orderby cost desc range betweencurrentrowand UNBOUNDED following ) as range from tcost;
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (orderby cost desc range betweencurrentrowand UNBOUNDED following ) as range,sum(cost) over (orderby cost descrowsbetweencurrentrowand UNBOUNDED following ) asrowfrom tcost;
[ RANGE | ROWS ] BETWEEN frame_start AND frame_end
frame_start 和 frame_end可以是:
1
2
3
4
5
UNBOUNDED PRECEDING
value PRECEDING
CURRENT ROW
value FOLLOWING
UNBOUNDED FOLLOWING
特别注意:value PRECEDING和value FOLLOWING 当前只允许ROWS模式。 RANGE模式后面只能接 UNBOUNDED FOLLOWING。
默认的框架选项是RANGE UNBOUNDED PRECEDING,该选项与 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。有ORDER BY, 它设置框架从分区的开始一直到与当前行相同的最后一行。没有ORDER BY, 那么就是当前分区的所有行都包含在框架中,因为所有行都会成为当前行的相同行。
RANGE时,请注意有没有 order by 的区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (range betweencurrentrowand UNBOUNDED following ) as no_order_by_range,sum(cost) over (orderby cost desc range betweencurrentrowand UNBOUNDED following ) as has_order_by_range from tcost;
test=# select path, cost, sum(cost) over (orderby cost desc) as sum_cost, sum(cost) over (rowsbetweencurrentrowand UNBOUNDED following ) as no_order_by_rows,sum(cost) over (orderby cost descrowsbetweencurrentrowand UNBOUNDED following ) as has_order_by_rows from tcost;