Запрос: подсчитать несколько агрегатов на элемент - PullRequest
2 голосов
/ 11 ноября 2008

Часто вам нужно показать список элементов базы данных и определенные совокупные номера для каждого элемента. Например, когда вы набираете текст заголовка в Stack Overflow, появляется список связанных вопросов. В списке показаны заголовки связанных записей и единое агрегированное количество ответов для каждого заголовка.

У меня похожая проблема, но мне нужно несколько агрегатов. Я хотел бы отобразить список элементов в любом из 3 форматов в зависимости от параметров пользователя:

  • Наименование моего товара (всего 15, принадлежит мне)
  • Название моего товара (всего 15)
  • Название моего предмета (13 принадлежит мне)

Моя база данных:

  • items : itemId, itemName, ownerId
  • категорий : catId, catName
  • map : 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

Но как мне получить их одновременно в одном запросе? Т.е.: название категории, количество идентификаторов элементов в каждой категории, количество идентификаторов элементов в каждой категории только для этого владельца_10

Бонус. Как я могу опционально получить только то, где catId count! = 0 для любого из них? При попытке "WHERE item_count <> 0" я получаю:

MySQL said: Documentation
#1054 - Unknown column 'rid_count' in 'where clause' 

Ответы [ 3 ]

5 голосов
/ 11 ноября 2008

Вот хитрость: вычисление 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. Эти предложения выполняются после того, как все выражения в списке выбора были оценены.

4 голосов
/ 11 ноября 2008

Вы можете проникнуть в оператор 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
2 голосов
/ 11 ноября 2008
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 для вас.

...