web-dev-qa-db-fra.com

ORDER BY date et heure AVANT GROUP BY name dans mysql

j'ai une table comme celle-ci:

name    date         time
tom | 2011-07-04 | 01:09:52
tom | 2011-07-04 | 01:09:52
mad | 2011-07-04 | 02:10:53
mad | 2009-06-03 | 00:01:01

je veux le nom le plus ancien en premier: 

SELECT * 
ORDER BY date ASC, time ASC 
GROUP BY name

(-> ne fonctionne pas!)

maintenant il devrait me donner d'abord folle (date antérieure) puis tom

mais avec GROUP BY name ORDER BY date ASC, time ASC me donne le nouveau fou en premier parce qu'il se groupe avant de trier!

encore une fois: le problème est que je ne peux pas trier par date et heure avant de me regrouper car GROUP BY doit être avant ORDER BY!

48
CodingYourLife

Une autre méthode:

SELECT * 
FROM (
    SELECT * 
    ORDER BY date ASC, time ASC 
) AS sub
GROUP BY name

GROUP BY sur le premier résultat correspondant atteint. Si ce premier résultat correspondant s'avère être celui que vous voulez, tout devrait fonctionner comme prévu.

Je préfère cette méthode car la sous-requête a un sens logique plutôt que de la combiner avec d'autres conditions.

69
user1908688

Je pense que c'est ce que vous recherchez:

SELECT name, min(date)
FROM myTable
GROUP BY name
ORDER BY min(date)

Pour le moment, vous devez créer une date mysql via STR_TO_DATE:

STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s')

Alors :

SELECT name, min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
FROM myTable
GROUP BY name
ORDER BY min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
25
Cyril Gandon

Comme je ne suis pas autorisé à commenter la réponse de user1908688, voici un indice pour les utilisateurs de MariaDB:

SELECT *
FROM (
     SELECT *
     ORDER BY date ASC, time ASC
     LIMIT 18446744073709551615
     ) AS sub
GROUP BY sub.name

https://mariadb.com/kb/en/mariadb/why-is-order-by-in-a-from-subquery-ignored/

20
Vincent

Cela a fonctionné pour moi:

SELECT *
FROM your_table
WHERE id IN (
    SELECT MAX(id)
    FROM your_table
    GROUP BY name
);
8
jokermt235

Utilisez une sous-sélection:

select name, date, time
from mytable main
where date + time = (select min(date + time) from mytable where name = main.mytable)
order by date + time;
3
Bohemian

J'avais une variante différente de cette question où je n'avais qu'un seul champ DATETIME et j'avais besoin d'un limit après un group by ou d'un distinct après avoir trié par ordre décroissant en fonction du champ datetime, mais c'est ce qui m'a aidé:

select distinct (column) from
(select column from database.table
order by date_column DESC) as hist limit 10

Dans cet exemple avec les champs divisés, si vous pouvez trier sur un concat, vous pourrez peut-être vous en tirer avec quelque chose comme:

select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted

Ensuite, si vous voulez limiter, vous ajouterez simplement votre déclaration de limite à la fin:

select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted limit 10
0
AbsoluteƵERØ

Un autre moyen de résoudre ce problème serait d'utiliser un LEFT JOIN, qui pourrait être plus efficace. Commençons par un exemple prenant en compte uniquement le champ de date, car il est probablement plus courant de stocker date + heure dans une colonne datetime. Je souhaite également que la requête soit simple afin de la rendre plus facile à comprendre.

Ainsi, avec cet exemple particulier, si vous souhaitez afficher l'enregistrement plus ancien en fonction de la colonne date, et en supposant que le nom de votre table s'appelle people, vous pouvez utiliser la requête suivante:

SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date > p2.date
WHERE p2.date is NULL
GROUP BY p.name

Ce que le LEFT JOIN fait, c’est lorsque la colonne p.date est à sa valeur minimale, il n’y aura pas de p2.date avec une valeur plus petite dans la jointure de gauche et le p2.date correspondant sera donc NULL. Donc, en ajoutant WHERE p2.date is NULL, nous nous assurons d’afficher uniquement les enregistrements avec la date la plus ancienne.

De même, si vous souhaitez afficher l’enregistrement latest à la place, vous pouvez simplement modifier l’opérateur de comparaison dans le LEFT JOIN:

SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date < p2.date
WHERE p2.date is NULL
GROUP BY p.name

Maintenant, pour cet exemple particulier où date + heure sont des colonnes séparées, vous devrez les ajouter si vous souhaitez interroger en fonction de la date/heure de deux colonnes combinées, par exemple:

SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date + INTERVAL TIME_TO_SEC(p.time) SECOND > p2.date + INTERVAL TIME_TO_SEC(p2.time) SECOND
WHERE p2.date is NULL
GROUP BY p.name

Vous pouvez en savoir plus à ce sujet (et voir d’autres moyens de le faire) à la page Les lignes contenant le maximum groupe de certaines colonnes .

0
Ben