I’m trying to write a query that would allow you to insert a new Room in a Building, but fail if that building ID does not exist
INSERT INTO room (
building_id, room_name
) VALUES (
$1,
CONCAT('Room ', (SELECT count(1) +1 FROM room WHERE building_id = $1))
)
RETURNING building_id, id, room_name, version
Currently you can do the insert with any random building id and it would end up as garbage data in the room table and lots of Room 1
entries with non-existing building ids.
Trying something like this (obviously inside a transaction) works, but what happens if the building gets deleted just after the SELECT runs before the INSERT happens?
INSERT INTO room (
building_id,
room_name
)
SELECT
$1,
CONCAT(
'Room ',
(SELECT count(1) + 1
FROM room AS r
WHERE r.building_id = $1)
)
FROM building AS b
WHERE b.id = $1
RETURNING building_id, id, room_name, version
It’s very unlikely though, but I’m still curious if it can be avoided.
Can this query be improved to completely avoid such concurrency issues or is there a way to use foreign keys to enforce the room insert to always have a valid building_id when the transaction commits or maybe some other novel solution?
2
Answers
This was done on SQL Server 2022. I could not think of a cleaner way to do this, but it works in all of my tests. It actually goes above and beyond your request in that it also checks if that room already exists in the building. So, in short, if the building exists, and that room does not exist, it will do the insert (that is the goal here of this sql).
I also agree with the folks on referential integrity. However folks, we do not know what this person’s permission is on the database, nor if they already asked to have this done, or maybe they’re a student working on a midterm project. This is EXAMPLE code.
A simple constraint would stop a room from being inserted if the building did not exists.
Try adding a constraint – something like this would give you something to test with.
then the following code will fail.
which results with the error
Here is a SQLFiddle link to play with:
http://sqlfiddle.com/#!17/babbf7/1