web-dev-qa-db-ja.com

存在する場合は、他の挿入を選択してから選択

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が返される場合は空白のフィールド値を挿入し、対応する主キーを返すことです。

49
Phillip Senn

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を使用したトランザクションは、レコード(見つかった場合)をロックしますが、ギャップはロックしません。

60
zvolkov
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列であると仮定します

32
Jane

あなたは近かった:

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
7
David

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
2
Guffa

テーブルにキーがないようです。単にINSERTを試すことができるはずです:重複している場合、キー制約が噛み、INSERTが失敗します。心配する必要はありません。アプリケーションがエラーを認識または無視しないようにするだけです。 「主キー」と言うとき、おそらくIDENTITY値を意味します。それはすべて非常にうまくいきますが、自然キーにキー制約(UNIQUEなど)も必要です。

また、あなたの手順がやりすぎているのではないかと思います。それぞれ「作成」アクションと「読み取り」アクションに別々の手順を用意することを検討してください。

2
onedaywhen
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
1
Rob Garrison
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('')
1
Phillip Senn