Запрос:подсчитайте несколько агрегатов для каждого элемента

StackOverflow https://stackoverflow.com/questions/281866

Вопрос

Часто вам нужно показать список элементов базы данных и определенные сводные данные о каждом элементе.Например, когда вы вводите текст заголовка в 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 за вас.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top