web-dev-qa-db-fra.com

MySQL - Contrôle quelle ligne est retournée par un groupe par

J'ai une table de base de données comme celle-ci:

id    version_id    field1    field2
1     1             texta      text1
1     2             textb      text2
2     1             textc      text3
2     2             textd      text4
2     3             texte      text5

Si vous n'y êtes pas parvenu, il contient plusieurs versions d'une ligne, puis des données textuelles.

Je veux l'interroger et renvoyer la version avec le numéro le plus élevé pour chaque identifiant. (donc les deuxième et dernière lignes uniquement dans ce qui précède).

J'ai essayé d'utiliser group par lors de la commande par version_id DESC - mais il semble que la commande soit postérieure à celle-ci, de sorte que cela ne fonctionne pas.

Quelqu'un a une idée? Je ne peux pas croire que ça ne peut pas être fait!

METTRE À JOUR:

Venez avec cela, qui fonctionne, mais utilise une sous-requête:

SELECT *
FROM (SELECT * FROM table ORDER BY version_id DESC) t1
GROUP BY t1.id
56
benlumley

Cela s'appelle sélectionner le groupe maximum d'une colonne. Voici plusieurs approches différentes pour mysql.

Voici comment je le ferais:

SELECT *
FROM (SELECT id, max(version_id) as version_id FROM table GROUP BY id) t1
INNER JOIN table t2 on t2.id=t1.id and t1.version_id=t2.version_id

Cela sera relativement efficace, bien que mysql crée une table temporaire en mémoire pour la sous-requête. Je suppose que vous avez déjà un index sur (id, version_id) pour cette table.

C'est un défaut de SQL que vous deviez plus ou moins utiliser une sous-requête pour ce type de problème ( semi-jointures sont un autre exemple).

Les sous-requêtes ne sont pas bien optimisées dans mysql, mais les sous-requêtes non corrélées ne sont pas si mauvaises tant qu'elles ne sont pas si énormes qu'elles seront écrites sur le disque plutôt que dans la mémoire. Étant donné que dans cette requête n'a que deux entiers, la sous-requête peut contenir des millions de lignes bien avant que cela se produise, mais la sous-requête select * de votre première requête risque de souffrir de ce problème beaucoup plus tôt.

49
ʞɔıu

Je pense que cela le ferait, je ne sais pas si c'est le meilleur ou le plus rapide.

SELECT * FROM table 
WHERE (id, version_id) IN 
  (SELECT id, MAX(version_id) FROM table GROUP BY id)
3
Chris J
SELECT id, version_id, field1, field2
FROM (
    SELECT @prev = id AS st, (@prev := id), m.*
    FROM (
           (SELECT @prev := NULL) p,
           (
            SELECT *
            FROM   mytable
            ORDER BY
                   id DESC, version_id DESC
           ) m
     ) m2
WHERE NOT IFNULL(st, FALSE);

Pas de sous-requête, on passe à UNIQUE INDEX ON MYTABLE (id, version_id) si vous en avez une (ce que je pense que vous devriez

2
Quassnoi

Cette requête fera le travail sans groupe par:

SELECT * FROM table AS t
LEFT JOIN table AS t2 
    ON t.id=t2.id 
    AND t.version_id < t2.version_id
WHERE t2.id IS NULL

Il n'a pas besoin de tables temporaires.

0
Patrick Savalle

C'est du pseudo-code mais quelque chose comme ça devrait marcher très bien

select *
from table
inner join
(
    select id , max(version_id) maxVersion
    from table 
) dvtbl ON id = dvtbl.id && versionid = dvtbl.maxVersion
0
Chris Meek

Je le fais habituellement avec une sous-requête:

select id, version_id, field1, field2 de datatable comme dt où id = (select id de datatable où id = dt.id commande par version_id desc limite 1)

0
mcassano