Запрос:подсчитайте несколько агрегатов для каждого элемента
-
07-07-2019 - |
Вопрос
Часто вам нужно показать список элементов базы данных и определенные сводные данные о каждом элементе.Например, когда вы вводите текст заголовка в Stack Overflow, появляется список Связанных вопросов.В списке показаны названия связанных записей и единое агрегированное количество ответов по каждому названию.
У меня похожая проблема, но мне нужно несколько агрегатов.Я бы хотел отобразить список элементов в любом из 3 форматов в зависимости от пользовательских настроек:
- Название моего товара (всего 15, 13 принадлежат мне)
- Название моего товара (всего 15)
- Название моего товара (13 принадлежат мне)
Моя база данных - это:
- Товары:Идентификатор элемента, имя_элемента, идентификатор владельца
- Категории:Идентификатор кошки, кошАчье имя
- Карта:mapID, ItemId, catId - Идентификатор объекта
Приведенный ниже запрос получает:название категории, количество идентификаторов товаров в каждой категории
SELECT
categories.catName,
COUNT(map.itemId) AS item_count
FROM categories
LEFT JOIN map
ON categories.catId = map.catId
GROUP BY categories.catName
Этот получает:название категории, количество идентификаторов товаров в каждой категории только для этого owner_id
SELECT categories.catName,
COUNT(map.itemId) AS owner_item_count
FROM categories
LEFT JOIN map
ON categories.catId = map.catId
LEFT JOIN items
ON items.itemId = map.itemId
WHERE owner = @ownerId
GROUP BY categories.catId
Но как мне получить их одновременно в одном запросе?То есть.:название категории, количество идентификаторов товаров в категории, количество идентификаторов товаров в категории только для этого owner_id
Бонус.Как я могу при необходимости получить только там, где catId count != 0 для любого из них?При попытке "WHERE item_count <> 0" Я получаю:
MySQL said: Documentation
#1054 - Unknown column 'rid_count' in 'where clause'
Решение
Вот такой трюк:вычисление a SUM()
из значений, которые, как известно, равны либо 1, либо 0, эквивалентно COUNT()
из строк, где значение равно 1.И вы знаете, что логическое сравнение возвращает 1 или 0 (или NULL).
SELECT c.catname, COUNT(m.catid) AS item_count,
SUM(i.ownerid = @ownerid) AS owner_item_count
FROM categories c
LEFT JOIN map m USING (catid)
LEFT JOIN items i USING (itemid)
GROUP BY c.catid;
Что касается бонусного вопроса, вы могли бы просто выполнить внутреннее соединение вместо внешнего соединения, что означало бы только категории с хотя бы одной строкой в map
был бы возвращен.
SELECT c.catname, COUNT(m.catid) AS item_count,
SUM(i.ownerid = @ownerid) AS owner_item_count
FROM categories c
INNER JOIN map m USING (catid)
INNER JOIN items i USING (itemid)
GROUP BY c.catid;
Вот другое решение, которое не столь эффективно, но я покажу его, чтобы объяснить, почему вы получили ошибку:
SELECT c.catname, COUNT(m.catid) AS item_count,
SUM(i.ownerid = @ownerid) AS owner_item_count
FROM categories c
LEFT JOIN map m USING (catid)
LEFT JOIN items i USING (itemid)
GROUP BY c.catid
HAVING item_count > 0;
Вы не можете использовать псевдонимы столбцов в WHERE
предложение, поскольку выражения в WHERE
предложения вычисляются перед выражениями в списке выбора.Другими словами, значения, связанные с выражениями из списка выбора, пока недоступны.
Вы можете использовать псевдонимы столбцов в GROUP BY
, HAVING
, и ORDER BY
пункты.Эти предложения выполняются после того, как были вычислены все выражения в списке выбора.
Другие советы
Вы можете незаметно ввести оператор CASE в свой SUM():
SELECT categories.catName,
COUNT(map.itemId) AS item_count,
SUM(CASE WHEN owner= @ownerid THEN 1 ELSE 0 END) AS owner_item_count
FROM categories
LEFT JOIN map ON categories.catId = map.catId
LEFT JOIN items ON items.itemId = map.itemId
GROUP BY categories.catId
HAVING COUNT(map.itemId) > 0
SELECT categories.catName,
COUNT(map.itemId) AS item_count,
COUNT(items.itemId) AS owner_item_count
FROM categories
INNER JOIN map
ON categories.catId = map.catId
LEFT JOIN items
ON items.itemId = map.itemId
AND items.owner = @ownerId
GROUP BY categories.catId
Обратите внимание, что вы могли бы использовать HAVING
положение о owner_item_count
, но внутреннее соединение позаботится о item_count за вас.