Позвольте мне получить это прямо.
У вас есть стол posts
. У вас есть стол posts_types
. У этих двух есть много ко многим, чтобы присоединиться к posts_types_assignment
. И у вас есть такой медленный запрос:
SELECT count(*)
FROM posts p
JOIN posts_types_assigment pta1
ON p.id = pta1.post_id
JOIN posts_types pt1
ON pt1.id = pta1.post_type_id
AND pt1.type = 'language'
AND pt1.name = 'English'
JOIN posts_types_assigment pta2
ON p.id = pta2.post_id
JOIN posts_types pt2
ON pt2.id = pta2.post_type_id
AND pt2.type = 'tag'
AND pt2.name = 'awesome'
И вы хотели бы знать, почему это мучительно медленно.
Мое первое замечание: PostgreSQL должен был бы выполнять намного меньше работы, если бы у вас были идентификаторы в таблице posts
, а не в соединениях. Но это спорный вопрос, решение принято.
Мое более полезное замечание - я считаю, что PostgreSQL имеет оптимизатор запросов, аналогичный Oracle. В этом случае, чтобы ограничить комбинаторный рост возможных планов запросов, которые он должен учитывать, он рассматривает только планы, начинающиеся с некоторой таблицы, а затем многократно объединяет еще один набор данных за раз. Однако такой план запроса не будет работать здесь. Вы можете начать с pt1
, получить 1 запись, затем перейти к pta1
, получить набор записей, присоединиться к p
, получить такое же количество записей, затем присоединиться к pta2
, и теперь вы получите огромное количество записей, затем присоединитесь к pt2
, получите всего несколько записей. Присоединение к pta2
- это медленный шаг, потому что база данных не знает, какие записи вам нужны, и поэтому должна создать временный набор результатов для каждой комбинации сообщения и фрагмента метаданных (типа, языка или тега) на нем .
Если это действительно ваша проблема, то план right выглядит следующим образом. Присоединяйтесь pt1
к pta1
, добавьте индекс. Присоедините pt2
к pta2
, затем присоединитесь к результату первого запроса, затем присоединитесь к p
. Тогда посчитай. Это означает, что мы не получаем огромных наборов результатов.
Если это так, то нет способа сообщить оптимизатору запросов, что если вы хотите, чтобы он придумал план выполнения нового типа. Но есть способ заставить его.
CREATE TEMPORARY TABLE t1
AS
SELECT pta*
FROM posts_types pt
JOIN posts_types_assignment pta
ON pt.id = pta.post_type_id
WHERE pt.type = 'language'
AND pt.name = 'English';
CREATE INDEX idx1 ON t1 (post_id);
CREATE TEMPORARY TABLE t2
AS
SELECT pta*
FROM posts_types pt
JOIN posts_types_assignment pta
ON pt.id = pta.post_type_id
JOIN t1
ON t1.post_id = pta.post_id
WHERE pt.type = 'language'
AND pt.name = 'English';
SELECT COUNT(*)
FROM posts p
JOIN t1
ON p.id = t1.post_id;
Запрет случайных опечаток и т. Д., Скорее всего, будет лучше. Если это не так, дважды проверьте индексы в своих таблицах.