оптимизировать запрос в PostgreSQL - PullRequest
3 голосов
/ 18 апреля 2011
SELECT count(*) 
FROM contacts_lists 
     JOIN plain_contacts 
          ON contacts_lists.contact_id = plain_contacts.contact_id 
     JOIN contacts 
          ON contacts.id = plain_contacts.contact_id 
WHERE plain_contacts.has_email 
      AND NOT contacts.email_bad 
      AND NOT contacts.email_unsub 
      AND contacts_lists.list_id =67339

как я могу оптимизировать этот запрос .. не могли бы вы объяснить ...

Ответы [ 5 ]

7 голосов
/ 08 мая 2011

Переформатирование вашего плана запроса для ясности:

QUERY PLAN Aggregate (cost=126377.96..126377.97 rows=1 width=0)
  -> Hash Join (cost=6014.51..126225.38 rows=61033 width=0)
     Hash Cond: (contacts_lists.contact_id = plain_contacts.contact_id)
    -> Hash Join (cost=3067.30..121828.63 rows=61033 width=8)
       Hash Cond: (contacts_lists.contact_id = contacts.id)
      -> Index Scan using index_contacts_lists_on_list_id_and_contact_id
         on contacts_lists (cost=0.00..116909.97 rows=61033 width=4)
         Index Cond: (list_id = 66996)
         -> Hash (cost=1721.41..1721.41 rows=84551 width=4)
         -> Seq Scan on contacts (cost=0.00..1721.41 rows=84551 width=4)
            Filter: ((NOT email_bad) AND (NOT email_unsub))
            -> Hash (cost=2474.97..2474.97 rows=37779 width=4)
            -> Seq Scan on plain_contacts (cost=0.00..2474.97 rows=37779 width=4)
               Filter: has_email

Два частичных индекса может исключить последующее сканирование в зависимости от вашего распределения данных:

-- if many contacts have bad emails or are unsubscribed:
CREATE INDEX contacts_valid_email_idx ON contacts (id)
WHERE (NOT email_bad AND NOT email_unsub);

-- if many contacts have no email:
CREATE INDEX plain_contacts_valid_email_idx ON plain_contacts (id)
WHERE (has_email);

Возможно, вам не хватает индекса по внешнему ключу:

CREATE INDEX plain_contacts_contact_id_idx ON plain_contacts (contact_id);

И последнее, но не менее важное: если вы никогда не анализировали свои данные, вам нужно выполнить:

VACUUM ANALYZE;

Если после того, как все это будет сделано, это все еще будет происходить медленно, вы не сможете ничего сделать, если не объединить свои таблицы plain_contacts и таблицы контактов: получение вышеуказанного плана запросов, несмотря на вышеприведенные индексы, означает, что большинство / все ваши подписчики подписался на этот конкретный список - в этом случае приведенный выше план запроса будет самым быстрым.

4 голосов
/ 18 апреля 2011

Это уже очень простой запрос, который позволяет наиболее эффективно выполнять базу данных при условии актуальности статистики

Так что с точки зрения самого запроса делать особо нечего.

С точки зрения администрирования базы данных вы можете добавлять индексы - в базе данных должны быть индексы для всех условий соединения, а также для наиболее селективной части предложения where (list_id, contact_id как FK в plain_contacts и contacts_lists). Это наиболее значимая возможность повысить производительность этого запроса (на несколько порядков). Тем не менее, как отмечает SpliFF, у вас, вероятно, уже есть эти индексы, поэтому проверьте.

Кроме того, у postgres есть хорошая команда объяснение , которую вы должны изучить и использовать. Это поможет с оптимизацией запросов.

1 голос
/ 23 апреля 2011

Вы недавно запускали ANALYZE для базы данных? Считают ли строки в плане EXPLAIN, что они имеют смысл? (Похоже, вы запустили только EXPLAIN. EXPLAIN ANALYZE дает как приблизительные, так и фактические сроки.)

1 голос
/ 19 апреля 2011

Поскольку вы хотите включить только те строки, для которых в объединенных таблицах установлены некоторые флаги, я бы переместил эти операторы в предложение join:

SELECT count(*) 
FROM contacts_lists 
     JOIN plain_contacts 
          ON contacts_lists.contact_id = plain_contacts.contact_id 
          AND NOT plain_contacts.has_email
     JOIN contacts 
          ON contacts.id = plain_contacts.contact_id 
          AND NOT contacts.email_unsub 
          AND NOT contacts.email_bad 
WHERE contacts_lists.list_id =67339

Я не уверен, что это сильно повлияет на производительность, но стоит попробовать. Вероятно, для оптимальной производительности у вас также должны быть индексы для объединенных таблиц, например:

plain_contacts: contact_id, has_email
contacts: id, email_unsub, email_bad
0 голосов
/ 18 апреля 2011

Вы можете использовать SELECT count(1) ..., но в остальном я бы сказал, что все выглядит хорошо. Вы всегда можете кэшировать некоторые части запроса, используя представления или помещая индексы в contact_id и list_id, если вы действительно испытываете затруднения (я предполагаю, что у вас уже есть одна для id).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...