Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

INSERT IF NOT EXISTS but return the identity either way

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.

like image 960
Charles Burns Avatar asked Nov 06 '11 21:11

Charles Burns


People also ask

How do you insert into if not exists?

There are three ways you can perform an “insert if not exists” query in MySQL: Using the INSERT IGNORE statement. Using the ON DUPLICATE KEY UPDATE clause. Or using the REPLACE statement.

How do I get the identity column value after insert?

Once we insert a row in a table, the @@IDENTITY function column gives the IDENTITY value generated by the statement. If we run any query that did not generate IDENTITY values, we get NULL value in the output. The SQL @@IDENTITY runs under the scope of the current session.


2 Answers

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 
like image 92
Hogan Avatar answered Sep 29 '22 20:09

Hogan


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.

like image 30
Charles Burns Avatar answered Sep 29 '22 18:09

Charles Burns