Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Aktualisieren und Einfügen von Abfragen, die einen Deadlock erzeugen

Vermeiden Sie Cursor, diese Abfrage benötigte sie nicht. SQL ist nicht eine imperative Sprache (weshalb sie einen schlechten Ruf bekommt, weil jeder sie als solche verwendet ) - es ist eine festgelegte Sprache.

Das erste, was Sie tun können, ist die grundlegende Ausführung Ihrer SQL zu beschleunigen, weniger Zeit für das Analysieren/Ausführen der Abfrage bedeutet eine geringere Wahrscheinlichkeit eines Deadlocks:

  • Fügen Sie allen Ihren Tabellen [dbo] voran - Dadurch wird die Parsing-Phase um bis zu 30 % verkürzt.
  • Verwenden Sie für Ihre Tabellen einen Alias ​​- es verkürzt die Planungsphase ein wenig.
  • Anführungszeichen darf Dinge beschleunigen.
  • Dies sind Tipps von einem Ex-SQL-PM, bevor jemand beschließt, sie anzufechten.

Sie können ein CTE verwenden, um die Daten zu aktualisieren, und dann ein UPDATE ... FROM ... SELECT verwenden Anweisung, die eigentlichen Updates durchzuführen. Dies ist schneller als ein Cursor, da Cursor hundelangsam sind im Vergleich zu Clean-Set-Operationen (sogar der schnellste 'Feuerwehr'-Cursor wie Ihrer). Weniger Zeitaufwand für die Aktualisierung bedeutet weniger Deadlock-Wahrscheinlichkeit. Hinweis:Ich habe Ihre Originaltabellen nicht, ich kann das nicht validieren - also vergleichen Sie es mit einer Entwicklungs-DB.

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;

Darüber hinaus könnten Sie PAGE-Sperren auf Ihrem Index verbieten, dies verringert die Wahrscheinlichkeit, dass einige Zeilen eine ganze Seite sperren (aufgrund der Sperren-Eskalation muss nur ein bestimmter Prozentsatz von Zeilen gesperrt werden, bevor die gesamte Seite gesperrt ist). P>

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

Dies wird nur die Wahrscheinlichkeit eines Deadlocks mindern. Sie können versuchen, @now auf ein Datum in der Vergangenheit einzuschränken (z. B. today - 1 day). ), um sicherzustellen, dass die eingefügte Zeile nicht in das Aktualisierungsprädikat fällt; die Chancen stehen gut, dass es den Deadlock vollständig verhindert.