测试表和索引(PostgreSQL 9.5.3):
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"
你可以说:“如果你使用ANY(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"
我的问题:
如果PostgreSQL足够智能以便与“IN”一起使用,那么ANY(ARRAY [])会出现什么问题?
如果我删除“ORDER BY”条款,为什么它适用于任何(ARRAY [])?
答案 0 :(得分:3)
PostgreSQL不够聪明,无法确定a =ANY(ARRAY[50])
与a = 50
相同。
它不检查数组是否只包含一个元素。
IN
列表的处理方式如下(请参阅src/backend/parser/parse_expr.c
中的transformAExprIn
):
检查IN
列表的所有元素是否为常量(请参阅here)。
如果有多个常量,并且它们都可以强制转换为相同类型,则构建=ANY
表达式(请参阅here)。
来自2.(如果有)的表达式和IN
列表的其余元素都内置在OR
表达式中(请参阅here)。
例如,表达式x IN (1, 2, mycol)
将以x = ANY ('{2,3}'::integer[])) OR (x = mycol))
结束,x IN (42)
将变为x = 42
(没有OR
因为列表只有一个元素)。
另一方面,=ANY
表达式在src/backend/parser/parse_oper.c
中make_scalar_array_op
处理。没有尝试专门处理单元素数组。
PostgreSQL 可以对=ANY
条件使用索引扫描,但除非该数组只有一个元素,否则结果将不会被b
<排序/ strong>,因此必须对结果进行排序。此外,由于索引扫描在第一次结果后无法停止,因此必须扫描更多行。
要了解为什么不会订购此类结果,请考虑以下示例:
这是索引的一部分:
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,8和991。如果我们必须使用ORDER BY b
返回结果,我们需要另外一种排序。唯一的例外是如果数组只包含一个元素,但PostgreSQL没有专门检查它。
因此,PostgreSQL选择扫描其他索引,因为它估计以排序顺序搜索表并在遇到匹配=ANY
条件的第一行时停止将更便宜。
如果删除ORDER BY
子句,PostgreSQL可以在第一次命中后停止扫描,并且使用第一个索引更便宜。