Pergunta

Muitas vezes, você precisa mostrar uma lista de itens de banco de dados e certos números agregados sobre cada item. Por exemplo, quando você digita o texto do título no Stack Overflow, liste as Questões relacionadas aparece. A lista mostra os títulos de entradas relacionadas e o número agregado única da quantidade de respostas para cada título.

Eu tenho um problema semelhante, mas precisando de vários agregados. Eu gostaria de exibir uma lista de itens em qualquer um dos 3 formatos, dependendo das opções do usuário:

  • O nome do meu item (15 totais, 13 possuído por mim)
  • O nome do meu item (15 no total)
  • O nome do meu item (13 possuído por mim)

Meu banco de dados é:

  • itens : itemId, itemName, ownerId
  • categorias : catId, catName
  • Mapa : mapId, itemId, catId

A consulta abaixo recebe: nome da categoria, a contagem de IDs de itens por categoria

SELECT
  categories.catName,
  COUNT(map.itemId) AS item_count
FROM categories
LEFT JOIN map
  ON categories.catId = map.catId
GROUP BY categories.catName

Esta se obtém: nome da categoria, a contagem de IDs de itens por categoria para este owner_id única

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

Mas como faço para obtê-los, ao mesmo tempo em uma única consulta? I.e .: nome da categoria, a contagem de IDs de itens por categoria, a contagem de IDs de itens por categoria para este owner_id única

Bonus. Como posso opcionalmente recuperar somente onde catid contar! = 0 para qualquer um desses? Na tentativa "ONDE ITEM_COUNT <> 0" eu recebo:

MySQL said: Documentation
#1054 - Unknown column 'rid_count' in 'where clause' 
Foi útil?

Solução

Aqui está um truque: cálculo de um SUM() de valores que são conhecidos por ser 1 ou 0 é equivalente a uma COUNT() das linhas onde o valor é 1. E você sabe que uma comparação boolean retorna 1 ou 0 (ou nulo) .

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;

Quanto à questão de bônus, você poderia simplesmente fazer uma junção interna em vez de uma associação externa, o que significaria apenas as categorias com pelo menos uma linha na map seria devolvido.

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;

Aqui está uma outra solução, que não é tão eficiente, mas eu vou mostrá-lo para explicar porque você tem o erro:

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;

Você não pode usar apelidos de coluna na cláusula WHERE, porque as expressões na cláusula WHERE são avaliadas antes das expressões na lista de seleção. Em outras palavras, os valores associados com select-list expressões ainda não estão disponíveis.

Você pode usar aliases de coluna nas cláusulas GROUP BY, HAVING e ORDER BY. Estas cláusulas são executados depois de todas as expressões na lista de seleção foram avaliadas.

Outras dicas

Você pode esgueirar-se uma instrução CASE dentro de sua 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

Note que você poderia usar uma cláusula HAVING em owner_item_count, mas a junção interna cuida de ITEM_COUNT para você.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top