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 BY
clá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 umHAVING
cláusula, mesmo sem chamadas de função agregada ouGROUP BY
clá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 menorcategoryid
vencer tal corrida.
-
Os parênteses são necessários para incluir umLIMIT
ouORDER BY
cláusula para uma perna individual de umUNION
inquerir.
-
Você só precisa se juntar à tabelacategory
para 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 primeiroSELECT
daUNION
consulta, que é mais barato.
-
Não sei por que você precisa doCOALESCE
. Se você tiver uma chave estrangeira emcontents.categoryid
paracategory.id
e amboscontents.categoryid
ecategory.name
sã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