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.
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With