私のクエリは:
SELECT COUNT("EventType"."id") AS "eventCount", "EventType"."id" AS "EventType.id"
FROM "events" AS "Event"
INNER JOIN "event_types" AS "EventType" ON "Event"."eventTypeId" = "EventType"."id"
INNER JOIN "projects" AS "EventType->Project" ON "EventType"."projectId" = "EventType->Project"."id"
WHERE "EventType->Project"."id" = 142
GROUP BY "EventType"."id";
基本的に、特定のプロジェクトで、各タイプのイベントがいくつ発生したかを知りたいです。
関連するスキーマは次のとおりです。
Table "public.projects"
Column | Type | Modifiers
-------------------+--------------------------+---------------------------------------------------------
id | integer | not null default nextval('projects_id_seq'::regclass)
Indexes:
"projects_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "event_types" CONSTRAINT "event_types_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES projects(id) ON UPDATE CASCADE ON DELETE CASCADE
Table "public.event_types"
Column | Type | Modifiers
---------------+--------------------------+------------------------------------------------------------
id | integer | not null default nextval('event_types_id_seq'::regclass)
projectId | integer | not null
Indexes:
"event_types_pkey" PRIMARY KEY, btree (id)
"event_types_project_id" btree ("projectId")
Foreign-key constraints:
"event_types_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES projects(id) ON UPDATE CASCADE ON DELETE CASCADE
Referenced by:
TABLE "events" CONSTRAINT "events_eventTypeId_fkey" FOREIGN KEY ("eventTypeId") REFERENCES event_types(id) ON UPDATE CASCADE ON DELETE CASCADE
Table "public.events"
Column | Type | Modifiers
-------------+---------+-------------------------------------------------------
id | integer | not null default nextval('events_id_seq'::regclass)
eventTypeId | integer | not null
Indexes:
"events_pkey" PRIMARY KEY, btree (id)
"events_event_type_id" btree ("eventTypeId")
Foreign-key constraints:
"events_eventTypeId_fkey" FOREIGN KEY ("eventTypeId") REFERENCES event_types(id) ON UPDATE CASCADE ON DELETE CASCADE
EXPLAIN ANALYZE
結果は次のとおりです。
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=320957.49..320962.39 rows=490 width=12) (actual time=2612.748..2612.814 rows=459 loops=1)
Group Key: "EventType".id
-> Hash Join (cost=122.12..312038.18 rows=1783862 width=4) (actual time=386.978..2501.421 rows=690140 loops=1)
Hash Cond: ("Event"."eventTypeId" = "EventType".id)
-> Seq Scan on events "Event" (cost=0.00..239469.41 rows=14562141 width=4) (actual time=0.026..1272.817 rows=14558556 loops=1)
-> Hash (cost=116.00..116.00 rows=490 width=4) (actual time=0.323..0.323 rows=459 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 25kB
-> Nested Loop (cost=0.28..116.00 rows=490 width=4) (actual time=0.061..0.263 rows=459 loops=1)
-> Seq Scan on projects "EventType->Project" (cost=0.00..1.56 rows=1 width=4) (actual time=0.017..0.021 rows=1 loops=1)
Filter: (id = 142)
Rows Removed by Filter: 47
-> Index Scan using event_types_project_id on event_types "EventType" (cost=0.28..109.53 rows=490 width=8) (actual time=0.042..0.193 rows=459 loops=1)
Index Cond: ("projectId" = 142)
Planning time: 3.891 ms
Execution time: 2613.033 ms
イベントテーブル全体(かなり大きい)をスキャンしていて、クエリ全体にかなりの時間がかかっているようです。インデックスをスキャンするだけで済むと思っていました。私の考えでは、インデックスは各インデックスキーのカウントを維持していましたが、おそらくそのメンタルモデルに欠陥があります。
このタイプのクエリを高速化する方法はありますか?そうでない場合は自分でカウントを追跡できますが、クエリまたはスキーマで簡単に見えるものを修正することで単純化できる場合は、.
_SELECT et.id AS event_type_id, count(e."eventTypeId") AS event_count
FROM event_types et
LEFT JOIN events e ON e."eventTypeId" = et.id
WHERE et."projectId" = 142
GROUP BY et.id;
_
可能であれば、CaMeLケース名を避けて、あなたと私たちの生活をより簡単にしてください。
少なくとも、二重引用符を必要としない有効なテーブルエイリアスをクエリで使用してください。
クエリは、宣言された目的のためのコーナーケースを見逃しています。
特定のプロジェクトで、発生した各タイプのイベントの数を知りたい。
_[INNER] JOIN
_からevents
は、イベントが0のすべてのタイプを結果から除外します。通常、イベントタイプが0のイベントタイプを含む完全なリストを取得するには、_LEFT [OUTER] JOIN
_が必要です。
したがって、代わりにテーブルevents
からnull以外の列をカウントします。明白な候補はcount(e."eventTypeId")
です。タイプのイベントが見つからなかった場合(およびその場合のみ)、その列はNULLであり、count()
はNULL値をカウントしません。
参照整合性はFK制約で強制されるため、テーブルprojects
を含める必要はまったくありません。 id
があります。これで十分です。
したがって、WHERE
句を_WHERE et."projectId" = 142
_に適合させます。より短く、より速く。
理想的には、_event_types_project_id
_の既存のインデックス_("projectId")
_を_("projectId", id)
_の複数列インデックスに置き換えます。正確に同じサイズ、複数の利点。主に、テーブル_event_types
_でインデックスのみのスキャンを目指しています。見る:
そして、すでに持っているテーブルevents
の( "eventTypeId")のインデックス(_events_event_type_id
_)。
また:
インデックスをスキャンするだけで済むと思っていました。私の考えでは、インデックスは各インデックスキーのカウントを維持していましたが、おそらくそのメンタルモデルに欠陥があります。
いいえ、インデックスはカウントを保持しません。最も一般的な値などの内部統計しかありません。しかし、あなたはcan逃げるインデックスのスキャンのみ-インデックスのみのスキャンの前提条件が満たされている場合。