J'ai une table de base de données contenant les colonnes suivantes:
id code value datetime timestamp
Dans ce tableau, les seules valeurs uniques résident dans id, c'est-à-dire la clé primaire.
Je souhaite récupérer le dernier ensemble distinct d'enregistrements dans cette table en fonction de la valeur datetime. Par exemple, disons ci-dessous ma table
id code value datetime timestamp
1 1023 23.56 2011-04-05 14:54:52 1234223421
2 1024 23.56 2011-04-05 14:55:52 1234223423
3 1025 23.56 2011-04-05 14:56:52 1234223424
4 1023 23.56 2011-04-05 14:57:52 1234223425
5 1025 23.56 2011-04-05 14:58:52 1234223426
6 1025 23.56 2011-04-05 14:59:52 1234223427
7 1024 23.56 2011-04-05 15:00:12 1234223428
8 1026 23.56 2011-04-05 15:01:14 1234223429
9 1025 23.56 2011-04-05 15:02:22 1234223430
Je veux récupérer les enregistrements avec les ID 4, 7, 8 et 9, c'est-à-dire le dernier ensemble d'enregistrements avec des codes distincts (basés sur la valeur datetime). Ce que j'ai mis en évidence n'est qu'un exemple de ce que j'essaie de réaliser, car ce tableau va éventuellement contenir des millions d'enregistrements et des centaines de valeurs de code individuelles.
Quelle instruction SQL puis-je utiliser pour y parvenir? Je n'arrive pas à le faire avec une seule instruction SQL. Ma base de données est MySQL 5.
Cela devrait fonctionner pour vous.
SELECT *
FROM [tableName]
WHERE id IN (SELECT MAX(id) FROM [tableName] GROUP BY code)
Si id est AUTO_INCREMENT, vous n'avez pas à vous soucier de la date/heure qui est beaucoup plus coûteuse à calculer, car la date/heure la plus récente aura également l'id le plus élevé.
pdate: Du point de vue des performances, assurez-vous que les colonnes id
et code
sont indexées lorsque vous traitez un grand nombre d'enregistrements. Si id
est la clé primaire, elle est intégrée, mais vous devrez peut-être ajouter un index non groupé couvrant code
et id
.
Essaye ça:
SELECT *
FROM <YOUR_TABLE>
WHERE (code, datetime, timestamp) IN
(
SELECT code, MAX(datetime), MAX(timestamp)
FROM <YOUR_TABLE>
GROUP BY code
)
C'est un vieux message, mais tester la réponse de @smdrager avec de grandes tables était très lent. Ma solution à cela était d'utiliser "jointure interne" au lieu de "où".
SELECT *
FROM [tableName] as t1
INNER JOIN (SELECT MAX(id) as id FROM [tableName] GROUP BY code) as t2
ON t1.id = t2.id
Cela a fonctionné très rapidement.
Je vais essayer quelque chose comme ça:
select * from table
where id in (
select id
from table
group by code
having datetime = max(datetime)
)
(avertissement: ceci n'est pas testé)
Si la ligne avec le plus grand datetime a également le plus grand id, la solution proposée par smdrager est plus rapide.
Il semble que toutes les réponses existantes suggèrent de faire GROUP BY code
sur toute la table. Quand elle est logiquement correcte, en réalité cette requête passera par toute la table (!) (Utilisez EXPLAIN
pour vous en assurer). Dans mon cas, j'ai moins de 500 Ko de lignes dans la table et j'exécute ...GROUP BY code
prend 0,3 seconde, ce qui n'est absolument pas acceptable.
Cependant, je peux utiliser la connaissance de mes données ici (lire "afficher les derniers commentaires pour les messages"):
code
>> quantité d'enregistrements "supérieurs" que vous souhaitez obtenirEn expérimentant avec des nombres, j'ai découvert que je peux toujours trouver 20 code
différents si je sélectionne seulement les 50 derniers enregistrements. Et dans ce cas, les travaux de requête suivants (en gardant à l'esprit le commentaire @smdrager sur la forte probabilité d'utiliser id
au lieu de datetime
)
SELECT id, code
FROM tablename
ORDER BY id DESC
LIMIT 50
La sélection des 50 dernières entrées est très rapide, car elle n'a pas besoin de vérifier l'ensemble du tableau. Et le reste consiste à sélectionner le top 20 avec des code
distincts parmi ces 50 entrées.
De toute évidence, les requêtes sur l'ensemble des 50 (100, 500) éléments sont nettement plus rapides que sur l'ensemble du tableau avec des centaines de milliers d'entrées.
"Post-traitement" SQL brut
SELECT MAX(id) as id, code FROM
(SELECT id, code
FROM tablename
ORDER BY id DESC
LIMIT 50) AS nested
GROUP BY code
ORDER BY id DESC
LIMIT 20
Cela vous donnera une liste de id
très rapide et si vous souhaitez effectuer des JOIN supplémentaires, placez cette requête comme une autre requête imbriquée et effectuez toutes les jointures dessus.
"Post-traitement" côté backend
Et après cela, vous devez traiter les données dans votre langage de programmation pour inclure dans l'ensemble final uniquement les enregistrements avec un code
distinct.
Une sorte de pseudo-code Python:
records = select_simple_top_records(50)
added_codes = []
top_records = []
for record in records:
# If record for this code was already found before
# Note: this is not optimal, better to use structure allowing O(1) search and insert
if record['code'] in added_codes:
continue
# Save record
top_records.append(record)
added_codes.append(record['code'])
# If we found all top-20 required, finish
if len(top_records) >= 20:
break