2010-08-31 3 views
1

J'ai une table définie comme:ligne la plus récente pour l'utilisateur donné dans MySQL

create table Foo (
    X integer, 
    Y timestamp, 
    Z varchar(255), 
    primary key (X, Y) 
); 

Pour un X donné, je veux trouver la ligne la plus récente. Jusqu'à présent, j'ai trouvé ce qui suit, mais je voudrais savoir si quelqu'un sait quelque chose qui fonctionnera mieux ou est juste "intéressant" comme utiliser une jointure gauche sur lui-même (que je n'ai pas réussi à mettre au travail)).

select * from Foo where X=1234 order by Y desc limit 0,1; 
select * from Foo where X=1234 and Y in (select max(Y) from Foo where X=1234); 

Merci!

+0

la colonne Y est garantie unique par utilisateur? –

+0

Deux clés primaires ne sont pas possibles dans une table – Murugesh

+0

@Mark Byers: bien que Y soit très susceptible d'être unique, il n'est pas garanti car deux entrées peuvent avoir des horodatages identiques en théorie. – Scruffers

Répondre

1

Dans votre deuxième exemple vous utilisez un sous-requête corrélée. Vous pouvez utiliser une sous-requête non corrélée (table dérivée) à la place. Ce sera plus rapide si vous répertoriez tous les objets, car une sous-requête corrélée devrait être appelée une fois pour chaque ligne de la requête externe.

Voici un exemple en utilisant une table dérivée:

SELECT foo.* 
FROM foo 
JOIN (
      SELECT MAX(Y) max_time, X 
      FROM  foo 
      GROUP BY X 
     ) d_foo ON (d_foo.X = foo.X AND 
        d_foo.max_time = foo.Y); 

Cas de test:

INSERT INTO foo VALUES (1, '2010-01-01 12:00:00', '1'); 
INSERT INTO foo VALUES (1, '2010-01-03 12:00:00', '2'); 
INSERT INTO foo VALUES (2, '2010-01-05 12:00:00', '3'); 
INSERT INTO foo VALUES (2, '2010-01-02 12:00:00', '4'); 
INSERT INTO foo VALUES (3, '2010-01-08 12:00:00', '5'); 
INSERT INTO foo VALUES (4, '2010-01-03 12:00:00', '6'); 
INSERT INTO foo VALUES (4, '2010-01-04 12:00:00', '7'); 

Résultat:

+---+---------------------+------+ 
| X | Y     | Z | 
+---+---------------------+------+ 
| 1 | 2010-01-03 12:00:00 | 2 | 
| 2 | 2010-01-05 12:00:00 | 3 | 
| 3 | 2010-01-08 12:00:00 | 5 | 
| 4 | 2010-01-04 12:00:00 | 7 | 
+---+---------------------+------+ 
4 rows in set (0.02 sec) 

Cependant, si vous serez toujours restreignez votre résultat juste un X, vos solutions sont probablement bien. Consultez @Mark Byers' answer pour d'autres conseils à ce sujet.

2

Votre deuxième solution fonctionnera mais les performances risquent de ne pas être optimales et de renvoyer plusieurs lignes. Vous devez également modifier IN-=:

SELECT * FROM Foo 
WHERE X = 1234 AND Y = (SELECT MAX(Y) FROM Foo WHERE X = 1234) 

Je pense que la première Soluton vaut mieux:

SELECT * 
FROM Foo 
WHERE X = 1234 
ORDER BY Y DESC 
LIMIT 1 

Ajouter un index sur X, Y pour obtenir des performances décentes.

2

sélectionner la ligne la plus récente pour chaque utilisateur (qui avait des événements)

select * from events e 
    join (select UserId, Max(time) time from events group by userId) t 
    ON (e.userId = t.userid and e.time = t.time) 
+0

Je ne pense pas que cela fonctionne correctement. Le 'select userId, max (time)' pourrait renvoyer une valeur qui ne correspond pas à une clé primaire dans la table, c.-à-d. Donner l'identifiant 1, avec le temps maximum pour l'utilisateur 2. – Scruffers

+0

@Scruffers, How 'primary key »et« groupe par »liés ici ?? Peu importe le champ 'time' fait partie de la clé primaire ou non. –

+0

Je pense que j'ai lu votre solution avant de l'éditer pour inclure 'group by'. Je pense que ça fonctionne maintenant. THX. – Scruffers

0

voici comment vous le faites:

SELECT * 
FROM foo INNER JOIN (SELECT `x`, MAX(`y`) AS `y` FROM foo GROUP BY `x`) AS foo2 ON foo.x = foo2.x AND foo.y = foo2.y 
Questions connexes