Question

Je veux être en mesure de sélectionner un groupe de lignes d'une table des e-mails et de les regrouper par l'expéditeur de. Ma requête ressemble à ceci:

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

La requête fonctionne presque comme je le veux - il sélectionne des enregistrements regroupés par e-mail. Le problème est que le sujet et l'horodatage ne correspondent pas à la plus récente record pour une adresse e-mail particulier.

Par exemple, il pourrait revenir:

fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome

Lorsque les enregistrements de la base de données sont:

fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome

Si le sujet « question de programmation » est le plus récent, comment puis-je obtenir MySQL pour sélectionner cet enregistrement lors du regroupement des e-mails?

Était-ce utile?

La solution

Une solution simple consiste à envelopper la requête dans une sous-sélection avec l'instruction ORDER premier et l'application de la clause GROUP BY plus tard :

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

Ceci est similaire à l'aide de la rejoindre, mais semble beaucoup plus agréable.

Utilisation de colonnes non agrégées dans un SELECT avec une clause GROUP BY est non-standard. MySQL retourne généralement les valeurs de la première ligne qu'il trouve et jeter le reste. ORDER BY clauses ne s'appliquera à la valeur de colonne retournée, et non à ceux mis au rebut.

Mise à jour importante Sélection des colonnes non agrégées utilisées pour travailler dans la pratique, mais ne devraient pas se fier. Par documentation MySQL « ceci est principalement utile lorsque toutes les valeurs dans chaque colonne non agrégée non nommé dans le GROUP BY sont les mêmes pour chaque groupe. le serveur est libre de choisir une valeur de chaque groupe, donc sauf si elles sont les mêmes, la valeurs choisies sont indéterminées « .

Au 5.6.21 j'ai remarqué des problèmes avec le GROUP BY sur la table temporaire revenir ORDER BY tri.

Au 5.7.5 ONLY_FULL_GROUP_BY est activé par défaut, à savoir qu'il est impossible d'utiliser des colonnes non agrégées.

Voir 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

Autres conseils

Voici une approche:

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)

En gros, vous joindre à la table sur elle-même, la recherche de lignes plus tard. Dans la clause where vous déclarez qu'il ne peut y avoir des lignes plus tard. Cela vous donne seulement la dernière ligne.

S'il peut y avoir plusieurs e-mails avec le même horodatage, cette requête aurait besoin de raffinage. S'il y a une colonne ID supplémentaire dans le tableau électronique, changer la REJOIGNEZ comme:

LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.id < next.id

Faites GROUP BY après ORDER BY en enveloppant votre requête avec GROUP BY comme ceci:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from

Comme indiqué dans une réponse déjà, la réponse actuelle est erronée, parce que le GROUP BY choisit arbitrairement l'enregistrement de la fenêtre.

Si l'on utilise MySQL 5.6, MySQL 5.7 ou avec ONLY_FULL_GROUP_BY, la requête correcte (déterministe) est la suivante:

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

Pour que la requête pour fonctionner efficacement, l'indexation appropriée est nécessaire.

Notez que pour des raisons de simplification, je l'ai enlevé le LOWER(), qui, dans la plupart des cas, ne sera pas utilisé.

Selon standard SQL, vous ne pouvez pas utiliser des colonnes non agrégées dans la liste de sélection. MySQL permet une telle utilisation (uless le mode ONLY_FULL_GROUP_BY utilisé) mais le résultat est pas prévisible.

ONLY_FULL_GROUP_BY

Vous devez d'abord sélectionner fromEmail, MIN (lire), puis, avec la deuxième requête (ou sous-requête) -. Objet

Je luttais avec ces deux approches pour des requêtes plus complexes que celles indiquées, parce que l'approche de sous-requête était horriblement ineficient, peu importe ce que les indices que je mets, et parce que je ne pouvais pas obtenir l'auto-jointure externe par Hibernate

La meilleure (et plus facile) façon de le faire est de regrouper par quelque chose qui est construit pour contenir une concaténation des champs dont vous avez besoin, puis de les retirer à l'aide d'expressions dans la clause SELECT. Si vous devez faire un MAX () assurez-vous que le champ à MAX () est toujours plus à la fin la plus importante de l'entité concaténer.

La clé de la compréhension est que la requête ne peut faire sens que si ces champs sont invariantes pour toute entité qui satisfait Max (), donc en termes de triera les autres pièces de la concaténation peuvent être ignorés. Il explique comment faire cela au bas de ce lien. http://dev.mysql.com/doc /refman/5.0/en/group-by-hidden-columns.html

Si vous pouvez obtenir h insertion / événement de mise à jour (comme un déclencheur) d'effectuer une pré-calculer la concaténation des champs que vous pouvez indexer et la requête sera aussi rapide que si le groupe en était sur tout le terrain que vous avez réellement voulu à MAX (). Vous pouvez même l'utiliser pour obtenir le maximum de champs multiples. Je l'utilise pour faire des requêtes sur des arbres à plusieurs dimensions expresssed sous forme d'ensembles imbriqués.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top