2016-09-21 4 views
1

J'ai un grand (> 100 millions de lignes) table de ma base de données MS SQL avec les colonnes suivantes:Speedup requêtes SQL avec des agrégats sur DateTime et groupe par

Id int not null, 
ObjectId int not null, 
Timestamp datetime not null 
State int not null 

Id la clef primaire de la table (et a un index clusterisé dessus). J'ai ajouté un index non clusterisé sur Timestamp et ObjectId (dans cet ordre). Il y a juste environ 2000 valeurs distinctes dans ObjectId. Je veux maintenant effectuer la requête suivante:

SELECT ObjectId, MAX(Timestamp) FROM Table GROUP BY ObjectId 

Cela prend quelque chose autour de quatre secondes, ce qui est trop lent pour mon application. Le plan d'exécution indique que 97% du temps d'exécution va à un balayage d'index de l'index non groupé.

Sur une copie de la table, je crée un index ordonné en clusters sur ObjectId et Horodatage. L'exécution qui en résulte est la même, le plan d'exécution dit faire maintenant un balayage d'index de l'index en cluster.

est-il une autre possibilité d'améliorer l'exécution sans diviser les données de la table en plusieurs tables?

+1

Avez-vous essayé un index sur ObjectID seul? Bien que je ne m'attends pas à ce que cela améliore le problème, puisque la requête que vous effectuez doit toucher chaque ligne de la base de données dans tous les cas. IMO il n'y aura pas d'amélioration possible sans upscaling votre serveur DB ou la redéfinition de votre schéma (par exemple, vous pouvez ajouter une deuxième table qui conserve l'horodatage max pour chaque objectid en utilisant un déclencheur). – jeroenh

+0

@jeroenh: Oui, j'ai essayé aussi sans résultat remarquable- –

+1

En fait, nous pouvons supposer que vous insérez des données fréquemment dans ce tableau peut-être ajouter un indice dans votre requête: AVEC (NOLOCK) –

Répondre

1

je peux vous proposer une autre réponse, ajoutez une colonne booléenne dernière et mise à jour pour la dernière vraie ObjectId false avant maintenant insérer ramer pour cette ObjectID avec LAST true. Créez un index sur ObjectID et LAST. Interrogation très simple:

SELECT ObjectId, Timestamp FROM Table where LAST = true 

Pas plus par groupe et FullScan mais une mise à jour plus chacun pour insérer.

+0

Bonne idée. J'ai essayé ça et ça marche vraiment bien. La mise à jour supplémentaire n'a pas beaucoup d'importance.C'est rapide et pour moi, l'exécution de l'instruction SELECT est beaucoup plus cruciale. –

0

4 secondes dans pas mal pour ce genre de travail DB avec plus de 100M lignes. Vous pouvez archiver chaque jour certaines données dans un autre tableau pour préserver l'historique. Vous pouvez archiver toutes les données dans une autre table et supprimer les anciens changement d'objets:

delete from TABLE where Id in (select t1.Id from Table t1, Table t2 
where t1.ObjectId = t2.ObjectId and t1.Timestamp < t2.Timestamp) 
+0

Non, c'est vraiment mauvais, 4s sont autour de 3.9s trop :) Et ma question \t excluait explicitement les solutions qui copient une partie des données vers d'autres tables. –

+1

Vous pouvez donc acheter une meilleure machine ou MySQL réglage fin (« key_buffer_size » dans le fichier my.cnf pour augmenter l'utilisation de la RAM d'index), mais vous mettez de bons indices. Les données d'une base de données doivent être archivées, vous ne pouvez pas penser que votre modèle peut atteindre l'infini sans problème de performance. –

+0

Le fait est que la requête ci-dessus a un temps d'exécution qui dépend linéairement du nombre de lignes dans la table. Et ceci est sous-optimal, peu importe s'il y a un million ou un milliard de lignes dans le tableau. Tant que le plan d'exécution dit qu'il scanne toute la table, il y a quelque chose à améliorer avant de commencer certaines techniques d'archivage. –

0

Pour cette requête particulière, un index sur (ObjectId, timestamp) sera optimale. Et il y a une chance que (ObjectId, Timestamp DESC) fonctionnera encore plus vite.