【发布时间】:2013-03-12 09:17:03
【问题描述】:
我正在学习更复杂的 SQL Server 2008 技术,所以如果我问了一个太明显的问题,我提前道歉。
我创建了下表:
CREATE TABLE [dbo].[t_Log_2]
(
[id] INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
[oid] INT,
[idtm] DATETIME2,
[odtm] DATETIME2,
[type] TINYINT,
[state] TINYINT,
[huid] UNIQUEIDENTIFIER,
[cnm] NVARCHAR(256),
[cmdl] NVARCHAR(256),
[batt] TINYINT,
[dvtp0] SMALLINT,
[dvtp1] SMALLINT
);
CREATE INDEX idx_idt
ON [dbo].[t_Log_2]([idtm]);
CREATE INDEX idx_odt
ON [dbo].[t_Log_2]([odtm]);
CREATE INDEX idx_huid
ON [dbo].[t_Log_2]([huid]);
CREATE INDEX idx_cnm
ON [dbo].[t_Log_2]([cnm]);
然后可以从 ASP.NET Web 应用程序的 多个同时线程 运行以下查询。请注意,整个查询需要原子地运行:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DELETE FROM [dbo].[t_Log_2]
WHERE [idtm]<'2011-03-12 08:41:57';
WITH ctx AS(
SELECT MIN([idtm]) AS mdIn,
MAX([odtm]) AS mdOut
FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [state] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [odtm] >= '2013-03-11 06:33:32'
AND [idtm] <= '2013-03-11 06:43:12'
)
INSERT INTO [dbo].[t_Log_2]
([oid],[idtm],[odtm],[type],[state],[huid],
[cnm],[cmdl],[batt],[dvtp0],[dvtp1])
SELECT
2,
CASE WHEN mdIn IS NOT NULL
AND mdIn < '2013-03-11 06:33:32'
THEN mdIn
ELSE '2013-03-11 06:33:32'
END,
CASE WHEN mdOut IS NOT NULL
AND mdOut > '2013-03-11 06:43:12'
THEN mdOut
ELSE '2013-03-11 06:43:12'
END,
0,
0,
N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4',
null,
null,
0,
1,
null
FROM ctx
SELECT ROWCOUNT_BIG()
DELETE FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [state] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [odtm] >= '2013-03-11 06:33:32'
AND [idtm] <= '2013-03-11 06:43:12'
AND [id] <> SCOPE_IDENTITY()
DELETE FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] >= (SELECT [idtm] FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY())
AND [odtm] <= (SELECT [odtm] FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY())
AND [id] <> SCOPE_IDENTITY()
;WITH ctx1 AS(
SELECT [idtm] AS dI
FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY()
)
UPDATE [dbo].[t_Log_2]
SET [odtm] = ctx1.dI
FROM ctx1
WHERE [id] <> SCOPE_IDENTITY()
AND [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] < ctx1.dI
AND [odtm] > ctx1.dI
;WITH ctx2 AS(
SELECT [odtm] AS dO
FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY()
)
UPDATE [dbo].[t_Log_2]
SET [idtm] = ctx2.dO
FROM ctx2
WHERE [id] <> SCOPE_IDENTITY()
AND [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] < ctx2.dO
AND [odtm] > ctx2.dO
COMMIT TRANSACTION;
SET XACT_ABORT OFF
请注意,上面的查询是从动态组合它的 C# 代码中一对一复制的。实际上,它的参数并不是如上所示的硬编码。
此查询在大多数情况下都有效,但偶尔我会在日志中收到以下错误:
事务(进程 ID 80)在锁定资源上死锁 另一个进程并已被选为死锁受害者。重新运行 交易。
知道我应该怎么做才能防止这种死锁吗?
【问题讨论】:
-
在您的选择中使用 UPDLOCK 提示。
-
捕获并附加死锁图(XML,不是它的图片!)见Save Deadlock Graphs (SQL Server Profiler)
-
@Arvo:你能再解释一下吗?
-
我在某些情况下遇到死锁,其中(内部事务)我最初从表中选择一些值,然后更新同一个表。在第一次选择中使用 UPDLOCK 提示 (select ... from ... with(updlock)) 在选择时已经将 IX 类型锁放置在受影响的记录上,其他事务将等到该记录完成。对于我们的麻烦,UPDLOCK 提示解决了这个问题。
-
另外,免费建议,将
SCOPE_IDENTITY()保存到一个变量中以便于维护,而不是在任何地方直接使用它。
标签: sql sql-server sql-server-2008 deadlock database-deadlocks