Unikaj kursorów, to zapytanie ich nie potrzebowało. SQL nie język imperatywny (dlatego zyskuje złą sławę, ponieważ wszyscy używają go jako jednego ) - to ustawiony język.
Pierwszą rzeczą, którą możesz zrobić, to przyspieszyć podstawowe wykonanie twojego SQL, mniej czasu na analizowanie/wykonywanie zapytania oznacza mniejszą szansę na zakleszczenie:
- Poprzedź wszystkie tabele przedrostkiem
[dbo]
- zmniejsza to do 30% etapu analizy. - Zamień swoje tabele – odcina niewielką ilość od etapu planowania.
- Cytowanie identyfikatorów może przyspieszyć działanie.
- Są to wskazówki od ex-SQL-PM, zanim ktokolwiek zdecyduje się je zakwestionować.
Możesz użyć CTE, aby pobrać dane do aktualizacji, a następnie użyć UPDATE ... FROM ... SELECT
oświadczenie, aby dokonać rzeczywistych aktualizacji. Będzie to szybsze niż kursor, ponieważ kursory są wolne przez psa w porównaniu do czystych operacji ustawiania (nawet najszybszy kursor „węża strażackiego”, taki jak twój). Mniej czasu spędzonego na aktualizowaniu oznacza mniejszą szansę na zakleszczenie. Uwaga:nie mam twoich oryginalnych tabel, nie mogę tego zweryfikować - więc sprawdź to z rozwojową bazą danych.
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;
Co więcej, możesz zabronić blokowania PAGE w swoim indeksie, zmniejszy to szanse na zablokowanie całej strony przez kilka wierszy (ze względu na blokowanie eskalacji tylko pewien procent wierszy musi zostać zablokowany, zanim cała strona zostanie po prostu zablokowana).
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
To tylko zmniejszy szanse na impas. Możesz spróbować ograniczyć @now datę z przeszłości (np. today - 1 day
), aby upewnić się, że wstawiony wiersz nie należy do predykatu aktualizacji; są szanse, że całkowicie zapobiegnie impasowi.