О запросе
Вдохновленный комментарием @ a_horse Я провел быстрый тест с реальной таблицей для аналогичной цели.
- 80 категорий
- 6862items
- оптимальные индексы (на
items.catID
. Здесь больше, но не полезно.)
Три кандидата.Результаты идентичны.Планы и производительность запросов различаются.
1) Подвыбрать для каждой категории (оригинал Серегветрина)
Общее время выполнения: 20,351 мс
SELECT c.*
,(SELECT COUNT(*) FROM items i WHERE i.catid = c.id) AS item_ct
FROM category c
2) ВЛЕВО, затем ГРУППА
Общее время выполнения: 36,320 мс
SELECT c.*
,count(*) AS item_ct
FROM category c
LEFT JOIN items i ON i.catid = c.id
GROUP BY c.catid; -- prim. key of category
3) GROUP, затем LEFT JOIN
Общее время выполнения: 18,588 мс
SELECT c.*
,item_ct
FROM category c
LEFT JOIN (
SELECT catid
,count(*) AS item_ct
FROM items
GROUP BY catid
) i ON i.catid = c.id
Итак, мое первое предложение не принесло пользы.Как и ожидалось (после некоторых раздумий), версия 3) работает лучше всего.Это также имеет смысл: если сначала посчитать, а затем СОЕДИНИТЬ, то потребуется меньше операций объединения.
Разница в производительности станет более заметной для больших таблиц, особенно с большим количеством категорий.
Чтобы 2) работало, вам нужен PostgreSQL 9.1, а category.id
должен быть первичным ключом.
Для более старых версий вам придется перечислять все неагрегированные столбцы в предложении GROUP BY.
Как правило, я переключился на LEFT JOIN
, поскольку исходный запрос включает категории без связанных элементов.
Индекс items.catID
используется только в 1), когда несколько подзапросов могут получить прибыль.В других запросах последовательное сканирование выполняется быстрее: все обе таблицы должны быть прочитаны в любом случае.