2017-09-25 4 views
3

Postgres 9.6では、約1200万行のユーザーテーブルに、activeブール列にbtreeインデックスが付いています。Postgresとインデックスの使用では、FALSEと= 'f'の差があります。

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 

ただし、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 

レコードの大半は、アクティブな値はtrueある、と私はインデックスが常に速くないです理解しています。

Railsは、active = 'f'の構文を好んでいると思われます。これは、クエリをビルドするときに出力されるためです。

なぜこれらは異なるのですか?それらはどう違いますか?もう片方を使うべきか?

+0

はRailsが好むものに詳しく説明してくださいインデックス条件ヒット(Index Cond: (active = false))によるものであった:比較null = falseはnullを返しますという事実にもかかわらず、それは、結果セットには影響しません。 –

+0

'user.where(アクティブ:false).limit(1).to_sql'は ' SELECT "users"を返します。* "FROM" users "WHERE" users ""アクティブ "= 'f' LIMIT 1' – tibbon

答えて

7

違いはNULL値の処理方法です。パーdocs

  • IS FALSE常に引数がnullの場合でも、ブール値を返します。
  • = 'f'は、引数がnullの場合はnullを返します。

ので、

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

equals_false | is_false 
--------------+---------- 
       | f 

を説明するために一般的には、SQLのほとんどのものは、null値がnullの結果を生成する三元論理的、で動作します。したがって、Railsのデフォルトが、その根拠と一致する演算子を使用することは、驚くことではありません。しかし、それはあなたがRubyで期待しているものとは異なります(!nil == true)ので、予想外の動作の原因となっている可能性があります。

1

私はRobert Nubelの回答に反対したいと思います。実行計画で

t=> with b(v) as (values(true),(false),(null)) 
mon-> select v,v::text from b; 
v | v 
---+------- 
t | true 
f | false 
    | 
(3 rows) 

t=> with b(v) as (values(true),(false),(null)) 
select v,v::text from b where v is false; 
v | v 
---+------- 
f | false 
(1 row) 

t=> with b(v) as (values(true),(false),(null)) 
select v,v::text from b where not v;; 
v | v 
---+------- 
f | false 
(1 row) 

t=> with b(v) as (values(true),(false),(null)) 
select v,v::text from b where v = 'f'; 
v | v 
---+------- 
f | false 
(1 row) 

違いは

+0

ポイントは、2つは常に同等ではありません。たとえば、値がちょうど 'false'と' null'の場合、 'SELECT EVERY(v IS FALSE)'と 'SELECT EVERY(v = 'f')'を比較します。後者については真。その微妙な違いは、Postgresがなぜインデックスを利用していないのかを説明しています。 –

関連する問題