Tengo una tabla que se parece a esta llamada 'makerar'
cname | wmname | avg
--------+-------------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Y quiero seleccionar el promedio máximo para cada cname.
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
pero tendré un error
ERROR: column "makerar.wmname" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
entonces hago esto
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname, wmname;
sin embargo, esto no dará los resultados previstos, y se muestra la salida incorrecta a continuación.
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Los resultados reales deben ser
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | usopp | 5.0000000000000000
¿Cómo puedo solucionar este problema?
Nota: Esta tabla es una VISTA creada a partir de una operación anterior.
sql
group-by
aggregate-functions
postgresql-9.1
Chico al azar
fuente
fuente
wmname="usopp"
espera y no por ejemplowmname="luffy"
?Respuestas:
Sí, este es un problema de agregación común. Antes de SQL3 (1999) , los campos seleccionados deben aparecer en la
GROUP BY
cláusula [*].Para solucionar este problema, debe calcular el agregado en una subconsulta y luego unirlo consigo mismo para obtener las columnas adicionales que debe mostrar:
Pero también puede usar funciones de ventana, que se ven más simples:
Lo único con este método es que mostrará todos los registros (las funciones de la ventana no se agrupan). Pero mostrará el correcto (es decir, máximo al
cname
nivel)MAX
para el país en cada fila, por lo que depende de usted:La solución, posiblemente menos elegante, para mostrar las únicas
(cname, wmname)
tuplas que coinciden con el valor máximo, es:[*]: Curiosamente, a pesar de que el tipo de especificación permite seleccionar campos no agrupados, a los motores principales parece no gustarles realmente. Oracle y SQLServer simplemente no permiten esto en absoluto. Mysql solía permitirlo de forma predeterminada, pero ahora desde 5.7 el administrador debe habilitar esta opción (
ONLY_FULL_GROUP_BY
) manualmente en la configuración del servidor para que esta característica sea compatible ...fuente
MAX
(vea la respuesta de @ypercube, también hay otra solución en mi respuesta), pero no de la forma en que lo hace. Verifique la salida esperada.avg
porcname
) pero no restringe las filas del resultado (como lo desea el OP). Ver los resultados reales debe ser párrafo en la pregunta.ONLY_FULL_GROUP_BY
en MySQL 5.7 no activa el modo en que SQL previsto por la norma cuando las columnas pueden ser omitidas de lagroup by
(o hace que MySQL se comportan como Postgres). Simplemente vuelve al comportamiento anterior donde MySQL devuelve resultados aleatorios (= "indeterminados").En Postgres, también puedes usar la
DISTINCT ON (expression)
sintaxis especial :fuente
BY cname
?El problema con la especificación de campos no agrupados y no agregados en
group by
selecciones es que el motor no tiene forma de saber qué campo de registro debería devolver en este caso. ¿Es primero? ¿Es el último? Por lo general, no hay registros que correspondan naturalmente al resultado agregado (min
ymax
son excepciones).Sin embargo, existe una solución alternativa: agregue también el campo requerido. En posgres, esto debería funcionar:
Tenga en cuenta que esto crea una matriz de todos los wnames, ordenados por avg, y devuelve el primer elemento (las matrices en postgres están basadas en 1).
fuente
Usando la
rank()
función de ventana :Nota
Cualquiera de los dos conservará múltiples valores máximos por grupo. Si desea un solo registro por grupo, incluso si hay más de un registro con un promedio igual al máximo, debe verificar la respuesta de @ ypercube.
fuente
Para mí, no se trata de un "problema de agregación común", sino de una consulta SQL incorrecta. La única respuesta correcta para "seleccionar el promedio máximo para cada cname ..." es
El resultado será:
Este resultado en general responde a la pregunta "¿Cuál es el mejor resultado para cada grupo?" . Vemos que el mejor resultado para España es 5 y para Canadá el mejor resultado es 2. Es cierto, y no hay error. Si también necesitamos mostrar wmname , debemos responder la pregunta: "¿Cuál es la REGLA para elegir wmname del conjunto resultante?" Cambiemos un poco los datos de entrada para aclarar el error:
¿Qué resultado esperas al ejecutar esta consulta
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
? ¿Debería serspain+luffy
ospain+usopp
? ¿Por qué? En la consulta no se determina cómo elegir "mejor" wmname si varios son adecuados, por lo que el resultado tampoco está determinado. Es por eso que el intérprete de SQL devuelve un error: la consulta no es correcta.En otras palabras, no hay una respuesta correcta a la pregunta "¿Quién es el mejor del
spain
grupo?" . Luffy no es mejor que usopp, porque usopp tiene el mismo "puntaje".fuente
SELECT cname, id, MAX(avg) FROM makerar GROUP BY cname;
que dio este error engañoso.Esto parece funcionar también
fuente
Recientemente me encontré con este problema, al intentar contar usando
case when
, y descubrí que cambiar el orden de las declaracioneswhich
ycount
soluciona el problema:En lugar de usar, en este último, donde obtuve errores de que las manzanas y las naranjas deberían aparecer en funciones agregadas
fuente
which
declaración?