Postgres和索引使用中的IS FALSE和='f'之间的差异

tib*_*bon 5 sql postgresql ruby-on-rails

在Postgres 9.6中,在一个大约有1200万行的用户表上,active布尔列上有一个btree索引.

EXPLAIN ANALYZE SELECT * FROM users WHERE active = 'f' LIMIT 1;

Limit  (cost=0.00..0.14 rows=1 width=982) (actual time=0.039..0.040 rows=1 loops=1)
  ->  Seq Scan on users  (cost=0.00..3190979.68 rows=23264168 width=982) (actual time=0.036..0.036 rows=1 loops=1)
        Filter: (NOT active)
        Rows Removed by Filter: 115
Planning time: 0.161 ms
Execution time: 0.067 ms
Run Code Online (Sandbox Code Playgroud)

但是,使用IS FALSE似乎使用索引.

EXPLAIN ANALYZE SELECT * FROM users WHERE active IS FALSE LIMIT 1;

Limit  (cost=0.44..0.59 rows=1 width=982) (actual time=0.054..0.056 rows=1 loops=1)
  ->  Index Scan using index_users_on_active on users  (cost=0.44..2059.14 rows=13183 width=982) (actual time=0.051..0.051 rows=1 loops=1)
        Index Cond: (active = false)
        Filter: (active IS FALSE)
Planning time: 0.170 ms
Execution time: 0.094 ms
Run Code Online (Sandbox Code Playgroud)

绝大多数记录,活动值是true,我理解索引并不总是更快.

似乎Rails更喜欢active = 'f'语法,因为它是构建查询时输出的内容.

为什么这些不同?他们有什么不同?是否应该使用其他?

Rob*_*bel 9

区别在于如何处理NULL值.根据文档:

  • IS FALSE 即使参数为null,也总会返回一个布尔值.
  • = 'f' 如果参数为null,则返回null.

所以说明一下,

rnubel=# SELECT NULL = 'f' as equals_false, NULL IS FALSE as is_false;

 equals_false | is_false
--------------+----------
              | f
Run Code Online (Sandbox Code Playgroud)

通常,SQL中的大多数东西都将在三元逻辑的基础上运行,其中空值产生空结果; 因此,Rails默认使用与该基础一致的运算符并不奇怪.但是,它与您在Ruby(在哪里!nil == true)的期望不同,因此它可能是导致许多意外行为的原因.