这个问题与Enforcing index scan for multicolumn comparison密切相关
解决方案是完美的,但似乎只有在所有索引列具有相同排序时才有效。这个问题是不同的,因为列b在这里描述,并且这个事实停止使用行语法来解决相同的问题。这就是为什么我在寻找另一种解决方案。
假设索引是为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列以上的复杂查询,优化器也会始终了解他应该执行范围扫描?为什么?
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可能倾向于使用全表扫描。
严格地说,您仍然可以使用(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
符合特殊指数。