Je veux pouvoir sélectionner un groupe de lignes dans une table d'e-mails et les regrouper par expéditeur. 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 souhaite - elle sélectionne les enregistrements groupés par e-mail. Le problème est que le sujet et l'horodatage ne correspondent pas à l'enregistrement le plus récent pour une adresse e-mail particulière.
Par exemple, il peut renvoyer:
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 faire en sorte que MySQL sélectionne cet enregistrement lors du regroupement des e-mails?
la source
As of 5.7.5 ONLY_FULL_GROUP_BY is enabled by default, i.e. it's impossible to use non-aggregate columns.
Le mode SQL peut être modifié pendant l'exécution sans privilèges d'administrateur, il est donc très facile de désactiver ONLY_FULL_GROUP_BY. Par exemple:SET SESSION sql_mode = '';
. Démo: db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/3Voici une approche:
Fondamentalement, vous joignez la table sur elle-même, en recherchant les lignes ultérieures. Dans la clause where, vous indiquez qu'il ne peut pas y avoir de lignes ultérieures. Cela ne vous donne que la dernière ligne.
S'il peut y avoir plusieurs e-mails avec le même horodatage, cette requête doit être affinée. S'il y a une colonne ID incrémentielle dans la table des e-mails, modifiez la jointure comme suit:
la source
textID
c'était ambigu = /LEFT JOIN
critèresAND next.timestamp <= UNIX_TIMESTAMP()
Comme déjà indiqué dans une réponse, la réponse actuelle est fausse, car le GROUP BY sélectionne arbitrairement l'enregistrement dans la fenêtre.
Si l'on utilise MySQL 5.6 ou MySQL 5.7 avec
ONLY_FULL_GROUP_BY
, la requête correcte (déterministe) est:Pour que la requête s'exécute efficacement, une indexation appropriée est requise.
Notez qu'à des fins de simplification, j'ai supprimé le
LOWER()
, qui dans la plupart des cas, ne sera pas utilisé.la source
order by
dans la sous-sélection dans les autres réponses, n'a aucun effet.Faites un GROUP BY après le ORDER BY en enveloppant votre requête avec le GROUP BY comme ceci:
la source
time
, le plus récenttime
ou aléatoire?time DESC
, puis le groupe par prend la première (la plus récente).Selon la norme SQL, vous ne pouvez pas utiliser de colonnes non agrégées dans la liste de sélection. MySQL permet une telle utilisation (mode ONLY_FULL_GROUP_BY uless utilisé) mais le résultat n'est pas prévisible.
ONLY_FULL_GROUP_BY
Vous devez d'abord sélectionner fromEmail, MIN (lecture), puis, avec la deuxième requête (ou sous-requête) - Objet.
la source
J'ai eu du mal avec ces deux approches pour des requêtes plus complexes que celles présentées, car l'approche des sous-requêtes était horriblement inefficace, quels que soient les index que j'ai mis, et parce que je ne pouvais pas obtenir l'auto-jointure externe via Hibernate
La meilleure (et la plus simple) façon de faire cela est de grouper par quelque chose qui est construit pour contenir une concaténation des champs dont vous avez besoin, puis de les extraire à l'aide d'expressions dans la clause SELECT. Si vous avez besoin de faire un MAX (), assurez-vous que le champ sur lequel vous voulez MAX () se trouve toujours à l'extrémité la plus significative de l'entité concaténée.
La clé pour comprendre cela est que la requête ne peut avoir de sens que si ces autres champs sont invariants pour toute entité qui satisfait le Max (), donc en termes de tri, les autres éléments de la concaténation peuvent être ignorés. Il explique comment faire cela tout en bas de ce lien. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html
Si vous pouvez obtenir un événement d'insertion / mise à jour (comme un déclencheur) pour précalculer la concaténation des champs, vous pouvez l'indexer et la requête sera aussi rapide que si le groupe par était juste au-dessus du champ que vous vouliez réellement MAX ( ). Vous pouvez même l'utiliser pour obtenir le maximum de plusieurs champs. Je l'utilise pour faire des requêtes sur des arbres multidimensionnels exprimés sous forme d'ensembles imbriqués.
la source