4

J'ai du mal avec l'optimisation des requêtes, actuellement je suis très proche de la refonte de la base de données. Et le stackoverflow est mon dernier espoir. Je ne pense pas que juste vous montrer la requête est assez si j'ai lié non seulement le script de base de données, mais aussi la sauvegarde de la base de données jointe au cas où vous ne voulez pas générer les données à la main. scénario et la sauvegardeProcédure stockée très lente

les problèmes commencent lorsque vous essayez de faire ce qui suit ...

exec LockBranches @count=64,@lockedBy='034C0396-5C34-4DDA-8AD5-7E43B373AE5A',@lockedOn='2011-07-01 01:29:43.863',@unlockOn='2011-07-01 01:32:43.863' 

les principaux problèmes se produisent dans cette partie:

UPDATE B 
SET B.LockedBy = @lockedBy, 
    B.LockedOn = @lockedOn, 
    B.UnlockOn = @unlockOn, 
    B.Complete = 1 
FROM 
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete 
    FROM Objectives AS O 
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID 
    INNER JOIN Branches AS B ON B.GenerationID = G.ID 
    INNER JOIN 
    (
     SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes 
     FROM SpicieBranches AS SB 
     INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID 
     INNER JOIN 
     (
      SELECT P.ID, 1 AS SuitableProbes 
      FROM Probes AS P 
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */ 
      GROUP BY P.ID 
      HAVING COUNT(R.ID) > 0 
     ) AS X ON P.ID = X.ID 
     GROUP BY SB.BranchID 
    ) AS X ON X.BranchID = B.ID 
    WHERE 
      (O.Active = 1) 
     AND (B.Sealed = 0) 
     AND (B.GenerationNo < O.BranchGenerations) 
     AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0) 
     AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)   
) AS B 

EDIT: Voici les montants des lignes dans chaque table:

Spicies   71536 
Results   10240 
Probes   10240 
SpicieBranches 4096 
Branches  256 
Estimates  5 
Generations  1 
Versions  1 
Objectives  1 
+0

J'ai essayé de restaurer, mais je n'ai pas R2 malchance. Combien de lignes dans les tableaux 'Results' et' Probes' respectivement? –

+0

J'ai édité la question pour montrer le nombre de lignes – Lu4

+0

SqlServer Profiler donne pour CPU/Reads/Writes/Duration autour de 6300/500000/670/8100 – Lu4

Répondre

2

Quelqu'un d'autre pourrait peut-être expliquer mieux que moi pourquoi c'est beaucoup plus rapide. L'expérience me dit que lorsque vous avez un tas de requêtes qui sont collectivement lentes mais qui doivent être rapides dans leurs parties individuelles, cela vaut la peine d'essayer une table temporaire.

Ceci est beaucoup plus rapide

ALTER PROCEDURE LockBranches 
-- Add the parameters for the stored procedure here 
@count INT, 
@lockedOn DATETIME, 
@unlockOn DATETIME, 
@lockedBy UNIQUEIDENTIFIER 

AS 
BEGIN 
-- SET NOCOUNT ON added to prevent extra result sets from 
-- interfering with SELECT statements. 
SET NOCOUNT ON 

--Create Temp Table 
SELECT SpicieBranches.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes 
INTO #BranchSuitableProbeCount 
FROM SpicieBranches 
INNER JOIN Probes AS P ON P.SpicieID = SpicieBranches.SpicieID 
INNER JOIN 
( 
    SELECT P.ID, 1 AS SuitableProbes 
    FROM Probes AS P 
    INNER JOIN Results AS R ON P.ID = R.ProbeID 
    GROUP BY P.ID 
    HAVING COUNT(R.ID) > 0 
) AS X ON P.ID = X.ID 
GROUP BY SpicieBranches.BranchID 


UPDATE B SET 
B.LockedBy = @lockedBy,  
B.LockedOn = @lockedOn,  
B.UnlockOn = @unlockOn,  
B.Complete = 1 
FROM 
(
    SELECT TOP (@count) Branches.LockedBy, Branches.LockedOn, Branches.UnlockOn, Branches.Complete 
    FROM Objectives 
    INNER JOIN Generations ON Generations.ObjectiveID = Objectives.ID 
    INNER JOIN Branches ON Branches.GenerationID = Generations.ID 
    INNER JOIN #BranchSuitableProbeCount ON Branches.ID = #BranchSuitableProbeCount.BranchID 
    WHERE 
    (Objectives.Active = 1) 
    AND (Branches.Sealed = 0) 
    AND (Branches.GenerationNo < Objectives.BranchGenerations) 
    AND (Branches.LockedBy IS NULL OR DATEDIFF(SECOND, Branches.UnlockOn, GETDATE()) > 0) 
    AND (Branches.Complete = 1 OR #BranchSuitableProbeCount.SuitableProbes = Objectives.BranchSize * Objectives.EstimateCount * Objectives.ProbeCount) 
) AS B 

END 

Ceci est beaucoup plus rapide avec un temps d'exécution moyen de 54ms par rapport à 6 secondes avec l'original.

EDIT

avait un regard et combiné mes idées avec ceux de la solution de RBarryYoung. Si vous utilisez les touches suivantes pour créer la table temporaire

SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes 
INTO #BranchSuitableProbeCount 
FROM SpicieBranches AS SB 
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID 
WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID) 
GROUP BY SB.BranchID 

alors vous pouvez obtenir cela à 15ms qui est 400x mieux que nous avons commencé avec.Regarder le plan d'exécution montre qu'il y a une analyse de table qui se passe sur la table temporaire. Normalement, vous évitez autant que vous le pouvez les scans de table, mais pour 128 lignes (dans ce cas), c'est plus rapide que ce qu'il faisait auparavant.

+0

Veuillez excuser mes abus de vos conventions de nommage Je trouve que SQL est vraiment difficile à lire quand tout est aliasé. –

+0

Salut David, c'est vraiment étrange, ça marche vraiment très vite, je ne comprends pas comment ça peut arriver ... En ce qui concerne les conventions pas un problème, utilisez le vôtre si vous voulez ... – Lu4

+0

bien :), les jointures ne sont pas à 100%, c'est seulement au niveau requis. Ainsi, impactant le scan de la table et d'autres dans le plan d'exécution ... – Pankaj

1

Il semble que la jointure sur les deux colonnes uniqueidentifier sont la source du problème. L'un est un index clusterisé, l'autre non-cluster sur la (table FK). Bien qu'il y ait des index sur eux. Malheureusement, les guids sont notoirement médiocres lorsqu'ils se joignent à un grand nombre de lignes.

Comme les étapes de dépannage:

  • quel état sont les indices en? À quand remonte la dernière fois que les statistiques ont été mises à jour?
  • comment performant est cette sous-requête sur elle-même, lorsqu'elle est exécutée adhoc? c'est-à-dire lorsque vous exécutez cette instruction par elle-même, à quelle vitesse le jeu de résultats revient-il? acceptable? Après la reconstruction des 2 index et la mise à jour des statistiques, y a-t-il une différence mesurable?
SELECT P.ID, 1 AS SuitableProbes FROM Probes AS P 
INNER JOIN Results AS R ON P.ID = R.ProbeID 
GROUP BY P.ID HAVING COUNT(R.ID) > 0 
+0

Tous les index sont reconstruits et réorganisés. Chacun a une fragmentation <20%, les statistiques n'ont pas été mises à jour du tout, n'ont pas compris la deuxième étape. Autant que j'ai compris les deux index devraient être groupés ou non groupés? – Lu4

2

Ceci est essentiellement une estimation complète, mais dans le passé, j'ai trouvé que l'adhésion sur les résultats d'une sous-requête peut être lente horrifically. Autrement dit, la sous-requête était évaluée trop souvent, alors qu'elle n'en avait pas vraiment besoin.
Le moyen de contourner ce problème était de déplacer les sous-requêtes dans des CTE et de les joindre à la place. Bonne chance!

1

qui suit dure environ 15x plus vite sur mon système:

UPDATE B 
SET B.LockedBy = @lockedBy, 
    B.LockedOn = @lockedOn, 
    B.UnlockOn = @unlockOn, 
    B.Complete = 1 
FROM 
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete 
    FROM Objectives AS O 
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID 
    INNER JOIN Branches AS B ON B.GenerationID = G.ID 
    INNER JOIN 
    (
     SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes 
     FROM SpicieBranches AS SB 
     INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID 
     WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID) 
     GROUP BY SB.BranchID 
    ) AS X ON X.BranchID = B.ID 
    WHERE 
      (O.Active = 1) 
     AND (B.Sealed = 0) 
     AND (B.GenerationNo < O.BranchGenerations) 
     AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0) 
     AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)   
) AS B 
1

insertion de sous requête dans la table temporaire locale

SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes 
into #temp FROM SpicieBranches AS SB 
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID 
INNER JOIN 
(
    SELECT P.ID, 1 AS SuitableProbes 
    FROM Probes AS P 
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */ 
    GROUP BY P.ID 
    HAVING COUNT(R.ID) > 0 
) AS X ON P.ID = X.ID 
GROUP BY SB.BranchID 

La requête ci-dessous montre la partie s'associe à la table correspondante au lieu de terminer !!

UPDATE B 
SET B.LockedBy = @lockedBy, 
    B.LockedOn = @lockedOn, 
    B.UnlockOn = @unlockOn, 
    B.Complete = 1 
FROM 
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete 
    From 
    (
     SELECT ID, BranchGenerations, (BranchSize * EstimateCount * ProbeCount) as MultipliedFactor 
     FROM Objectives AS O WHERE (O.Active = 1) 
    )O 
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID 
    Inner Join 
    (
     Select Sealed, GenerationNo, LockedBy, UnlockOn, ID, Complete 
     From Branches 
     Where B.Sealed = 0 AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0) 
    )B ON B.GenerationID = G.ID 
    INNER JOIN 
    (
     Select * from #temp 
    ) AS X ON X.BranchID = B.ID 
    WHERE 
     AND (B.GenerationNo < O.BranchGenerations) 
     AND (B.Complete = 1 OR X.SuitableProbes = O.MultipliedFactor)   
) AS B 
+0

en dehors de quelques différences de syntaxe c'est la même que la solution que j'ai donnée ci-dessus. C'est bon de voir que nous pensons tous les deux dans le même sens. –

+0

Je répète, Dans mon cas, les jointures ne sont pas 100%, ce n'est que dans la mesure nécessaire. – Pankaj