La dificultad específica aquí:consultas con una o más funciones agregadas en SELECT lista y no GROUP BY cláusula produce exactamente una fila, incluso si no se encuentra ninguna en la tabla subyacente.
No hay nada que puedas hacer en WHERE cláusula para suprimir esa fila. Debe excluir dicha fila después del hecho , es decir, en el HAVING cláusula, o en una consulta externa.
Por documentación:
Si una consulta contiene llamadas a funciones agregadas, pero no GROUP BY cláusula, la agrupación aún ocurre:el resultado es una sola fila de grupo (o quizás ninguna fila en absoluto, si la fila única es luego eliminada por HAVING ). Lo mismo es cierto si contiene un HAVING cláusula, incluso sin ninguna llamada de función agregada o GROUP BY cláusula.
Cabe señalar que agregar un GROUP BY La cláusula con solo una expresión constante (¡que de otra manera es completamente inútil!) también funciona. Ver ejemplo a continuación. Pero prefiero no usar ese truco, incluso si es corto, barato y simple, porque no es muy obvio lo que hace.
La siguiente consulta solo necesita un análisis de una sola tabla y devuelve las 7 categorías principales ordenadas por conteo. Si (y solo si ) hay más categorías, el resto se resume en 'Otros':
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
-
Debe romper los empates si varias categorías pueden tener el mismo recuento en el rango 7 u 8. En mi ejemplo, las categorías con el
categoryidmás pequeño ganar tal carrera. -
Se requieren paréntesis para incluir un
LIMIToORDER BYcláusula a un tramo individual de unaUNIONconsulta. -
Solo necesitas unirte a la tabla
categorypara las 7 primeras categorías. Y generalmente es más barato agregar primero y unirse más tarde en este escenario. Por lo tanto, no se una a la consulta base en la CTE (expresión de tabla común) llamadacte, solo únete al primeroSELECTde laUNIONconsulta, eso es más barato. -
No estoy seguro de por qué necesita
COALESCE. Si tiene una clave externa decontents.categoryidacategory.idy amboscontents.categoryidycategory.nameestán definidosNOT NULL(como probablemente deberían ser), entonces no lo necesitas.
El extraño GROUP BY true
Esto también funcionaría:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true; E incluso obtengo planes de consulta un poco más rápidos. Pero es un truco bastante extraño...
Violín SQL demostrando todo.
Respuesta relacionada con más explicaciones para UNION ALL / LIMIT técnica:
- Sumar los resultados de algunas consultas y luego encontrar los 5 principales en SQL