INSERT IF NOT EXISTS 但以任何方式返回身份

发布于 2024-12-14 06:22:17 字数 675 浏览 0 评论 0 原文

我有 3 个表:audioFormats、videoFormats 和 fileInfo。 我有一个事务,当我插入 fileInfo 表时,该插入包含来自 audioFormats 和 videoFormats 的 FK。如果音频格式或视频格式尚未在这些表中,则会插入到后面的表中,然后将生成的(或现有的)ID 值插入到 fileInfo 中。

如何仅在该值不存在时有效地插入该值,同时获取该值的 ID(无论该值已存在还是仅使用 SQL(可能是事务)新插入)。

如果值尚不存在,我可以插入它:

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 值。 我可以执行标量查询来在可能的插入后获取标识,但似乎我应该能够使用最多一个 SELECT 和 INSERT 来完成所有这些操作。

I have 3 tables: audioFormats, videoFormats, and fileInfo.
I have a transaction such that when I insert into the fileInfo table, that insert includes an FK from audioFormats and videoFormats. An insertion into the latter tables takes place if the audio format or video format are not already in those tables, then the generated (or existing) ID value is inserted into fileInfo.

How do I efficiently insert a value only if that value does not exist, but get the ID of the value whether it already exists or was freshly inserted using only SQL (and perhaps a transaction).

I can insert a value if it does not already exist:

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

I can get the inserted ID from an insertion:

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

SCOPE_IDENTITY won't give me an ID value if no insertion took place.
I can execute a scalar query to get the identity after a possible insertion, but it seems like I should be able to do all of this with at most one SELECT and INSERT.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

萌辣 2024-12-21 06:22:17

您可以使用 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

You can use an IF statement to do this

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

or you could do it like this:

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
国产ˉ祖宗 2024-12-21 06:22:17

我将一个答案标记为正确,因为它与我最终的 SQL 最相似,但它并没有真正满足最多使用一次查询和一次插入的原始要求。以下是我最终使用的:

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

当大多数对查询的调用实际上执行插入时,Hogan 的答案可能会更快,因为 IF NOT EXISTS 查询比实际执行的查询更快返回行。

在大多数情况下值已经存在的情况下,我的可能会更快,因为这样只会执行一个查询(并且不会执行 IF NOT EXISTS 查询)。我怀疑空检查是微不足道的。

I marked an answer as correct because it was the most similar to my final SQL, but it did not really fulfill the original requirement of using at most one query and one insert. The following does, and is what I ended up using:

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

Hogan's answer will probably be faster when most calls to the query do in fact perform an insertion because an IF NOT EXISTS query is faster than one that actually does return rows.

Mine will probably be faster in cases where the value already exists most of the time because then exactly one query (and no IF NOT EXISTS query) will be made. I suspect the null check is trivial.

圈圈圆圆圈圈 2024-12-21 06:22:17

我认为你可以使用 MERGE 来实现这一点。

这为您提供了无交易的解决方案。一些聪明的火花可能能够使用 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 链接

I think you can use MERGE for this.

This gives you a transactionless solution. Some bright spark might be able to improve this using the output clause to get the target id, but below will work just fine.

I've added a link to the StackOverflow query tester below.

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

Run this query here: Query StackOverflow link for sample

说谎友 2024-12-21 06:22:17

如果audioFormats表具有自动增量IDENTITY(1,1) PK,您可以通过简单的选择来获取刚刚插入的ID:

SELECT MAX(ID) FROM audioFormats 

编辑:

正如评论中提到的,这种方法适用于只有一个查询插入表。

否则,您可以查看 IDENT_CURRENT('table_name') 函数。

IDENT_CURRENT 返回为某个对象生成的最后一个标识值
指定的表或视图。最后生成的身份值可以是
任何会话和任何范围。

If audioFormats table has autoincrement IDENTITY(1,1) PK you can get just inserted ID by simple select:

SELECT MAX(ID) FROM audioFormats 

EDIT:

As was mentioned in comment this approach is applicable when only one query inserting into a table.

Otherwise you can take a look at the IDENT_CURRENT('table_name') function.

IDENT_CURRENT Returns the last identity value generated for a
specified table or view. The last identity value generated can be for
any session and any scope.

别低头,皇冠会掉 2024-12-21 06:22:17

您需要通过 audioformat 列是唯一的>UNIQUE 约束,并将代码放入 尝试/捕获

尝试插入。如果失败,则捕获,查询新条目,并返回现有 ID。

你可以先尝试查询一下。但是,如果有人在批处理开始和插入之间插入,数据库无论如何都会抛出异常。

You'll need to guarantee your audioformat column is unique via a UNIQUE constraint, and put your code in a try/catch.

Try to insert. If it fails, then catch, query for the new entry, and return the existing ID.

You could try querying first. But if someone inserts between the start of your batch and when you insert, the DB will throw an exception anyhow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文