单项ANY(ARRAY [...])的索引使用情况

时间:2017-07-12 15:30:27

标签: arrays postgresql indexing postgresql-9.5 any

测试表和索引(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"

我的问题:

  1. 如果PostgreSQL足够智能以便与“IN”一起使用,那么ANY(ARRAY [])会出现什么问题?

  2. 如果我删除“ORDER BY”条款,为什么它适用于任何(ARRAY [])?

1 个答案:

答案 0 :(得分:3)

PostgreSQL不够聪明,无法确定a =ANY(ARRAY[50])a = 50相同。
它不检查数组是否只包含一个元素。

IN列表的处理方式如下(请参阅src/backend/parser/parse_expr.c中的transformAExprIn):

  1. 检查IN列表的所有元素是否为常量(请参阅here)。

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

  3. 来自2.(如果有)的表达式和IN列表的其余元素都内置在OR表达式中(请参阅here)。

  4. 例如,表达式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.cmake_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可以在第一次命中后停止扫描,并且使用第一个索引更便宜。