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
categoryid
más pequeño ganar tal carrera. -
Se requieren paréntesis para incluir un
LIMIT
oORDER BY
cláusula a un tramo individual de unaUNION
consulta. -
Solo necesitas unirte a la tabla
category
para 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 primeroSELECT
de laUNION
consulta, eso es más barato. -
No estoy seguro de por qué necesita
COALESCE
. Si tiene una clave externa decontents.categoryid
acategory.id
y amboscontents.categoryid
ycategory.name
está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