2008-11-11 7 views
2

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

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

  • Имя моего элемента (15 общих, 13 принадлежит мне) имя
  • моего элемента (15 всего)
  • имя моего элемента (13 принадлежит мне)

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

  • пункты: Itemid, ITEMNAME, OwnerId
  • категории: CatID, catName
  • карта: МАПИД, 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 

Это получает: название категории, количество элементов для каждой категории для этого владельца_ только

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 

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

Бонус. Как я могу опционально получить, когда catId count! = 0 для любого из них? В попытке «ГДЕ ITEM_COUNT <> 0» я получаю:

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

ответ

5

Вот трюк: вычисляют 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 оцениваются перед выражениями в списке выбора. Другими словами, значения, связанные с выражениями select-list, пока недоступны.

Вы можете использовать псевдонимы столбцов в статьях GROUP BY, HAVING и ORDER BY. Эти предложения запускаются после того, как все выражения в списке выбора были оценены.

4

Вы можете стащить саз внутри 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
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 для вас.