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

问题描述 投票:2回答:2

这个问题与Enforcing index scan for multicolumn comparison密切相关

解决方案是完美的,但似乎只有在所有索引列具有相同排序时才有效。这个问题是不同的,因为列b在这里描述,并且这个事实停止使用行语法来解决相同的问题。这就是为什么我在寻找另一种解决方案。

假设索引是为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列以上的复杂查询,优化器也会始终了解他应该执行范围扫描?为什么?

sql postgresql indexing
2个回答
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等,这可能很棘手。

最后,如果您创建ad-hoc索引,则仍然可以使用-b列值来使用索引,例如:

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

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


1
投票

严格地说,您仍然可以使用(a ASC, b DESC, c ASC)上的索引,但仅基于主要表达式a。看到:

它的用处是有限的,如果单独使用a上的谓词足够有选择性(低于大约5%的行具有a >= 10),Postgres将仅使用它。 (或者可能在可能的情况下从仅索引扫描中获利。)但是必须读取仅限于a的所有索引元组,并且您将在查询计划中看到FILTER步骤以丢弃不合格的行 - 这两者都增加了额外的成本。只有(a)的指数通常做得更好,因为它更小,维护成本更低。

我曾经尝试过并且失败了,充分利用了非均匀排序顺序(ASC | DESC)的索引,就像你显示的ROW值比较一样。我很确定这是不可能的。想一想:Postgres比较整行值,它可以更大或更小,但不能同时进行。

有定义的否定数据类型的解决方法(如数字数据类型的-)。 See the solution provided by "The Impaler"!诀窍是反转值并将其包装在表达式索引中,以便在所有索引表达式之后得到统一的排序顺序 - 这是目前唯一可以充分利用行比较的方法。一定要使WHERE条件和ORDER BY符合特殊指数。

© www.soinside.com 2019 - 2024. All rights reserved.