当我使用“或”条件时,为啥我的查询使用过滤而不是索引条件?

Posted

技术标签:

【中文标题】当我使用“或”条件时,为啥我的查询使用过滤而不是索引条件?【英文标题】:Why is my query uses filtering instead of index cond when I use an `OR` condition?当我使用“或”条件时,为什么我的查询使用过滤而不是索引条件? 【发布时间】:2021-11-12 18:50:23 【问题描述】:

我在 PostgreSQL 中有一个 transactions 表,其中 block_heightindex 作为 BIGINT 值。这两个值用于确定此表中事务的顺序。

因此,如果我想从该表中查询给定 block_heightindex 之后的事务,我必须将其置于条件

如果两个事务在同一个block_height 中,则检查它们的index 的顺序 否则比较他们的block_height

例如,如果我想获得 10 个在 block_height 100000index 5 之后发生的事务:

SELECT * FROM transactions 
WHERE (
  (block_height = 10000 AND index > 5)
  OR (block_height > 10000)
)
ORDER BY block_height, index ASC
LIMIT 10

但是我发现这个查询非常慢,对于一个有 5000 万行的表,它最多需要 60 秒。

但是,如果我拆分条件并像这样单独运行它们:

SELECT * FROM transactions 
WHERE block_height = 10000 AND index > 5 
ORDER BY block_height, index ASC
LIMIT 10

SELECT * FROM transactions 
WHERE block_height > 10000
ORDER BY block_height, index ASC
LIMIT 10

两个查询最多在同一张表上使用200ms!执行两个查询然后UNION 最终结果比在条件中添加OR 要快得多。

这是慢查询(OR-ed 条件)的查询计划的一部分:

  ->  Nested Loop  (cost=0.98..11689726.68 rows=68631 width=73) (actual time=10230.480..10234.289 rows=10 loops=1)
        ->  Index Scan using src_transactions_block_height_index on src_transactions  (cost=0.56..3592792.96 rows=16855334 width=73) (actual time=10215.698..10219.004 rows=1364 loops=1)
              Filter: (((block_height = $1) AND (index > $2)) OR (block_height > $3))
              Rows Removed by Filter: 2728151

这是快速查询的查询计划:

  ->  Nested Loop  (cost=0.85..52.62 rows=1 width=73) (actual time=0.014..0.014 rows=0 loops=1)
        ->  Index Scan using src_transactions_block_height_index on src_transactions  (cost=0.43..22.22 rows=5 width=73) (actual time=0.014..0.014 rows=0 loops=1)
              Index Cond: ((block_height = $1) AND (index > $2))

我认为主要区别在于查询计划之间使用Filter 而不是Index Cond

有没有什么方法可以在不使用UNION 解决方法的情况下以高效的方式执行此查询?

【问题讨论】:

or 经常扼杀索引的使用。我能说什么?习惯它。您了解一种解决方法,即使用单独的子查询。 【参考方案1】:

block_height 与您知道恰好相等的两个不同参数进行比较的事实可能是一个问题。如果您两次使用 $1,而不是 $1 和 $3,会怎样?

但更好的是,尝试一个元组比较

WHERE (block_height, index) > (10000, 5)

使用(block_height, index) 上的两列索引可以加快速度。

【讨论】:

完美!非常感谢,我不知道你可以进行元组比较。使用这个比较,我的查询在同一张表上运行只需 190 毫秒。

以上是关于当我使用“或”条件时,为啥我的查询使用过滤而不是索引条件?的主要内容,如果未能解决你的问题,请参考以下文章

sqlwhere后可以有多个条件后再嵌套吗

在条件动态过滤的选择位置中嵌套“ifs”

为啥当我使用 IIF 时,我的表中的某些行而不是其他行的日期转换失败

为啥当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not All

MS Access 如何计算过滤报告上的唯一记录或值

在 linq 查询中按条件过滤