Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

mettre à jour et insérer des requêtes créant un blocage

Évitez les curseurs, cette requête n'en avait pas besoin. SQL n'est pas un langage impératif (c'est pourquoi il a une mauvaise réputation parce que tout le monde l'utilise comme tel ) - c'est une langue définie.

La première chose que vous pouvez faire est d'accélérer l'exécution de base de votre SQL, moins de temps pour analyser/exécuter la requête signifie moins de risque d'impasse :

  • Préfixez toutes vos tables avec [dbo] - cela réduit jusqu'à 30 % l'étape d'analyse.
  • Créez un alias pour vos tables :cela réduit un peu l'étape de planification.
  • Citer des identifiants peut accélérer les choses.
  • Ce sont des conseils d'un ancien gestionnaire de projet SQL avant que quiconque ne décide de le contester.

Vous pouvez utiliser un CTE pour obtenir les données à mettre à jour, puis utiliser un UPDATE ... FROM ... SELECT déclaration pour faire les mises à jour réelles. Ce sera plus rapide qu'un curseur, car les curseurs sont lents comme des chiens par rapport aux opérations de nettoyage (même le curseur le plus rapide comme le vôtre). Moins de temps passé à mettre à jour signifie moins de risque de blocage. Remarque :Je n'ai pas vos tables d'origine, je ne peux pas valider ceci - alors vérifiez-le par rapport à une base de données de développement.

DECLARE @nowTime datetime = convert(datetime, @now, 21);

WITH [DailyAggregates] AS
(
    SELECT  
        [D].[dailyId] AS [dailyId],
        [D].[spentDaily] AS [spentDaily],
        [D].[impressionsCountCache] AS [impressionsCountCache],
        SUM([I].[amountCharged]) as [sumCharged],
        COUNT([I].[impressionId]) as [countImpressions]
        FROM [dbo].[Daily] AS [D]
            INNER JOIN [dbo].[Impressions] AS [I]
               ON [I].[dailyId] = [D].[dailyId]
        WHERE [I].[isCharged] = 0
          AND [I].[showTime] < @nowTime 
          AND [D].[isActive] = 1
    GROUP BY [D].[dailyId], [D].[spentDaily], [D].[impressionsCountCache]
)
UPDATE [dbo].[Daily]
    SET [spentDaily] = [A].[spentDaily] + [A].[sumCharged],
        [impressionsCountCache] = [A].[impressonsCountCache] + [A].[countImpressions]
    FROM [Daily] AS [D]
    INNER JOIN [DailyAggregates] AS [A]
       ON [D].[dailyId] = [A].[dailyId];

UPDATE [dbo].[Impressions]
SET [isCharged] = 1 
WHERE [showTime] < @nowTime 
  AND [isCharged] = 0;

De plus, vous pouvez interdire les verrous PAGE sur votre index, cela réduira les chances que quelques lignes verrouillent une page entière (en raison de l'escalade de verrouillage, seul un certain pourcentage de lignes doit être verrouillé avant que la page entière ne soit verrouillée).

CREATE NONCLUSTERED INDEX [IDX_Impressions_isCharged_showTime] ON [dbo].[Impressions]              
(
    [showTime] ASC, -- I have a hunch that switching these around might have an effect.
    [isCharged] ASC  
)
WITH (ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY] 
GO

Cela ne fera qu'atténuer les risques d'impasse. Vous pouvez essayer de restreindre @maintenant une date dans le passé (c'est-à-dire today - 1 day ) pour s'assurer que la ligne insérée ne tombe pas dans le prédicat de mise à jour ; il y a de fortes chances que cela prévienne complètement l'impasse.