MySQL – Contrôle la ligne renvoyée par un groupe par

J’ai une table de firebase database 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 ne l’avez pas réglé, il contient plusieurs versions d’une ligne, puis des données textuelles.

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

J’ai essayé d’utiliser le group by tout en classant par version_id DESC – mais il semble qu’il soit ordonné après son regroupement, donc 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 ceci, qui fonctionne, mais utilise une sous-requête:

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

Cela s’appelle sélectionner le maximum par groupe 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éera 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 devez plus ou moins utiliser une sous-requête pour ce type de problème (les 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 sur la mémoire. Étant donné que cette requête ne comporte que deux ints, la sous-requête peut contenir des millions de lignes bien avant que cela ne se produise, mais la sous-requête select * de votre première requête risque de rencontrer ce problème beaucoup plus tôt.

Je pense que cela le ferait, pas sûr 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) 
 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êtes, une passe sur UNIQUE INDEX ON MYTABLE (id, version_id) si vous en avez une (que je pense que vous devriez)

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

select id, id_version, champ1, champ2 de datatable comme dt où id = (sélectionnez l’id de datatable où id = ordre dt.id par version_id desc limit 1)

Ceci est un pseudo-code mais quelque chose comme ça devrait fonctionner très bien

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

Cette requête fera le travail sans groupe en:

 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.

On peut toujours opter pour des fonctions analytiques qui vous donneront plus de contrôle

select tmp.* from ( select id,version_id,field1,field2, rank() over(partition by id order by version_id desc ) as rnk from table) tmp where tmp.rnk=1

Si vous rencontrez un problème avec la fonction rank () en fonction du type de données, vous pouvez choisir row_number () ou dense_rank ().

Je pense que c’est ce que vous voulez.

 select id, max(v_id), field1, field2 from table group by id 

Les résultats que j’en tire sont

1, 2, textb, text2

2, 3, texte, texte5

Edit: J’ai recréé la table et inséré les mêmes données avec l’id un ID version étant une clé primaire composée. Cela a donné la réponse que j’ai fournie plus tôt. C’était aussi dans MySQL.

pas testé, mais quelque chose comme ça pourrait fonctionner:

SELECT * FROM table GROUP BY id ORDER BY MAX (version_id) DESC