【问题标题】:Postgres: What's the best index for 'SELECT * FROM t WHERE a < CURRENT_TIMESTAMP ORDER BY b'?Postgres:'SELECT * FROM t WHERE a < CURRENT_TIMESTAMP ORDER BY b' 的最佳索引是什么?
【发布时间】:2019-05-24 19:12:25
【问题描述】:

查询是:

SELECT *
FROM t
WHERE a < CURRENT_TIMESTAMP
ORDER BY b

什么是最好的索引?

如果我们有另一个查询怎么办:

SELECT *
FROM t
WHERE a < CURRENT_TIMESTAMP AND c < CURRENT_TIMESTAMP
ORDER BY b

什么是最好的索引? 是否有一个索引可以同时为这两种查询提供服务?

编辑:我很抱歉在已经写了一些答案之后更改了查询。新的查询更好地反映了我的情况。

编辑:其他可能相关的详细信息:

1) 'SELECT *' 是为了简化问题而写的(实际查询不使用 *)。

2) 如果可以提高整体性能,我可以使用 LIMIT 分解查询。

【问题讨论】:

  • 你需要定义什么是CURRENT_TIMESTAMP?它改变了关于它是否只是常量的占位符,或者它是否是表 t 中的实际列的问题。此外,列的类型可能很重要,您应该提供 CREATE TABLE ddl。此外,一些样本数据和传播指示可能有助于确定最适合您的指数。
  • CURRENT_TIMESTAMP 是 Postgres 提供的神奇的“常数”,它始终等于当前时间。我不知道将这个动态系统管理的变量称为“常量”在技术上是否正确。
  • 如果您的数据大部分属于过去(即小于 current_timestamp),则没有任何索引有用。你也在使用SELECT *
  • @SalmanA,在下面的其中一个 cmets 中,他指出他正在使用限制。
  • 我写 * 是为了让问题简单,但实际实现不使用 *。大多数数据属于未来(> CURRENT_TIMESTAMP),但您的观点很好,谢谢。

标签: sql postgresql


【解决方案1】:

您的案例的最佳索引是按此顺序排列的多列索引:(a, c, b)

对于第一个查询,规划器将按a &lt; 0 进行过滤,并使用第二列进行排序(查询结束时不需要排序)。

对于第二种情况,planner 将通过a &lt; 0 and c &lt; 0 进行过滤,并使用第三列对找到的记录进行排序(也跳过最后的排序)。

Dumitrescu Bogdan 的答案有效,但最后需要一个排序过程,在第二种情况下,过滤的记录数比我的方法少。

学习多列索引的好参考:https://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys

另外,这个解释了索引如何避免最后的排序操作:https://use-the-index-luke.com/sql/sorting-grouping/indexed-order-by

更新

作者更新后,需要两个索引来避免排序操作。

对于第一种情况,您需要(a, b) 上的索引。 对于第二种情况,您将需要(a, c, b) 上的索引。使用此配置,在这两种情况下,您都可以避免最终的排序操作。

【讨论】:

  • 感谢您的回复。我在编写第二个查询时意外交换了“b”和“c”。这会改变答案吗?
  • @RayZhang,是的,它改变了答案......在这种情况下,列的顺序应该是(a,c,b)。如果您有时间,请查看我在问题中留下的链接。它很好地解释了为什么订单很重要
  • 我不认为 Postgres 可以避免使用建议的索引进行排序(对于没有以 b 作为前导列的索引的 order by b。)
  • @viniciusjssouza 我删除了反对票。我[错误地]认为过滤条件的选择性不够。
  • @spencer7593 你是对的。作者改变了问题,消除了避免对第一种情况进行排序的好处
【解决方案2】:

在这种特殊情况下,您需要的是部分索引(过滤索引)。你可以在这里找到文档:https://www.postgresql.org/docs/current/indexes-partial.html

CREATE INDEX idx___xxx ON table (b)
     WHERE a<0;

在问题已更改时进行编辑:只要 where 子句使用常量并且该常量在索引操作之前已知,此答案就很好。

【讨论】:

  • 这个答案有可取之处。不过,如果所选行数少于 5%,部分索引可能会非常有用。
  • 我原来的问题是“a
  • 这里不能使用部分索引,因为比较值会随时间变化(CURRENT_TIMESTAMP)。
【解决方案3】:

一个可能起作用的索引是:

CREATE INDEX ix_t_006 on t (a, c);

经过反复测试,我发现这是处理您的两个查询的“OK”索引。您会看到该索引处理了您的 WHERE 子句,但 ORDER BY b 没有被处理。

相反,ORDER BY 由内存中的快速排序处理。这是因为正在对列 (a, c) 进行“位图索引扫描”,这意味着任何隐含的排序顺序都将被撤消。因此,在 (a, c, b) 上创建索引将无效,因为订单将被撤消,因此,我们唯一要做的就是在 (a, c) 上创建索引。

您的问题缺少的是您将获得的结果数量以及内存中的快速排序是否令人满意。

请使用 EXPLAIN ANALYZE SELECT ... 进行验证。

第一个查询结果:

EXPLAIN ANALYZE
SELECT *
FROM t
WHERE a < CURRENT_TIMESTAMP
ORDER BY b;

 Sort  (cost=59.91..61.61 rows=680 width=12) (actual time=0.012..0.012 rows=0 loops=1)
   Sort Key: b
   Sort Method: quicksort  Memory: 25kB
   ->  Bitmap Heap Scan on t  (cost=9.42..27.92 rows=680 width=12) (actual time=0.005..0.006 rows=0 loops=1)
         Recheck Cond: (a < 0)
         ->  Bitmap Index Scan on ix_t_006  (cost=0.00..9.25 rows=680 width=0) (actual time=0.004..0.004 rows=0 loops=1)
               Index Cond: (a < 0)
 Planning time: 0.091 ms
 Execution time: 0.042 ms

第二个查询结果:

EXPLAIN ANALYZE
SELECT *
FROM t
WHERE a < CURRENT_TIMESTAMP AND c < CURRENT_TIMESTAMP
ORDER BY b;

 Sort  (cost=33.30..33.86 rows=227 width=12) (actual time=0.012..0.013 rows=0 loops=1)
   Sort Key: b
   Sort Method: quicksort  Memory: 25kB
   ->  Bitmap Heap Scan on t  (cost=11.01..24.41 rows=227 width=12) (actual time=0.006..0.006 rows=0 loops=1)
         Recheck Cond: ((a < 0) AND (c < 0))
         ->  Bitmap Index Scan on ix_t_006  (cost=0.00..10.95 rows=227 width=0)(actual time=0.005..0.005 rows=0 loops=1)
               Index Cond: ((a < 0) AND (c < 0))

经过反复测试,我发现我的答案不得不反复编辑。有一次,我发现自己想删除我的答案,因为我一直在改变它。很遗憾我没有找到对您的 ORDER BY 有帮助的答案。

我认为关键是继续尝试 EXPLAIN ANALYZE,直到你得到它。

【讨论】:

  • 感谢您的回复。我在编写第二个查询时意外交换了“b”和“c”。这会改变答案吗?
  • 雷,你更新的问题需要重写答案
  • Ray,在使用 EXPLAIN ANALYZE 测试索引后,我重写了我的整个答案。
  • 感谢您的付出。非常感谢。
  • @StephenQuan,您的方法不是最优的,因为它无法过滤行。执行程序必须扫描索引,直到找到与 a 和 c 上的过滤器匹配的行。 b 的顺序不保证 b 和 c 的顺序。此链接提供有关该主题的说明:use-the-index-luke.com/sql/sorting-grouping/indexed-order-by
【解决方案4】:

可能没有索引会比堆扫描(又名全表扫描)更好。

您不包括表统计信息,但我假设过滤条件检索超过 5% 的行(很可能)。如果是这种情况,堆扫描将比任何索引都快,并且占用的资源更少。

这些查询返回的行的百分比是多少? 50%、5%、0.5%?

【讨论】:

  • 查询在一个用例中返回
  • 那么,索引在第一种情况下很有用。在第二种情况下,可能没有索引会比堆扫描更好。
  • 当查询访问历史数据时,@TheImpaler 的答案将是正确的,除非使用了限制。但是,正如问题的作者所指出的那样,他使用了一个限制,这使得索引很有用
  • @viniciusjssouza 是不是在初始扫描或过滤完成后应用了 LIMIT,因为必须在 LIMIT 工作之前对结果进行排序?您能否阐明允许 LIMIT 保持索引有用的操作顺序?
  • @RayZhang 查询执行器将扫描收集结果的索引,直到达到您提供的限制。由于记录已经排序(按索引本身),因此无需进行最终排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 2011-04-06
  • 2021-07-06
  • 1970-01-01
  • 1970-01-01
  • 2011-11-07
相关资源
最近更新 更多