J'ai la requête suivante, et en raison de nombreux SUM
appels de fonction, ma requête s'exécute trop lentement. J'ai beaucoup d'enregistrements dans ma base de données et je voudrais obtenir un rapport de l'année en cours et de l'année dernière (30 derniers jours, 90 derniers jours et 365 derniers jours) pour chacun:
SELECT
b.id as [ID]
,d.[Title] as [Title]
,e.Class as [Class]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]
FROM
tb1 a
INNER JOIN
tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN
tb3 c on b.fid = c.col5
INNER JOIN
tb4 d on c.id = d.col6
INNER JOIN
tb5 e on c.col7 = e.id
GROUP BY
b.id, d.Title, e.Class
Quelqu'un a-t-il une idée de comment puis-je améliorer ma requête afin d'exécuter plus rapidement?
EDIT: J'ai été encouragé à déplacer l' DATEADD
appel de fonction vers l' where
instruction et à charger d'abord les deux premières années, puis à les filtrer en colonnes, mais je ne suis pas sûr que la réponse suggérée soit exécutée et fonctionne, elle pourrait être trouvée ici: https: // stackoverflow. com / a / 59944426/12536284
Si vous êtes d'accord avec la solution ci-dessus, veuillez me montrer comment puis-je l'appliquer dans ma requête actuelle?
Juste pour info, j'utilise ce SP en C #, Entity Framework (DB-First), quelque chose comme ceci:
var result = MyDBEntities.CalculatorSP();
Execution Plan
. Veuillez l'afficherRéponses:
Comme cela a déjà été mentionné, le plan d'exécution sera vraiment utile dans ce cas. Sur la base de ce que vous avez montré, il semble que vous ayez extrait 12 colonnes de 15 colonnes au total
tb1 (a)
, vous pouvez donc essayer d'exécuter votre requête sans aucune jointure et justetb1
pour voir si votre requête fonctionne comme prévu. Étant donné que je ne vois rien de mal avec vos appels de fonction SUM, ma meilleure supposition est que vous avez un problème avec vos jointures, je suggère de faire ce qui suit. Vous pouvez commencer par exclure la dernière jointure par exemple,INNER JOIN tb5 e on c.col7 = e.id
et toute utilisation connexe commee.Class as [Class]
ete.Class
dans votre groupe par déclaration. Nous n'allons pas l'exclure complètement, c'est juste un test pour vous assurer que le problème est ou non, si votre requête fonctionne mieux et comme prévu, vous pouvez essayer d'utiliser une table temporaire comme solution de contournement au lieu de la dernière jointure , quelque chose comme ça:En fait, les tables temporaires sont des tables qui existent temporairement sur le serveur SQL. Les tables temporaires sont utiles pour stocker les jeux de résultats immédiats auxquels on accède plusieurs fois. Vous pouvez en savoir plus à ce sujet ici https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ Et ici https://codingsight.com/introduction-to-temporary-tables-in -serveur SQL/
Je recommande également fortement, si vous utilisez la procédure stockée, définissez la valeur
NOCOUNT
surON
, cela peut également améliorer considérablement les performances, car le trafic réseau est considérablement réduit:Sur cette base :
la source
tb5
sur la#Temp
table et la jonction de la table temporaire fonctionnent plus rapidement que la jonctiontb5
directe? ils contiennent sûrement les mêmes données (et il#Temp
peut manquer un index s'il existait entb5
). Je ne comprends vraiment pas pourquoi cela est plus efficace (pour autant que je sache, il devrait être moins efficace de copier toutes les données et de les rejoindre).tb5
se trouve sur un autre serveur? Dans ce cas, l'utilisation d'une table temporaire est nettement plus rapide que la jointure directe à un autre serveur. Ce n'était qu'une suggestion pour tester et voir si quelque chose a changé. J'ai eu une situation similaire dans le passé, et il semble heureusement que la table temporaire a également aidé le PO dans ce cas.La meilleure approche consiste à insérer dans une variable de table / table de hachage (si le nombre de lignes est petit, utilisez une variable de table ou utilisez une table de hachage si le nombre de lignes est assez gros). Mettez ensuite à jour l'agrégation, puis sélectionnez enfin la variable de table ou la table de hachage. Il est nécessaire d'examiner le plan de requête.
la source
Je suppose que tb1 est un grand tableau (par rapport à tb2, tb3, tb4 et tb5).
Si c'est le cas, il est logique ici de restreindre la sélection de cette table (avec une clause WHERE).
Si seule une petite partie de tb1 est utilisée, par exemple parce que les jointures avec tb2, tb3, tb4 et tb5 réduisent les lignes nécessaires à quelques pour cent seulement, vous devez vérifier si les tables sont indexées sur les colonnes que vous utilisez dans les jointures .
Si une grande partie de tb1 est utilisée, il peut être judicieux de regrouper ses résultats avant de les joindre à tb2, tb3, tb4 et tb5. En voici un exemple.
la source
Utilisez simplement des colonnes calculées
Exemple
Spécifiez les colonnes calculées dans une table
la source
Pour optimiser ces calculs, vous pouvez envisager de pré-calculer certaines des valeurs. L'idée des pré-calculs est de réduire le nombre de lignes à lire ou à poursuivre.
Une façon d'y parvenir consiste à utiliser une vue indexée et à laisser le moteur effectuer les calculs par lui-même. Comme ce type de vues présente certaines limites, vous finissez par créer un tableau simple et effectuez les calculs à la place. Fondamentalement, cela dépend des besoins de l'entreprise.
Ainsi, dans l'exemple ci - dessous , je suis en train de créer une table
RowID
et desRowDatetime
colonnes et insérer 1 million de lignes. J'utilise une vue indexée pour compter les entités par jour, donc au lieu d'interroger 1 million de lignes par an, je vais interroger 365 lignes par an pour compter ces mesures.Le succès d'une telle solution dépend beaucoup de la façon dont les données sont distribuées et du nombre de lignes dont vous disposez. Par exemple, si vous avez une entrée par jour pour chaque jour de l'année, la vue et le tableau auront la même correspondance de lignes, de sorte que les opérations d'E / S ne seront pas réduites.
En outre, ce qui précède n'est qu'un exemple de matérialisation des données et de leur lecture. Dans votre cas, vous devrez peut-être ajouter plus de colonnes à la définition de la vue.
la source
J'utiliserais une table de recherche "Dates" pour joindre mes données à un index sur DatesId. J'utilise les dates comme filtre lorsque je souhaite parcourir les données historiques. La jointure est rapide et donc le filtrage car le DatesId est un index primaire en cluster (clé primaire). Ajoutez également la colonne de date (en tant que colonne incluse) pour votre tableau de données.
Le tableau des dates comprend les colonnes suivantes:
DatesId, Date, Year, Quarter, YearQuarter, MonthNum, MonthNameShort, YearWeek, WeekNum, DayOfYear, DayOfMonth, DayNumOfWeek, DayName
Exemple de données: 20310409 2031-04-09 2031 2 2031-Q2 4 avril avr 2031_15 15 99 9 3 mercredi
Vous pouvez m'envoyer un PM si vous voulez un fichier csv de cette façon afin que vous puissiez l'importer dans la base de données, mais je suis sûr que vous pouvez facilement trouver quelque chose comme ça en ligne et créer le vôtre.
J'ajoute également une colonne d'identité afin que vous puissiez obtenir un entier pour chaque date. Cela rend le travail un peu plus facile, mais pas obligatoire.
Cela me permet de revenir facilement à une certaine période. Il est assez facile de créer vos propres vues à ce sujet. Vous pouvez bien sûr également utiliser la fonction ROW_NUMBER () pour cela pendant des années, des semaines, etc.
Une fois que j'ai la plage de données que je veux, je me joins aux données. Fonctionne très vite!
la source
Étant donné que vous regroupez toujours des valeurs sur la base d'un nombre entier de mois, je voudrais d'abord regrouper par mois dans une sous-requête dans la clause from. Cela revient à utiliser une table temporaire. Je ne sais pas si cela accélérerait réellement votre requête.
la source
Pour améliorer la vitesse de la requête SQL, vous devez ajouter des index. Pour chaque table jointe, vous devez ajouter un index.
Comme cet exemple de code pour oracle:
la source