2010-06-22 3 views
3

J'ai la table mysql suivante appelée « photos », avec les champs suivants et les données de l'échantillon:mysql: groupe par ID, obtenir la plus haute priorité pour chaque ID

id vehicle_id filename priority 
1 45   a.jpg  4 
2 45   b.jpg  1 
3 56   f.jpg  4 
4 67   cc.jpg  4 
5 45   kt.jpg  3 
6 67   gg.jpg  1 

Est-il possible, dans une seule requête, pour obtenir une ligne pour chaque vehicle_id, et la ligne soit la plus haute priorité?

Le résultat que je cherche:

array (
    [0] => array([id] => '2', [vehicle_id] => '45', [filename] => 'b.jpg', [priority] => '1'), 
    [1] => array([id] => '3', [vehicle_id] => '56', [filename] => 'f.jpg', [priority] => '4'), 
    [2] => array([id] => '6', [vehicle_id] => '67', [filename] => 'gg.jpg', [priority] => '1') 
); 

Si pas possible dans une seule requête, quelle serait la meilleure approche?

Merci!

+0

Que se passe-t-il si un véhicule_id a 2 lignes liées pour le plus haut. Voulez-vous les deux en arrière? –

+0

Non, alors je serais par défaut au plus tôt posté. Une seule ligne par véhicule_id. – briang

+0

@briang: Comment savez-vous quel est le plus ancien affiché? Celui avec l'ID le plus bas? –

Répondre

4

Bien que cela puisse être la réponse 'accepté', la performance de Mark's solution est dans des circonstances normales plusieurs fois mieux, et tout aussi valable pour la question, donc par tous les moyens, optez pour sa solution dans la production!


SELECT a.id, a.vehicle_id, a.filename, a.priority 
FROM pics a 
LEFT JOIN pics b    -- JOIN for priority 
ON b.vehicle_id = a.vehicle_id 
AND b.priority > a.priority 
LEFT JOIN pics c    -- JOIN for priority ties 
ON c.vehicle_id = a.vehicle_id 
AND c.priority = a.priority 
AND c.id < a.id 
WHERE b.id IS NULL AND c.id IS NULL 

En supposant 'id' est une colonne non nulle.

[edit]: mon mauvais, besoin d'une seconde jointure, ne peut pas le faire avec un seul.

+0

Parfait! Une seule requête :) – briang

+1

Ce ne sera pas trop lent? Avez-vous testé la performance de celui-ci? De quelle manière les autres réponses ne sont-elles pas «une seule requête»? –

+0

@Mark: Je ne suis pas familier avec les tests de performance réels. J'ai seulement supposé qu'une seule requête serait plus rapide que multiple. Je voudrais les tester. Existe-t-il une ressource particulière avec laquelle je pourrais tester ces options? Merci pour l'aide! – briang

5

Cela semble être une requête max par groupe typique. Dans la plupart des bases de données, vous pouvez facilement le faire en utilisant ROW_NUMBER:

SELECT id, vehicle_id, filename, priority 
FROM (
    SELECT 
     id, vehicle_id, filename, priority, 
     ROW_NUMBER() OVER (PARTITION BY vehicle_id 
          ORDER BY priority DESC, id) AS rn 
    FROM pics 
) AS T1 
WHERE rn = 1 

Depuis MySQL ne supporte pas encore ROW_NUMBER vous pouvez l'imiter en utilisant des variables:

SELECT id, vehicle_id, filename, priority 
FROM (
    SELECT 
     id, vehicle_id, filename, priority, 
     @rn := CASE WHEN @prev_vehicle_id = vehicle_id 
        THEN @rn + 1 
        ELSE 1 
       END AS rn, 
     @prev_vehicle_id := vehicle_id 
    FROM (SELECT @prev_vehicle_id := NULL) vars, pics T1 
    ORDER BY vehicle_id, priority DESC, id 
) T2 
WHERE rn = 1 
+2

En vérité, je n'ai presque jamais de votes pour ça:/ –

+0

Pour une raison quelconque, les gens trouvent les variables effrayantes dans MySQL, je suppose que c'est la méconnaissance des '@' et ': =' par rapport à ANSI SQL qui les effraie de. Il ne devrait pas bien sûr, les variables peuvent être un coup de pouce majeur à la performance. – Wrikken

5

Notez que cette méthode a été publiée avant qu'elle ne soit faite clair qu'il est nécessaire de gérer les liens prioritaires. Je le laisse ici pour référence (voir les commentaires ci-dessous). Consultez @Mark's answer une solution qui gère les liens au besoin:

SELECT p.id, p.vehicle_id, p.filename, p.priority 
FROM pics p 
JOIN (
      SELECT vehicle_id, MAX(priority) max_priority 
      FROM  pics 
      GROUP BY vehicle_id 
     ) sub_p ON (sub_p.vehicle_id = p.vehicle_id AND 
        sub_p.max_priority = p.priority) 
GROUP BY p.vehicle_id; 

Cela suppose qu'il peut y avoir aucun lien de priorité pour la même vehicle_id.

cas de test:

CREATE TABLE pics (id int, vehicle_id int, filename varchar(10), priority int); 

INSERT INTO pics VALUES ('1', '45', 'a.jpg', '4'); 
INSERT INTO pics VALUES ('2', '45', 'b.jpg', '1'); 
INSERT INTO pics VALUES ('3', '56', 'f.jpg', '4'); 
INSERT INTO pics VALUES ('4', '67', 'cc.jpg', '4'); 
INSERT INTO pics VALUES ('5', '45', 'kt.jpg', '3'); 
INSERT INTO pics VALUES ('6', '67', 'gg.jpg', '1'); 

Résultat:

+------+------------+----------+----------+ 
| id | vehicle_id | filename | priority | 
+------+------------+----------+----------+ 
| 1 |   45 | a.jpg |  4 | 
| 3 |   56 | f.jpg |  4 | 
| 4 |   67 | cc.jpg |  4 | 
+------+------------+----------+----------+ 
3 rows in set (0.01 sec) 
+0

Cela ne fonctionne que s'ils ont besoin d'un seul enregistrement, min/max à cela. Cela ne fonctionnera pas s'ils veulent le top 5/10/etc par. –

+0

@OMG: C'est vrai. Mais je pense que c'est le seul inconvénient, n'est-ce pas? ... Le PO ne semble pas exiger que: "la rangée soit la priorité la plus élevée" –

+0

Il a dit explicitement qu'il voulait seulement une rangée, donc je ne pense pas que ce soit un problème sérieux. Un problème plus sérieux est que cela peut renvoyer plus d'une ligne, même si elle est réparable. –