1

私はより複雑な 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# コードから 1 対 1 でコピーされていることに注意してください。実際には、そのパラメーターは上記のようにハードコーディングされていません。

このクエリはほとんどの場合機能しますが、ログに次のエラーが表示されることがあります。

トランザクション (プロセス ID 80) は、別のプロセスとのロック リソースでデッドロックされ、デッドロックの犠牲者として選択されました。トランザクションを再実行します。

このデッドロックを防ぐために何をすればよいでしょうか?

4

1 に答える 1

0

より多くのロックまたはより少ないロックを保持する必要があります。

NOLOCK最も簡単な答えは、 (最高のパフォーマンス)またはTABLOCKX(考える必要のない一貫性)のいずれかに行くことです。

整合性要件のために使用できない場合はwith (nolock)、を追加できwith (tablockx)ます。これは事実上、一度に1つのスレッドのみが同様のステートメントを実行できることを意味します-並行性はありません。

別の方法は、要件をより詳細に分析することです。これは、テーブルを更新する理由、データの目的などを理解しないと実行できません。

たとえば、このステートメントは本当にトランザクションに含まれている必要がありますか?それはハウスキーピングのようなにおいがします:

  DELETE FROM [dbo].[t_Log_2] 
  WHERE [idtm]<'2011-03-12 08:41:57';

それをトランザクションから取り出して別のバッチに入れると、問題が解決する場合があります。

于 2013-03-12T10:22:44.710 に答える