索引扫描以进行多列比较-索引列的非均匀排序

时间:2019-04-10 17:17:37

标签: sql postgresql indexing

此问题与Enforcing index scan for multicolumn comparison

密切相关

那里的解决方案是完美的,但似乎只有在所有索引列都具有相同顺序的情况下才有效。这个问题是不同的,因为这里的b列是 desc ,并且这个事实不再使用行语法来解决相同的问题。这就是为什么我正在寻找其他解决方案。

假设索引是为3列(a asc, b DESC, c asc)构建的,我希望Postgres:

  1. 在该B树中找到键[a = 10,b = 20,c = 30]
  2. 扫描下10个条目并返回它们。

如果索引只有一列,则解决方法很明显:

select * from table1 where a >= 10 order by a limit 10

但是,如果有更多列,解决方案将变得更加复杂。对于3列:

select * from table1
where a > 10 or (a = 10 and (b < 20 or b = 20 and c <= 30))
order by a, b DESC, c
limit 10;

如何告诉Postgres我想要此操作?

我是否可以确定,即使对于2+列的那些复杂查询,优化器也会始终理解他应该执行范围扫描?为什么?

2 个答案:

答案 0 :(得分:2)

PostgreSQL非常彻底地实现了元组(不同于Oracle,DB2,SQL Server等中发现的一半实现)。您可以使用“元组不平等”来写条件,如:

select * 
from table1
where (a, -b, c) >= (10, -20, 30)
order by a, -b, c
limit 10

请注意,由于第二列是降序排列,因此在比较期间必须“反转”其值。这就是为什么将其表示为-b以及-20的原因。对于非数字列,例如日期,varchars,LOB等,这可能会很棘手。

最后,如果创建临时索引,则-b列值仍然可以使用索引,例如:

create index ix1 on table1 (a, (-b), c);

但是,您永远不能强迫PostgreSQL使用索引。 SQL是一种声明性语言,而不是命令性语言。您可以通过保持表状态为最新状态并选择少量行来吸引。如果您的LIMIT太大,PostgreSQL可能会倾向于使用全表扫描。

答案 1 :(得分:1)

严格来说,您在(a ASC, b DESC, c ASC)上的索引仍然可以使用,但只能基于前导表达式a 。参见:

它的用途是有限的,仅当a上的谓词具有足够的选择性时(只有大约5%的行有a >= 10时,Postgres才会使用它)。 (或者可能会从可能的仅索引扫描中获利。)但是必须读取仅符合a的所有索引元组,并且您将在查询计划中看到FILTER步骤来丢弃非索引元组。合格行-都增加了额外费用。仅索引(a)上的索引通常会做得更好,因为它的索引较小且维护成本较低。

过去,我曾尝试并未能像您显示的用于ROW值比较那样充分利用索引顺序不统一(ASC | DESC)的索引。我敢肯定这是不可能的。想一想:Postgres比较整个行的值,可以比较大或小,但不能同时比较两者。

对于具有已定义取反符的数据类型,有解决方法(例如对于数字数据类型,-)。 See the solution provided by "The Impaler"!的诀窍是将值求逆并将其包装在表达式索引中,以便毕竟获得所有索引表达式的统一排序顺序-这是目前充分利用行比较潜力的唯一方法。确保同时设置 WHERE的条件,并且ORDER BY符合特殊索引。