2 つの同時クライアントが同じ fieldValue を 2 回挿入しないようにするには、トランザクションでこれを行う必要があります:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
DECLARE @id AS INT
SELECT @id = tableId FROM table WHERE [email protected]
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
SELECT @id
COMMIT TRANSACTION
ダブルチェック ロック も使用できます ロックのオーバーヘッドを減らす
DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE [email protected]
IF @id IS NULL
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @id = tableID FROM table WHERE [email protected]
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
COMMIT TRANSACTION
END
SELECT @id
ISOLATION LEVEL SERIALIZABLE が必要な理由については、シリアル化可能なトランザクション内にいる場合、テーブルにヒットする最初の SELECT によって、レコードがあるべき場所をカバーする範囲ロックが作成されるため、このトランザクションが終了するまで他の誰も同じレコードを挿入できません。
ISOLATION LEVEL SERIALIZABLE がないと、デフォルトの分離レベル (READ COMMITTED) は読み取り時にテーブルをロックしないため、SELECT と UPDATE の間で誰かが挿入できます。 READ COMMITTED 分離レベルのトランザクションでは、SELECT がロックされません。 REPEATABLE READS を使用したトランザクションは、レコード (見つかった場合) をロックしますが、ギャップはロックしません。