web-dev-qa-db-fra.com

Optimiser la requête SQL pour éviter la correspondance de hachage (agrégat)

J'ai une requête SQL qui prend 7 minutes + pour renvoyer les résultats. J'essaie d'optimiser au maximum et le plan d'exécution perd 82% du temps sur un match de hachage (agrégat). J'ai fait quelques recherches et il semble que l'utilisation d'un "EXISTS" aiderait à résoudre, mais je n'ai pas encore trouvé la syntaxe de la requête pour la faire fonctionner. Voici la requête:

select dbo.Server.Name,
                dbo.DiskSpace.Drive,
                AVG(dbo.DiskSpace.FreeSpace) as 'Free Disk Space',
                AVG(dbo.Processor.PercentUsed) as 'CPU % Used',
                AVG(dbo.Memory.PercentUtilized) as '% Mem Used'

                from Server
                join dbo.DiskSpace on dbo.Server.ID=DiskSpace.ServerID
                join dbo.Processor on dbo.Server.ID=Processor.ServerID
                join dbo.Memory on dbo.Server.ID=dbo.Memory.ServerID

                where
                dbo.Processor.ProcessorNum='_Total' and dbo.Processor.Datetm>DATEADD(DAY,-(1),(CONVERT (date, GETDATE()))) and (dbo.Server.Name='qp-ratking' or dbo.Server.Name='qp-hyper2012' or dbo.Server.Name='qp-hyped' or dbo.Server.Name='qp-lichking')
                Group By dbo.server.name, Dbo.DiskSpace.Drive
                Order By Dbo.Server.Name, dbo.DiskSpace.Drive;

Comment puis-je réduire/éliminer les jointures à l'aide de EXISTS? Ou s’il existe un meilleur moyen d’optimiser, je suis partant pour cela aussi. Merci

8
gregs

Un collègue a décomposé la requête et extrait les données par petits morceaux, évitant ainsi autant de traitement des données renvoyées par les jointures. Il a été réduit à moins d'une seconde. Nouvelle requête:

WITH tempDiskSpace AS
(
SELECT dbo.Server.Name
      ,dbo.DiskSpace.Drive
      ,AVG(dbo.DiskSpace.FreeSpace) AS 'Free Disk Space'

FROM dbo.DiskSpace
      LEFT JOIN dbo.Server ON dbo.DiskSpace.ServerID=Server.ID

WHERE dbo.DiskSpace.Datetm>DATEADD(DAY,-(1),(CONVERT (date, GETDATE())))
AND (dbo.Server.Name='qp-ratking'
      OR dbo.Server.Name='qp-hyper2012'
      OR dbo.Server.Name='qp-hyped'
      OR dbo.Server.Name='qp-lichking')

GROUP BY Name, Drive
)
,tempProcessor
AS
(
SELECT dbo.Server.Name
      ,AVG(dbo.Processor.PercentUsed) AS 'CPU % Used'

FROM dbo.Processor
      LEFT JOIN dbo.Server ON dbo.Processor.ServerID=Server.ID

WHERE dbo.Processor.Datetm>DATEADD(DAY,-(1),(CONVERT (date, GETDATE())))
AND dbo.Processor.ProcessorNum='_Total'
AND (dbo.Server.Name='qp-ratking'
      OR dbo.Server.Name='qp-hyper2012'
      OR dbo.Server.Name='qp-hyped'
      OR dbo.Server.Name='qp-lichking')

GROUP BY Name
)
,tempMemory
AS
(
SELECT dbo.Server.Name
      ,AVG(dbo.Memory.PercentUtilized) as '% Mem Used'

FROM dbo.Memory
      LEFT JOIN dbo.Server ON dbo.Memory.ServerID=Server.ID

WHERE dbo.Memory.Datetm>DATEADD(DAY,-(1),(CONVERT (date, GETDATE())))
AND (dbo.Server.Name='qp-ratking'
      OR dbo.Server.Name='qp-hyper2012'
      OR dbo.Server.Name='qp-hyped'
      OR dbo.Server.Name='qp-lichking')

GROUP BY Name
)

SELECT tempDiskSpace.Name, tempDiskSpace.Drive, tempDiskSpace.[Free Disk Space], tempProcessor.[CPU % Used], tempMemory.[% Mem Used]
FROM tempDiskSpace
LEFT JOIN tempProcessor ON tempDiskSpace.Name=tempProcessor.Name
LEFT JOIN tempMemory ON tempDiskSpace.Name=tempMemory.Name
ORDER BY Name, Drive;

Merci pour toutes les suggestions.

5
gregs

Je commencerais par vérifier les index. Est-ce que toutes les clés utilisées dans la jointure sont définies comme primary keys? Ou ont-ils au moins des index?

Ensuite, des index supplémentaires sur Processor et Server pourraient aider:

create index idx_Processor_ProcessorNum_Datetm_ServerId on ProcessorNum(ProcessorNum, Datetm, ServerId);
create index idx_Server_Name_ServerId on Server(Name, ServerId)
2
Gordon Linoff

L’énoncé semble raisonnablement structuré et ne prévoit pas une marge d’optimisation considérable à condition que les conditions requises soient traitées, telles que

  1. Vérifiez la fragmentation d'index et assurez-vous que tous les index sont maintenus
  2. Vérifier si les statistiques sont à jour
  3. Si le mode "sale" est acceptable, cela vaut la peine d'envisager d'appliquer WITH (NOLOCK) sur les tables.
  4. Si la requête autorise la déclaration de variables, il peut être avantageux de déplacer DATEADD de l'instruction Filter comme indiqué ci-dessous.

J'espère que cela t'aides.

-- Assuming Variables can be declared see the script below. 
-- I made a few changes per my coding standard only to help me read better.

DECLARE @dt_Yesterdate DATE

SET @dt_Yesterdate = DATEADD (DAY, - (1), CONVERT (DATE, GETDATE ()))

SELECT s.Name,
       ds.Drive,
       AVG(ds.FreeSpace) AS 'Free Disk Space',
       AVG(P.PercentUsed) AS 'CPU % Used',
       AVG(m.PercentUtilized) AS '% Mem Used'
FROM Server s
     JOIN dbo.DiskSpace AS ds
         ON s.ID = ds.ServerID
     JOIN dbo.Processor AS p
         ON s.ID = p.ServerID
     JOIN dbo.Memory AS m
         ON s.ID = m.ServerID
WHERE P.ProcessorNum = '_Total'
  AND P.Datetm > @dt_Yesterdate
  AND s.Name IN ('qp-ratking', 'qp-hyper2012', 'qp-hyped','qp-lichking')
GROUP BY s.name, ds.Drive
ORDER BY s.Name, ds.Drive;
2

À tout le moins, je commencerais par me débarrasser de toutes ces OR clauses.

AND (dbo.Server.Name='qp-ratking'
      OR dbo.Server.Name='qp-hyper2012'
      OR dbo.Server.Name='qp-hyped'
      OR dbo.Server.Name='qp-lichking')

et remplacer par

AND dbo.Server.Name in ('qp-ratking','qp-hyper2012','qp-hyped','qp-lichking')  

Je ne suis cependant pas sûr de tout convertir en CTE. Vous ne pouvez pas indexer les CTE et je ne suis pas encore tombé sur une occasion où les CTE surpassent une requête classique. Votre requête initiale semblait bien formée, mis à part l'utilisation excessive de OR comme mentionné ci-dessus, je regarderais donc les index par la suite.

1
Nick