web-dev-qa-db-fra.com

Sélectionnez la ligne avec la date la plus récente par utilisateur

J'ai un tableau ("lms_attendance") des heures d'enregistrement et de départ des utilisateurs qui ressemble à ceci:

id  user    time    io (enum)
1   9   1370931202  out
2   9   1370931664  out
3   6   1370932128  out
4   12  1370932128  out
5   12  1370933037  in

J'essaie de créer une vue de cette table qui ne produirait que l'enregistrement le plus récent par identifiant d'utilisateur, tout en me donnant la valeur "in" ou "out", ainsi:

id  user    time    io
2   9   1370931664  out
3   6   1370932128  out
5   12  1370933037  in

Je suis assez proche jusqu'à présent, mais j'ai compris que les vues n'acceptaient pas les sous-requêtes, ce qui rend la tâche beaucoup plus difficile. La requête la plus proche que j'ai eu était:

select 
    `lms_attendance`.`id` AS `id`,
    `lms_attendance`.`user` AS `user`,
    max(`lms_attendance`.`time`) AS `time`,
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io`

Mais ce que je reçois c'est:

id  user    time    io
3   6   1370932128  out
1   9   1370931664  out
5   12  1370933037  in
4   12  1370932128  out

Ce qui est proche, mais pas parfait. Je sais que le dernier groupe de ne devrait pas être là, mais sans lui, il renvoie l'heure la plus récente, mais pas avec sa valeur relative IO.

Des idées? Merci!

111
Keith

Requete:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user)

Résultat:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

Solution qui va marcher à chaque fois:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user            
                 ORDER BY t2.id DESC
                 LIMIT 1)
175
Justin

Inutile d'essayer de réinventer la roue, car c'est courant problème du plus grand nombre par groupe . Very Nice la solution est présentée .

Je préfère la solution la plus simpliste ( voir SQLFiddle, Justin's mis à jour ) sans sous-requêtes (donc facile à utiliser dans les vues):

SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND (t1.time < t2.time 
         OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL

Cela fonctionne également dans le cas où il y a deux enregistrements différents avec la même plus grande valeur dans le même groupe - grâce à l'astuce avec (t1.time = t2.time AND t1.Id < t2.Id). Tout ce que je fais ici est d’assurer que dans le cas où deux enregistrements du même utilisateur ont le même temps, un seul est choisi. Peu importe si le critère est Id ou quelque chose d’autre - en principe, tout critère dont l’unicité est garantie rendrait le travail ici.

66
TMS

Basé sur @TMS answer, j'aime bien parce qu'il n'y a pas besoin de sous-requêtes mais je pense qu'omettre la partie 'OR' sera suffisant et beaucoup plus simple à comprendre et à lire.

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL

si vous n'êtes pas intéressé par les lignes avec des temps nuls, vous pouvez les filtrer dans la clause WHERE:

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL
5
user1792210

Déjà résolu, mais pour le compte rendu, une autre approche serait de créer deux vues ...

CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));

CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la 
GROUP BY la.user;

CREATE VIEW latest_io AS
SELECT la.* 
FROM lms_attendance la
JOIN latest_all lall 
    ON lall.user = la.user
    AND lall.time = la.time;

INSERT INTO lms_attendance 
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');

SELECT * FROM latest_io;

Cliquez ici pour le voir en action chez SQL Fiddle

4
davmos

Essayez cette requête:

  select id,user, max(time), io 
  FROM lms_attendance group by user;
0
Sugan

Ok, cela peut être un hack ou une erreur, mais de toute façon cela fonctionne aussi bien.

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;
0
kev
select b.* from 

    (select 
        `lms_attendance`.`user` AS `user`,
        max(`lms_attendance`.`time`) AS `time`
    from `lms_attendance` 
    group by 
        `lms_attendance`.`user`) a

join

    (select * 
    from `lms_attendance` ) b

on a.user = b.user
and a.time = b.time
0
chetan
 select result from (
     select vorsteuerid as result, count(*) as anzahl from kreditorenrechnung where kundeid = 7148
     group by vorsteuerid
 ) a order by anzahl desc limit 0,1