2016-08-24 3 views
0

を作成するときに私は、データベース内の次の表ました:SQL Serverの2016 - ストアドプロシージャのロック既存のない記録に

------------------------------------------ 
| EventDefinitions      | 
------------------------------------------ 
| EventDefinitionId: uniqueidentifier | 
| Code:     varchar(63)  | 
| EventSystem:   int    | 
------------------------------------------ 

------------------------------------------ 
| Event         | 
------------------------------------------ 
| EventId:    uniqueidentifier | 
| EventDateTime:  datetime2  | 
| EventDefinitionId: uniqueidentifier | 
------------------------------------------ 

をEventDefinitions(1)< - イベント(N)
EventDefinitionが上で一意制約を持ちますイベントシステムとコード。

私はイベントレコードを作成し、指定されたコードとEventSystemに基づいて正しいEventDefinitionレコードにマップするストアドプロシージャに取り組んでいます。
一致するEventDefinitionが存在しない場合、プロシージャは新しいEventDefinitionを作成する必要があります。
この手順は、複数のアプリケーションによって同時に実行されます。

私の目標:

  • 手順は、並列実行可能でなければなりません。
    ロックをできるだけ少なくすることで、複数のイベントレコードを同時に書き込むことができます。
  • EventDefinitionが存在しない場合は、1つのプロセスで作成する必要があります。
    プロシージャは、指定されたCodeおよびEventSystemを持つEventDefinitionレコードが1回だけ作成されるようにロックを作成する必要があります(そうしないと、DBは一意の制約違反例外をスローします)。 他のプロセスは、Eventレコードを新しく作成したEventDefinitionレコードにマップする必要があります。ここで

私がこれまで試したものです:

CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int 
AS 
    DECLARE @eventDefinitionId uniqueidentifier 
    DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier) 
    DECLARE @currentLevel varchar(255) 
    BEGIN TRANSACTION 
     -- Try read event definition 
     SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode) 

     -- Create event definition if not exists 
     IF @eventDefinitionId IS NULL 
      BEGIN 
       BEGIN TRANSACTION 
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
        -- Check if event definition was created by an other process 
        SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode) 

        -- Create event definition 
        IF @eventDefinitionId IS NULL 
         BEGIN 
          INSERT INTO EventDefinitions (Code, EventSystem, Severity) 
           OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition 
           VALUES (@eventCode, @eventSystem, 0) 

          SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition) 
         END 
       COMMIT TRANSACTION 
       SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
      END 

     INSERT INTO Events (EventDefinitionId, EventDateTime) 
      VALUES (@eventDefinitionId, GETDATE()) 

    COMMIT TRANSACTION 

問題手続きを並列に実行されたとき、私の現在のSQLコードは、デッドロックが発生します。

答えて

1

コードが複雑すぎます。 SQLサーバーは、ほとんどのチェックとブロックを行います。 挿入/挿入を試みるだけで、UNIQUE制約違反で失敗した場合は、EventDefinitionsを再度読み込みます。種類:

CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int 
AS 
    DECLARE @eventDefinitionId uniqueidentifier 
    DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier) 
    DECLARE @currentLevel varchar(255) 
    -- Try read event definition 
    SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode) 
    -- Create event definition if not exists 
    IF @eventDefinitionId IS NULL 
    BEGIN TRY 
     INSERT INTO EventDefinitions (Code, EventSystem, Severity) 
     OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition 
     VALUES (@eventCode, @eventSystem, 0); 
     SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition) 
    END TRY 
    BEGIN CATCH 
     SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode) 
    END CATCH 
    -- proceed with @eventDefinitionId value 
+0

ありがとうございました。私はなぜそんなに簡単な問題を複雑な方法で解決しようとしなかったのですか^^ – musium

関連する問題