varios sumar repetidos registros promedio funcion ejemplos ejemplo contar consultas campos avg agrupar agrupados agrupadas sql group-by aggregate-functions postgresql-9.1

sumar - sql contar registros agrupados



debe aparecer en la cláusula GROUP BY o debe usarse en una función agregada (6)

Tengo una mesa 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 la media máxima para cada cname.

SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;

pero obtendré 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 intencionados, y se muestra el resultado incorrecto 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.


El problema de especificar campos no agrupados y no agregados en group by selecciones es que el motor no tiene forma de saber qué campo de registro debe devolver en este caso. ¿Es primero? ¿Es el último? Generalmente no hay registros que naturalmente correspondan al resultado agregado ( min Y max Son excepciones).

Sin embargo, hay una solución alternativa: hacer que el campo requerido se agregue también. En posgres, esto debería funcionar:

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg) FROM makerar GROUP BY cname;

Tenga en cuenta que esto crea una matriz de todas las wnames, ordenadas por avg, y devuelve el primer elemento (las matrices en postgres están basadas en 1).


En Postgres, también puede usar la sintaxis especial DISTINCT ON (expression) :

SELECT DISTINCT ON (cname) cname, wmname, avg FROM makerar ORDER BY cname, avg DESC ;


Esto parece funcionar tan bien

SELECT * FROM makerar m1 WHERE m1.avg = (SELECT MAX(avg) FROM makerar m2 WHERE m1.cname = m2.cname )


Recientemente me encontré con este problema, al tratar de contar el uso de mayús / case when , y encontré que al cambiar el orden de las instrucciones which y count soluciona el problema:

SELECT date(dateday) as pick_day, COUNT(CASE WHEN (apples = ''TRUE'' OR oranges ''TRUE'') THEN fruit END) AS fruit_counter FROM pickings GROUP BY 1

En lugar de usar, en este último, donde obtuve errores, las manzanas y las naranjas deberían aparecer en funciones agregadas

CASE WHEN ((apples = ''TRUE'' OR oranges ''TRUE'') THEN COUNT(*) END) END AS fruit_counter


Sí, este es un problema de agregación común. Antes de SQL3 (1999) , los campos seleccionados deben aparecer en la cláusula GROUP BY [*].

Para solucionar este problema, debe calcular el agregado en una subconsulta y luego unirlo consigo mismo para obtener las columnas adicionales que debe mostrar:

SELECT m.cname, m.wmname, t.mx FROM ( SELECT cname, MAX(avg) AS mx FROM makerar GROUP BY cname ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg ; cname | wmname | mx --------+--------+------------------------ canada | zoro | 2.0000000000000000 spain | usopp | 5.0000000000000000

Pero también puede usar funciones de ventana, que se ve más simple:

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx FROM makerar ;

Lo único con este método es que mostrará todos los registros (las funciones de ventana no agrupan). Pero mostrará el MAX correcto (es decir, cname at cname level) para el país en cada fila, por lo que depende de usted:

cname | wmname | mx --------+--------+------------------------ canada | zoro | 2.0000000000000000 spain | luffy | 5.0000000000000000 spain | usopp | 5.0000000000000000

La solución, posiblemente menos elegante, para mostrar las únicas (cname, wmname) coinciden con el valor máximo, es:

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */ m.cname, m.wmname, t.avg AS mx FROM ( SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn FROM makerar ) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1 ; cname | wmname | mx --------+--------+------------------------ canada | zoro | 2.0000000000000000 spain | usopp | 5.0000000000000000

[*]: Curiosamente, aunque el tipo de especificación permite seleccionar campos no agrupados, parece que a los motores principales no les gusta. Oracle y SQLServer simplemente no permiten esto en absoluto. Mysql solía permitirlo de manera predeterminada, pero ahora desde 5.7 el administrador necesita habilitar esta opción ( ONLY_FULL_GROUP_BY ) manualmente en la configuración del servidor para que esta característica sea compatible ...


SELECT t1.cname, t1.wmname, t2.max FROM makerar t1 JOIN ( SELECT cname, MAX(avg) max FROM makerar GROUP BY cname ) t2 ON t1.cname = t2.cname AND t1.avg = t2.max;

Utilizando la función de ventana rank() :

SELECT cname, wmname, avg FROM ( SELECT cname, wmname, avg, rank() OVER (PARTITION BY cname ORDER BY avg DESC) FROM makerar) t WHERE rank = 1;

Nota

Cualquiera de los dos conservará múltiples valores máximos por grupo. Si solo desea un solo registro por grupo, incluso si hay más de un registro con promedio igual a máximo, debe verificar la respuesta de @ ypercube.