web-dev-qa-db-ja.com

存在しない場合は挿入しますが、いずれかの方法でIDを返します

私は3つのテーブルを持っています:audioFormats、videoFormats、およびfileInfo。 fileInfoテーブルに挿入すると、その挿入にaudioFormatsとvideoFormatsのFKが含まれるようなトランザクションがあります。後者のテーブルへの挿入は、オーディオ形式またはビデオ形式がまだそれらのテーブルにない場合に行われ、生成された(または既存の)ID値がfileInfoに挿入されます。

値が存在しない場合にのみ効率的に値を挿入し、値がすでに存在するか、SQL(およびおそらくトランザクション)のみを使用して新しく挿入されたかにかかわらず、値のIDを取得するにはどうすればよいですか。

値がまだ存在しない場合は、値を挿入できます。

INSERT INTO audioformats (audioformat)
VALUES(@format)
WHERE NOT EXISTS (SELECT 1 FROM audioformats WHERE audioformat = @format)

挿入から挿入IDを取得できます。

INSERT INTO audioFormats (audioFormat)
VALUES ('Test')
SET @audioFormatId = SCOPE_IDENTITY()

挿入が行われなかった場合、SCOPE_IDENTITYはID値を提供しません。スカラークエリを実行して、挿入の可能性の後にIDを取得できますが、最大で1つのSELECTとINSERTを使用してこれらすべてを実行できるはずです。

21
Charles Burns

これを行うには、IFステートメントを使用できます

IF NOT EXISTS(SELECT TOP 1 1 FROM audioformats WHERE audioformat = @format)
BEGIN
  INSERT INTO audioFormats (audioFormat)
  VALUES ('Test')
  SET @audioFormatId = SCOPE_IDENTITY()
END
ELSE
BEGIN
  SELECT @audioFormatID = ID FROM audioformats WHERE audioformat = @format
END

またはあなたはこのようにそれを行うことができます:

INSERT INTO audioformats (audioformat)
  SELECT @format
  FROM audioFormats
  WHERE NOT EXISTS (SELECT TOP 1 1 FROM audioformats WHERE audioformat = @format)

SELECT @audioFormatID = ID FROM audioformats WHERE audioformat = @format
13
Hogan

最終的なSQLに最も類似しているため、回答を正解としてマークしましたが、最大で1つのクエリと1つの挿入を使用するという元の要件を実際には満たしていませんでした。以下はそうです、そして私が使用することになったものです:

-- One select
SELECT @audioFormatId = id
FROM audioformats
WHERE audioformat = @audioFormat;

-- Optionally one insert
IF @audioFormatId IS NULL AND @audioFormat IS NOT NULL
BEGIN
    INSERT INTO audioFormats (audioFormat)
    VALUES (@audioFormat)
    SET @audioFormatId = SCOPE_IDENTITY()
END

IF NOT EXISTSクエリは実際に行を返すクエリよりも高速であるため、クエリへのほとんどの呼び出しが実際に挿入を実行する場合、ホーガンの答えはおそらく高速になります。

値がすでにほとんどの時間存在している場合は、正確に1つのクエリ(IF NOT EXISTSクエリは作成されない)が作成されるため、私の場合はおそらく高速になります。ヌルチェックは些細なことだと思います。

4
Charles Burns

これにはMERGEを使用できると思います。

これにより、トランザクションレスソリューションが提供されます。明るいsparkは、output句を使用してターゲットIDを取得することでこれを改善できる可能性がありますが、以下は問題なく機能します。

以下にStackOverflowクエリテスターへのリンクを追加しました。

-- Merge example
-- Get ID of existing row or insert new row

-- Initialise unit test data
declare @AudioFormatId int;
declare @AudioFormat nvarchar(50)
declare @tblAudioFormats TABLE (AudioFormatId int identity, AudioFormat nvarchar(50)   );
insert into @tblAudioFormats(AudioFormat) values ('MP3'), ('WAV');

-- set query criteria
set @AudioFormat = 'MP3' -- query below returns 1 - updating MP3
--set @AudioFormat = 'WAV' -- query below returns 2 - updating WAV
--set @AudioFormat = 'MIDI' -- query below returns 3 - inserting MIDI

-- Insert or update AudioFormat and return id of target audio format.
merge
  @tblAudioFormats as Target
using
  (select @AudioFormat as AudioFormat) as source(AudioFormat)
on 
  (source.AudioFormat = target.AudioFormat)
when matched then
  update set @AudioFormatID = target.AudioFormatId
when not matched then
  insert(AudioFormat) values (source.AudioFormat);

if @AudioFormatId is null set @AudioFormatId = scope_identity()

-- return ID of target audio format
select @AudioFormatId as TargetAudioFormatId

ここでこのクエリを実行します: サンプルのStackOverflowリンクをクエリします

3
Neil Moss

[〜#〜] unique [〜#〜] 制約を使用して、audioformat列が一意であることを保証し、コードを try /キャッチ

挿入してみてください。失敗した場合は、キャッチして新しいエントリをクエリし、既存のIDを返します。

最初にクエリを実行してみてください。ただし、バッチの開始から挿入までの間に誰かが挿入した場合、DBはとにかく例外をスローします。

AudioFormatsテーブルに自動インクリメントIDENTITY(1,1) PKがある場合は、次のコマンドを選択するだけで、挿入されたIDを取得できます。

SELECT MAX(ID) FROM audioFormats 

編集:

コメントで述べたように、このアプローチは、1つのクエリのみがテーブルに挿入される場合に適用できます。

それ以外の場合は、 IDENT_CURRENT( 'table_name') 関数を確認できます。

IDENT_CURRENT指定されたテーブルまたはビューに対して生成された最後のID値を返します。生成される最後のID値は、任意のセッションおよび任意のスコープのものです。

2
sll