Microsoft SQL Server 2005では次のように言います。
IF EXISTS (SELECT * FROM Table WHERE FieldValue='') THEN
SELECT TableID FROM Table WHERE FieldValue=''
ELSE
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END IF
私がやろうとしているのは、空白のフィールド値が既にあるかどうかを確認し、そのテーブルIDが返される場合は空白のフィールド値を挿入し、対応する主キーを返すことです。
2つの同時クライアントが同じfieldValueを2回挿入しないようにするには、トランザクションでこれを行う必要があります。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
DECLARE @id AS INT
SELECT @id = tableId FROM table WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (@newValue)
SELECT @id = SCOPE_IDENTITY()
END
SELECT @id
COMMIT TRANSACTION
Double-checked locking を使用して、ロックのオーバーヘッドを減らすこともできます。
DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @id = tableID FROM table WHERE fieldValue=@newValue
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を使用したトランザクションは、レコード(見つかった場合)をロックしますが、ギャップはロックしません。
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='')
BEGIN
SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT SCOPE_IDENTITY() AS TableID
END
IF ELSEの詳細については、 here を参照してください
注:これを再確認するのに便利なSQL Serverインストールなしで書かれていますが、正しいと思います
また、EXISTS内で何が返されるかを気にしないため、SELECT *ではなくSELECT 1を実行するようにEXISTSビットを変更しました。 TableIDがID列であると仮定します
あなたは近かった:
IF EXISTS (SELECT * FROM Table WHERE FieldValue='')
SELECT TableID FROM Table WHERE FieldValue=''
ELSE
BEGIN
INSERT INTO TABLE (FieldValue) VALUES ('')
SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END
if...else..endif
の構造を多少変更する必要があります。
if exists(select * from Table where FieldValue='') then begin
select TableID from Table where FieldValue=''
end else begin
insert into Table (FieldValue) values ('')
select TableID from Table where TableID = scope_identity()
end
次のこともできます。
if not exists(select * from Table where FieldValue='') then begin
insert into Table (FieldValue) values ('')
end
select TableID from Table where FieldValue=''
または:
if exists(select * from Table where FieldValue='') then begin
select TableID from Table where FieldValue=''
end else begin
insert into Table (FieldValue) values ('')
select scope_identity() as TableID
end
テーブルにキーがないようです。単にINSERT
を試すことができるはずです:重複している場合、キー制約が噛み、INSERT
が失敗します。心配する必要はありません。アプリケーションがエラーを認識または無視しないようにするだけです。 「主キー」と言うとき、おそらくIDENTITY
値を意味します。それはすべて非常にうまくいきますが、自然キーにキー制約(UNIQUE
など)も必要です。
また、あなたの手順がやりすぎているのではないかと思います。それぞれ「作成」アクションと「読み取り」アクションに別々の手順を用意することを検討してください。
DECLARE @t1 TABLE (
TableID int IDENTITY,
FieldValue varchar(20)
)
--<< No empty string
IF EXISTS (
SELECT *
FROM @t1
WHERE FieldValue = ''
) BEGIN
SELECT TableID
FROM @t1
WHERE FieldValue=''
END
ELSE BEGIN
INSERT INTO @t1 (FieldValue) VALUES ('')
SELECT SCOPE_IDENTITY() AS TableID
END
--<< A record with an empty string already exists
IF EXISTS (
SELECT *
FROM @t1
WHERE FieldValue = ''
) BEGIN
SELECT TableID
FROM @t1
WHERE FieldValue=''
END
ELSE BEGIN
INSERT INTO @t1 (FieldValue) VALUES ('')
SELECT SCOPE_IDENTITY() AS TableID
END
create schema tableName authorization dbo
go
IF OBJECT_ID ('tableName.put_fieldValue', 'P' ) IS NOT NULL
drop proc tableName.put_fieldValue
go
create proc tableName.put_fieldValue(@fieldValue int) as
declare @tableid int = 0
select @tableid = tableid from table where fieldValue=''
if @tableid = 0 begin
insert into table(fieldValue) values('')
select @tableid = scope_identity()
end
return @tableid
go
declare @tablid int = 0
exec @tableid = tableName.put_fieldValue('')