A dificuldade específica aqui:Consultas com uma ou mais funções agregadas no
SELECT lista e não GROUP BY cláusula produz exatamente uma linha, mesmo que nenhuma linha seja encontrada na tabela subjacente. Não há nada que você possa fazer no
WHERE cláusula para suprimir essa linha. Você deve excluir tal linha após o fato , ou seja, no HAVING cláusula ou em uma consulta externa. Por documentação:
Se uma consulta contiver chamadas de função agregadas, mas nãoGROUP BYcláusula,o agrupamento ainda ocorre:o resultado é uma única linha de grupo (ou talvez norows, se a única linha for eliminada porHAVING). O mesmo é verdadeiro se contiver umHAVINGcláusula, mesmo sem chamadas de função agregada ouGROUP BYcláusula.
Deve-se notar que adicionar um
GROUP BY cláusula com apenas uma expressão constante (que de outra forma é completamente inútil!) também funciona. Veja o exemplo abaixo. Mas prefiro não usar esse truque, mesmo que seja curto, barato e simples, porque dificilmente é óbvio o que ele faz. A consulta a seguir precisa apenas de uma varredura de tabela única e retorna as 7 principais categorias ordenadas por contagem. Se (e somente se ) existem mais categorias, o resto é resumido em 'Outros':
WITH cte AS (
SELECT categoryid, count(*) AS data
, row_number() OVER (ORDER BY count(*) DESC, categoryid) AS rn
FROM contents
GROUP BY 1
)
( -- parentheses required again
SELECT categoryid, COALESCE(ca.name, 'Unknown') AS label, data
FROM cte
LEFT JOIN category ca ON ca.id = cte.categoryid
WHERE rn <= 7
ORDER BY rn
)
UNION ALL
SELECT NULL, 'Others', sum(data)
FROM cte
WHERE rn > 7 -- only take the rest
HAVING count(*) > 0; -- only if there actually is a rest
-- or: HAVING sum(data) > 0
-
Você precisa desempate se várias categorias puderem ter a mesma contagem na 7ª/8ª classificação. No meu exemplo, categorias com o menorcategoryidvencer tal corrida.
-
Os parênteses são necessários para incluir umLIMITouORDER BYcláusula para uma perna individual de umUNIONinquerir.
-
Você só precisa se juntar à tabelacategorypara as 7 melhores categorias. E geralmente é mais barato agregar primeiro e juntar mais tarde neste cenário. Portanto, não participe da consulta base no CTE (expressão de tabela comum) chamadocte, junte-se apenas no primeiroSELECTdaUNIONconsulta, que é mais barato.
-
Não sei por que você precisa doCOALESCE. Se você tiver uma chave estrangeira emcontents.categoryidparacategory.ide amboscontents.categoryidecategory.namesão definidosNOT NULL(como eles provavelmente deveriam ser), então você não precisa disso.
O ímpar GROUP BY true
Isso também funcionaria:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true; E ainda recebo planos de consulta um pouco mais rápidos. Mas é um hack meio estranho...
Fiddle SQL demonstrando tudo.
Resposta relacionada com mais explicações para o
UNION ALL / LIMIT técnica:- Somar os resultados de algumas consultas e encontrar os 5 principais em SQL