LoginSignup
5
2

More than 3 years have passed since last update.

【SQL】IndexScanしたいのに、SeqScanになってしまうのは何故か

Last updated at Posted at 2019-06-23

経緯

"完了"・"着手中"・"未着手"という3つのステータスをテーブルに設定し、いずれかのステータスを検索して絞ったテーブルを表示をするプログラムを作っておりました。
データ量が多くなった時の検索時間を短くするために、ステータスカラムにIndexを付与したのですが、どう検索してもフルスキャンになってしまいました。

結論

検索対象のデータ量に対し、約10%以下まで絞り込めるのであれば、IndexScanされる。
検索結果のデータ量が多すぎると、SeqScanになる。
 ・他にもIndexScanの条件はありますが、それだけで記事になると思います。
  もし同じ現象で悩まれている方の、1つの参考になればと思います。
  私が今回の問題にあたり、参考にさせて頂きましたリンク先を末尾に記載します。

IndexScanになるパターンとSeqScanになるパターンの実験

◆10,000データ用意
 Stateカラム
 “完了” = 1,000件
 “着手中” = 2,000件
 “未着手” = 7,000件

◆実施結果
 “完了” → Index Scan
 “着手中” → Seq Scan
 “未着手” → Seq Scan

◆explainメソッドにて実行計画の確認
・完全一致と前方一致以外だと、IndexScanがされないそうなので、今回は完全一致にて実験しました。
 今回はRansakというGemを使って、検索を行っています。

”完了”で検索(1,000 / 10,000件)

[1] pry(#<TasksController>)> @q.result(distinct: true).explain
  Task Load (3.8ms)  SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '完了'
   (pry):1
=> EXPLAIN for: SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '完了'
                                          QUERY PLAN
-----------------------------------------------------------------------------------------------
 Unique  (cost=4.31..4.33 rows=1 width=71)
   ->  Sort  (cost=4.31..4.32 rows=1 width=71)
         Sort Key: id, subject, content, created_at, updated_at, expired_at
         ->  Index Scan using index_tasks_on_state on tasks  (cost=0.29..4.30 rows=1 width=71)
               Index Cond: ((state)::text = '完了'::text)
(5 rows)

”着手中”で検索(2,000 / 10,000件)

[1] pry(#<TasksController>)> @q.result(distinct: true).explain
  Task Load (9.3ms)  SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '着手中'
   (pry):2
=> EXPLAIN for: SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '着手中'
                                  QUERY PLAN
------------------------------------------------------------------------------
 HashAggregate  (cost=305.00..325.00 rows=2000 width=70)
   Group Key: id, subject, content, created_at, updated_at, expired_at, state
   ->  Seq Scan on tasks  (cost=0.00..270.00 rows=2000 width=70)
         Filter: ((state)::text = '着手中'::text)
(4 rows)

”未着手”で検索(7,000 / 10,000件)

[1] pry(#<TasksController>)> @q.result(distinct: true).explain
  Task Load (21.2ms)  SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '未着手'
   (pry):3
=> EXPLAIN for: SELECT DISTINCT "tasks".* FROM "tasks" WHERE "tasks"."state" = '未着手'
                                  QUERY PLAN
------------------------------------------------------------------------------
 HashAggregate  (cost=392.50..462.50 rows=7000 width=70)
   Group Key: id, subject, content, created_at, updated_at, expired_at, state
   ->  Seq Scan on tasks  (cost=0.00..270.00 rows=7000 width=70)
         Filter: ((state)::text = '未着手'::text)
(4 rows)

参考リンク

【PostgreSQL】インデックス必要?explainで確認できない
https://qiita.com/gijutu/items/45c488372714eeaf6157

Railsエンジニアなら知っておきたい!RDBでindex作成を検討するときってどんなとき?
https://qiita.com/viptakechan/items/03cb15684fdace0b5bbe

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2