MySQL "Agrupar Por" y " Ordenar Por"


Quiero ser capaz de seleccionar un montón de filas de una tabla de correos electrónicos y agruparlos por el remitente de. Mi consulta se ve así:

SELECT 
    `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails` 
GROUP BY LOWER(`fromEmail`) 
ORDER BY `timestamp` DESC

La consulta casi funciona como yo quiero - selecciona registros agrupados por correo electrónico. El problema es que el asunto y la marca de tiempo no corresponden al registro más reciente de una dirección de correo electrónico en particular.

Por ejemplo, podría devolver:

fromEmail: [email protected], subject: hello
fromEmail: [email protected], subject: welcome

Cuando los registros en la base de datos son:

fromEmail: [email protected], subject: hello
fromEmail: [email protected], subject: programming question
fromEmail: [email protected], subject: welcome

Si la "programación pregunta " el asunto es el más reciente, ¿cómo puedo hacer que MySQL seleccione ese registro al agrupar los correos electrónicos?

Author: T30, 2009-07-01

6 answers

Una solución simple es envolver la consulta en una subselección con la instrucción ORDER primero y aplicar el GRUPO POR más tarde :

SELECT * FROM ( 
    SELECT `timestamp`, `fromEmail`, `subject`
    FROM `incomingEmails` 
    ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)

Esto es similar a usar la combinación, pero se ve mucho mejor.

El uso de columnas no agregadas en un SELECT con una cláusula GROUP BY no es estándar. MySQL generalmente devuelve los valores de la primera fila que encuentra y descarta el resto. Cualquier cláusula ORDER BY solo se aplicará al valor de la columna devuelta, no a las descartadas.

ACTUALIZACIÓN IMPORTANTE Seleccionar columnas no agregadas utilizadas para trabajar en la práctica, pero no se debe confiar en ellas. Según MySQL documentation "esto es útil principalmente cuando todos los valores en cada columna no agregada no nombrada en el GRUPO POR son los mismos para cada grupo. El servidor es libre de elegir cualquier valorde cada grupo, por lo que a menos que sean los mismos, los valores elegidos son indeterminados."

A partir de 5.6.21 he notado problemas con el AGRUPAR POR en la tabla temporal revirtiendo el ORDEN ordenando.

A partir de 5.7.5 ONLY_FULL_GROUP_BY está habilitado por defecto, es decir, es imposible usar columnas no agregadas.

Véase http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

 121
Author: b7kich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-24 19:46:25

Aquí hay un enfoque:

SELECT cur.textID, cur.fromEmail, cur.subject, 
     cur.timestamp, cur.read
FROM incomingEmails cur
LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.timestamp < next.timestamp
WHERE next.timestamp is null
and cur.toUserID = '$userID' 
ORDER BY LOWER(cur.fromEmail)

Básicamente, se une a la tabla sobre sí misma, buscando filas posteriores. En la cláusula where se indica que no puede haber filas posteriores. Esto te da solo la última fila.

Si puede haber varios correos electrónicos con la misma marca de tiempo, esta consulta necesitaría refinarse. Si hay una columna de ID incremental en la tabla de correo electrónico, cambie la combinación como:

LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.id < next.id
 40
Author: Andomar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 23:10:08

Haga un GRUPO POR después del ORDEN POR envolviendo su consulta con el GRUPO DE la siguiente manera:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
 27
Author: 11101101b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-30 19:53:32

De acuerdo con el estándar SQL, no puede usar columnas no agregadas en la lista de selección. MySQL permite tal uso (se usa el modo uless ONLY_FULL_GROUP_BY) pero el resultado no es predecible.

ONLY_FULL_GROUP_BY

Primero debe seleccionar fromEmail, MIN(read), y luego, con second query (o subconsulty) - Subject.

 21
Author: noonex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-06 14:03:41

Como ya se señaló en una respuesta, la respuesta actual es incorrecta, porque el GRUPO selecciona arbitrariamente el registro de la ventana.

Si se usa MySQL 5.6, o MySQL 5.7 con ONLY_FULL_GROUP_BY, la consulta correcta (determinista) es:

SELECT incomingEmails.*
  FROM (
    SELECT fromEmail, MAX(timestamp) `timestamp`
    FROM incomingEmails
    GROUP BY fromEmail
  ) filtered_incomingEmails
  JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp

Para que la consulta se ejecute de manera eficiente, se requiere una indexación adecuada.

Tenga en cuenta que para fines de simplificación, he eliminado el LOWER(), que en la mayoría de los casos, no se utilizará.

 19
Author: Marcus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-27 12:15:19

Luché con ambos enfoques para consultas más complejas que las mostradas, porque el enfoque de subconsulta era horriblemente ineficiente sin importar los índices que pusiera, y porque no pude obtener el auto-join externo a través de Hibernar

La mejor (y más fácil) manera de hacer esto es agrupar por algo que está construido para contener una concatenación de los campos que necesita y luego extraerlos usando expresiones en la cláusula SELECT. Si necesitas hacer una MAX() asegúrate de que el campo sobre el que desea MAX () está siempre en el extremo más significativo de la entidad concatenada.

La clave para entender esto es que la consulta solo puede tener sentido si estos otros campos son invariantes para cualquier entidad que satisfaga a Max(), por lo que en términos de la ordenación las otras piezas de la concatenación pueden ser ignoradas. Explica cómo hacer esto en la parte inferior de este enlace. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html

Si puedes get am insert / update event (como un trigger) para pre-calcular la concatenación de los campos puedes indexarla y la consulta será tan rápida como si el group by hubiera terminado solo el campo que realmente querías MAX (). Incluso puede usarlo para obtener el máximo de campos múltiples. Lo uso para hacer consultas contra árboles multidimensionales expresados como conjuntos anidados.

 2
Author: Mike N,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-31 14:00:56