J'ai une table SQL Server avec plus de 3 milliards de lignes. Une de mes requêtes prend un temps extrêmement long, je pense donc à l'optimiser. La requête ressemble à ceci:
SELECT [Enroll_Date]
,Count(*) AS [Record #]
,Count(Distinct UserID) AS [User #]
FROM UserTable
GROUP BY [Enroll_Date]
[Enroll_Date] est une colonne à faible sélectivité avec moins de 50 valeurs possibles, tandis que la colonne UserID est une colonne à haute sélectivité avec plus de 200 millions de valeurs distinctes. Sur la base de mes recherches, je pense que je devrais créer un indice composite non clusterisé sur ces deux colonnes, et en théorie, la colonne de haute sélectivité devrait être la première colonne. Mais je ne suis pas sûr dans mon cas, est-ce que cela fonctionnerait parce que j'utilise la colonne de faible sélectivité dans la clause group by.
Cette table n'a pas d'index cluster.
la source
Réponses:
Comme alternative à la solution de @ AaronBertrand (si vous ne pouvez pas ou ne voulez pas créer une vue indexée), je vous recommande de créer un index sur
(Enroll_Date, UserID)
. Si ce type de question est très courant sur votre table, cela devrait probablement même être votre index clusterisé.Je ne recommanderais généralement pas les index à haute sélectivité en tant que "meilleure pratique" générale, mais je chercherais plutôt à savoir quel index donnera à votre requête les meilleures performances.
Un index sur
(Enroll_Date, UserID)
donnera à votre requête un plan de requête hautement optimisé et non bloquant avec Stream Aggregates."Non bloquant" dans ce contexte signifie que la requête n'a pas besoin de mettre en mémoire tampon des quantités importantes de données (comme, par exemple, un tri ou un agrégat de hachage), ce qui signifie qu'elle (a) commence immédiatement à renvoyer des lignes, et ( b) ne consomme pratiquement pas de mémoire de travail.
la source
La réponse d'Aarons est une excellente solution. Je vais répondre à la question en supposant que vous ne voulez pas adopter cette approche.
La requête que vous avez publiée sera généralement exécutée en regroupant d'abord
(Enroll_Date, UserID)
, puis à nouveau(Enroll_Date)
. Cette optimisation est nouvelle dans SQL Server 2012. Elle prend effet en cas de singleCOUNT DISTINCT
.Un index sur ces deux colonnes dans l'ordre spécifique
(Enroll_Date, UserID)
suffira pour obtenir un plan efficace qui achemine une analyse d'index dans deux agrégats de flux consécutifs. L'ordre inverse ne permettrait pas ce plan.Par conséquent, utilisez la commande
(Enroll_Date, UserID)
. Vous n'avez pas le choix ici.la source
Cela ressemble à un scénario idéal pour une vue indexée, qui vous permet de payer des calculs et des agrégats au moment de l'écriture au lieu du temps de la requête.
Cela prendra un certain temps à créer et nécessitera bien sûr une maintenance tout au long de toutes les opérations DML, tout comme un index sur la table de base.
Maintenant, la requête par rapport à cette vue serait assez similaire - chaque ligne de la vue représente maintenant un combo utilisateur / date distinct, de sorte que le chiffre peut être calculé par un seul COUNT (*), tandis que le nombre total de lignes dans la table de base est déjà partiellement agrégées pour vous, il vous suffit maintenant de les additionner en utilisant SUM par date:
Ajout d'un indice NOEXPAND, après avoir rappelé ceci et cela .
Je peux vous dire sans aucun doute que cette requête sera plus rapide que votre requête actuelle (mais pas de combien), sauf dans les rares cas où vous avez exactement un utilisateur pour chaque date (auquel cas la même quantité de données aura à lire) et les colonnes que nous connaissons sont les seules colonnes de l'index de la table de base. Nous ne pouvons pas vous dire si cet accroissement des performances au moment de la lecture vaut le travail supplémentaire qui affectera la partie écriture de votre charge de travail - vous devrez le tester pour mesurer le compromis (aucun index n'est gratuit).
Et si vous utilisez fréquemment les mêmes clauses WHERE communes contre Enroll_Date pour des plages spécifiques et bien définies (par exemple, le trimestre ou l'année en cours à ce jour), vous pouvez ajouter des index filtrés correspondants qui réduisent encore plus les E / S (mais il y a toujours un troquer).
Vous pouvez également envisager de placer un index cluster sur la table de base. Cela ne semble pas être l'un de ces cas d'utilisation très rares qui bénéficient d'un tas.
la source