此问题与Enforcing index scan for multicolumn comparison
密切相关那里的解决方案是完美的,但似乎只有在所有索引列都具有相同顺序的情况下才有效。这个问题是不同的,因为这里的b列是 desc ,并且这个事实不再使用行语法来解决相同的问题。这就是为什么我正在寻找其他解决方案。
假设索引是为3列(a asc, b DESC, c asc)
构建的,我希望Postgres:
如果索引只有一列,则解决方法很明显:
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+列的那些复杂查询,优化器也会始终理解他应该执行范围扫描?为什么?
答案 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
符合特殊索引。