为什么Postgresql不使用索引进行IN查询?

时间:2014-01-17 08:24:47

标签: sql postgresql

我有一个表social_accounts,其中facebook_id列的部分索引位于user_id IS NULL

如果我执行简单查询WHERE facebook_id = '123',则使用索引:

 => EXPLAIN for: SELECT "social_accounts".* FROM "social_accounts"  WHERE (user_id IS NOT NULL) AND "social_accounts"."facebook_id" = '123'
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Index Scan using index_social_accounts_on_facebook_id on social_accounts  (cost=0.00..8.28 rows=1 width=345)
   Index Cond: ((facebook_id)::text = '123'::text)
   Filter: (user_id IS NOT NULL)

但如果我使用IN进行查询,则不使用索引:

 => EXPLAIN for: SELECT "social_accounts".* FROM "social_accounts"  WHERE (user_id IS NOT NULL) AND "social_accounts"."facebook_id" IN ('123', '456')
                                            QUERY PLAN
---------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on social_accounts  (cost=8.53..16.36 rows=2 width=345)
   Recheck Cond: ((facebook_id)::text = ANY ('{123,456}'::text[]))
   Filter: (user_id IS NOT NULL)
   ->  Bitmap Index Scan on index_social_accounts_on_facebook_id  (cost=0.00..8.52 rows=2 width=0)
         Index Cond: ((facebook_id)::text = ANY ('{123,456}'::text[]))
(5 rows)

为什么不在第二种情况下使用索引?有什么方法可以加快这个问题吗?

(请注意,对于此示例,我已经截断了数组,并且我已经测试了更多元素,但结果相同,结果很慢)

1 个答案:

答案 0 :(得分:4)

实际上, 使用索引。只是这样做不同。

索引扫描逐个访问行,以随机顺序从一个磁盘页面来回传递。

位图索引扫描首先过滤要访问的磁盘页面,然后按顺序逐个访问后者。重新检查cond是因为,在每个页面中,您需要过滤掉无效的行。

对于少量行,索引扫描最便宜。对于更多行,位图索引扫描变得最便宜。对于更大数量的行,seq扫描最终变得最便宜。