Ich habe eine Tabelle progresses
(enthält derzeit Hunderttausende von Datensätzen):
Column | Type | Modifiers
---------------+-----------------------------+---------------------------------------------------------
id | integer | not null default nextval('progresses_id_seq'::regclass)
lesson_id | integer |
user_id | integer |
created_at | timestamp without time zone |
deleted_at | timestamp without time zone |
Indexes:
"progresses_pkey" PRIMARY KEY, btree (id)
"index_progresses_on_deleted_at" btree (deleted_at)
"index_progresses_on_lesson_id" btree (lesson_id)
"index_progresses_on_user_id" btree (user_id)
und eine Ansicht, v_latest_progresses
die nach den neuesten progress
von user_id
und fragt lesson_id
:
SELECT DISTINCT ON (progresses.user_id, progresses.lesson_id)
progresses.id AS progress_id,
progresses.lesson_id,
progresses.user_id,
progresses.created_at,
progresses.deleted_at
FROM progresses
WHERE progresses.deleted_at IS NULL
ORDER BY progresses.user_id, progresses.lesson_id, progresses.created_at DESC;
Ein Benutzer kann für eine bestimmte Lektion viele Fortschritte erzielen. Wir möchten jedoch häufig eine Reihe der zuletzt erstellten Fortschritte für eine bestimmte Gruppe von Benutzern oder Lektionen (oder eine Kombination aus beiden) abfragen.
Die Ansicht v_latest_progresses
macht das gut und ist sogar performant, wenn ich eine Reihe von user_id
s spezifiziere :
# EXPLAIN SELECT "v_latest_progresses".* FROM "v_latest_progresses" WHERE "v_latest_progresses"."user_id" IN ([the same list of ids given by the subquery in the second example below]);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Unique (cost=526.68..528.66 rows=36 width=57)
-> Sort (cost=526.68..527.34 rows=265 width=57)
Sort Key: progresses.user_id, progresses.lesson_id, progresses.created_at
-> Index Scan using index_progresses_on_user_id on progresses (cost=0.47..516.01 rows=265 width=57)
Index Cond: (user_id = ANY ('{ [the above list of user ids] }'::integer[]))
Filter: (deleted_at IS NULL)
(6 rows)
Wenn ich jedoch versuche, dieselbe Abfrage user_id
durchzuführen und die Menge von s durch eine Unterabfrage zu ersetzen, wird dies sehr ineffizient:
# EXPLAIN SELECT "v_latest_progresses".* FROM "v_latest_progresses" WHERE "v_latest_progresses"."user_id" IN (SELECT "users"."id" FROM "users" WHERE "users"."company_id"=44);
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Merge Semi Join (cost=69879.08..72636.12 rows=19984 width=57)
Merge Cond: (progresses.user_id = users.id)
-> Unique (cost=69843.45..72100.80 rows=39969 width=57)
-> Sort (cost=69843.45..70595.90 rows=300980 width=57)
Sort Key: progresses.user_id, progresses.lesson_id, progresses.created_at
-> Seq Scan on progresses (cost=0.00..31136.31 rows=300980 width=57)
Filter: (deleted_at IS NULL)
-> Sort (cost=35.63..35.66 rows=10 width=4)
Sort Key: users.id
-> Index Scan using index_users_on_company_id on users (cost=0.42..35.46 rows=10 width=4)
Index Cond: (company_id = 44)
(11 rows)
Ich versuche herauszufinden, warum PostgreSQL die DISTINCT
Abfrage für die gesamte progresses
Tabelle ausführen möchte, bevor sie im zweiten Beispiel nach der Unterabfrage gefiltert wird.
Hätte jemand einen Rat, wie man diese Abfrage verbessern kann?
quelle
144.07..144.6
unter den 70.000, die ich bekommen habe! Vielen Dank.