2017-07-12 40 views
1

测试表和索引(PostgreSQL的9.5.3):与单项指标的使用ANY(ARRAY [...])

CREATE TABLE public.t (id serial, a integer, b integer); 

INSERT INTO t(a, b) 
SELECT round(random()*1000), round(random()*1000) 
FROM generate_series(1, 1000000); 

CREATE INDEX "i_1" ON public.t USING btree (a, b); 
CREATE INDEX "i_2" ON public.t USING btree (b); 

如果“A = 50”在第一个查询,一切都确定,适当的索引 “I_1” 用于:

SELECT * FROM t WHERE a = 50 ORDER BY b LIMIT 1 

"Limit (cost=0.42..4.03 rows=1 width=12) (actual time=0.085..0.085 rows=1 loops=1)" 
" Buffers: shared hit=1 read=3" 
" -> Index Scan using i_1 on t (cost=0.42..4683.12 rows=1300 width=12) (actual time=0.084..0.084 rows=1 loops=1)" 
"  Index Cond: (a = 50)" 
"  Buffers: shared hit=1 read=3" 
"Planning time: 0.637 ms" 
"Execution time: 0.114 ms" 

随着“一个IN(50)”结果是相同的:

SELECT * FROM t WHERE a IN (50) ORDER BY b LIMIT 1 

"Limit (cost=0.42..4.03 rows=1 width=12) (actual time=0.058..0.058 rows=1 loops=1)" 
" Buffers: shared hit=4" 
" -> Index Scan using i_1 on t (cost=0.42..4683.12 rows=1300 width=12) (actual time=0.056..0.056 rows=1 loops=1)" 
"  Index Cond: (a = 50)" 
"  Buffers: shared hit=4" 
"Planning time: 0.287 ms" 
"Execution time: 0.105 ms" 

问题是当我尝试使用“a = ANY(ARRAY [50])”。索引错误“I_2”代替“I_1”和执行时间变得更长X25:

SELECT * FROM t WHERE a = ANY(ARRAY[50]) ORDER BY b LIMIT 1 

"Limit (cost=0.42..38.00 rows=1 width=12) (actual time=2.591..2.591 rows=1 loops=1)" 
" Buffers: shared hit=491 read=4" 
" -> Index Scan using i_2 on t (cost=0.42..48853.65 rows=1300 width=12) (actual time=2.588..2.588 rows=1 loops=1)" 
"  Filter: (a = ANY ('{50}'::integer[]))" 
"  Rows Removed by Filter: 520" 
"  Buffers: shared hit=491 read=4" 
"Planning time: 0.251 ms" 
"Execution time: 2.627 ms" 

你可以说:“如果你使用任何(ARRAY [])PostgreSQL的不能使用索引”,但实际上它可以。如果我删除 “ORDER BY” 它再次工作:

SELECT * FROM t WHERE a = ANY(ARRAY[50]) LIMIT 1 

"Limit (cost=0.42..4.03 rows=1 width=12) (actual time=0.034..0.034 rows=1 loops=1)" 
" Buffers: shared hit=4" 
" -> Index Scan using i_1 on t (cost=0.42..4683.12 rows=1300 width=12) (actual time=0.033..0.033 rows=1 loops=1)" 
"  Index Cond: (a = ANY ('{50}'::integer[]))" 
"  Buffers: shared hit=4" 
"Planning time: 0.182 ms" 
"Execution time: 0.090 ms" 

我的问题:

  1. 如果PostgreSQL是足够聪明地用 “IN”,什么是与任何问题很好地工作(ARRAY [])?

  2. 如果我删除了“ORDER BY”子句,它为什么能与ANY(ARRAY [])一起使用?

回答

3

PostgreSQL是没有足够的智慧弄清楚,a =ANY(ARRAY[50])相同a = 50
它不检查数组是否只包含一个元素。

IN列出处理的工作原理是这样的(见src/backend/parser/parse_expr.ctransformAExprIn):如果他们是常量或没有(见here

  1. 所有IN列表中的元素进行检查。

  2. 如果有多个常量,并且它们都可以被强制转换为相同类型,则会生成一个=ANY表达式(请参见here)。

  3. 从2.(如果有)和IN列表的其余元素的表达式构建到OR表达式中(请参阅here)。

因此,例如表达x IN (1, 2, mycol)最终将成为x = ANY ('{2,3}'::integer[])) OR (x = mycol)),和x IN (42)将变得x = 42(没有OR因为列表仅具有一个元素)。

另一方面,=ANY表达式在make_scalar_array_opsrc/backend/parser/parse_oper.c中处理。没有尝试专门处理单元素阵列。

的PostgreSQL 可以使用索引扫描为=ANY条件,但除非阵列仅具有一个元素,则结果不会被b排序,所以它必须对结果进行分类。而且,由于索引扫描不能在第一个结果之后停止,所以它将不得不扫描更多的行。

要知道为什么这样的结果将不会被订购,请看下面的例子:

这是该指数的一部分:

a | b 
----+---- 
... | ... 
49 | 812 
50 | 1 
50 | 2 
50 | 595 
50 | 973 
51 | 5 
52 | 80 
52 | 991 
55 | 27 
... | ... 

现在,如果我们扫描索引a =ANY(ARRAY[50,51]),该值发现的b将依次为1,2,595,973,5,80和991。如果我们必须返回ORDER BY b的结果,我们需要额外的排序。唯一的例外是如果数组只包含一个元素,但PostgreSQL没有专门检查。

因此PostgreSQL选择扫描其他索引,因为它估计按排序顺序搜索表并停止遇到匹配=ANY条件的第一行将更便宜。

如果删除ORDER BY子句,PostgreSQL可以在两种情况下的第一次命中后停止扫描,并且使用第一个索引更便宜。

+0

为什么使用“IN”?如果这是更优化的,那么使用“IN”而不是“ANY(ARRAY [])”是否是好习惯? “结果不会按b排序” - 您能提供一些参考文件来证实这一点吗? –

+0

我已经扩展了解决这些问题的答案。 –

+0

这只是关于一个有1个元素的数组,在任何情况下,如果没有真正的原因,为什么IN的工作方式不同(在这种情况下更好)比ANY(ARRAY []),那么这很混乱。 –