Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

aggiornare e inserire query creando un deadlock

Evita i cursori, quella query non ne aveva bisogno. SQL non un linguaggio imperativo (ecco perché ottiene una cattiva reputazione perché tutti lo usano come tale ) - è una lingua impostata.

La prima cosa che puoi fare è accelerare l'esecuzione di base del tuo SQL, meno tempo per analizzare/eseguire la query significa meno possibilità di un deadlock:

  • Prefissa tutte le tue tabelle con [dbo] - questo riduce fino al 30% di sconto sulla fase di analisi.
  • Alias ​​i tuoi tavoli:elimina una piccola quantità dalla fase di pianificazione.
  • Gli identificatori di virgolette possono accelerare le cose.
  • Questi sono suggerimenti di un ex-PM SQL prima che qualcuno decida di contestarlo.

Puoi utilizzare un CTE per ottenere i dati da aggiornare e quindi utilizzare un UPDATE ... FROM ... SELECT dichiarazione per fare gli aggiornamenti effettivi. Sarà più veloce di un cursore, perché i cursori sono lento rispetto alle operazioni di set pulito (anche il cursore "manica antincendio" più veloce come il tuo). Meno tempo dedicato all'aggiornamento significa meno possibilità di un deadlock. Nota:non ho le tue tabelle originali, non posso convalidarlo, quindi confrontalo con un DB di sviluppo.

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;

Inoltre potresti non consentire i blocchi PAGE sul tuo indice, questo ridurrà le possibilità che alcune righe blocchino un'intera pagina (a causa dell'escalation del blocco, solo una certa percentuale di righe deve essere bloccata prima che l'intera pagina sia bloccata).

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

Questo attenuerà solo le possibilità di una situazione di stallo. Potresti provare a limitare @now una data nel passato (ad esempio today - 1 day ) per assicurarsi che la riga inserita non rientri nel predicato di aggiornamento; è probabile che eviterà completamente lo stallo.