カーソルは避けてください。そのクエリではカーソルは必要ありませんでした。 SQLはありません 命令型言語(これが、誰もがそれを1つとして使用するために悪い名前になる理由です )-それは設定された言語です。
最初にできることは、SQLの基本的な実行を高速化することです。クエリの解析/実行にかかる時間が短いほど、デッドロックの可能性が低くなります。
- すべてのテーブルの前に
[dbo]
を付けます -これにより、解析段階が最大30%削減されます。 - テーブルにエイリアスを設定します-計画段階から少し切り取ります。
- 識別子を引用する可能性があります 物事をスピードアップします。
- これらは、誰かが異議を唱えることを決定する前の、元SQL-PMからのヒントです。
CTEを使用して更新するデータを取得してから、UPDATE ... FROM ... SELECT
を使用できます。 実際の更新を行うためのステートメント。カーソルはドッグスローであるため、これはカーソルよりも高速になります クリーンセット操作と比較した場合(あなたのような最速の「消火ホース」カーソルでさえ)。更新に費やす時間が少ないということは、デッドロックの可能性が少ないことを意味します。 注:元のテーブルがないため、検証できません。開発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;
さらに、インデックスでPAGEロックを禁止することもできます。これにより、数行がページ全体をロックする可能性が低くなります(ロックエスカレーションのため、ページ全体をロックする前に、特定の割合の行のみをロックする必要があります)。
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
これにより、デッドロックの可能性が軽減されます。 @nowを過去の日付に制限してみてください(つまり、today - 1 day
)挿入された行が更新述語に該当しないことを確認します。デッドロックを完全に防ぐ可能性があります。