I have been using the following pattern to validate FOREIGN KEY (FK) parameters for stored procedures which execute INSERTs:
CREATE PROC spCreateChildFoo
@ForeignKey_Id int,
@Attribute varchar(50)
AS
IF NOT EXISTS (SELECT ForeignKey_Id FROM ForeignTable WHERE ForeignKey_Id = @ForeignKey_Id)
BEGIN
RAISERROR ('The input ForeignKey_Id does not exist', 16, 1)
RETURN;
END
INSERT INTO ChildTable (ForeignKey_Id, Attribute)
VALUES (@ForeignKey_Id, @Attribute)
I did this because I wanted to catch bad FK entries early in the execution of the stored procedure before any data is INSERTed, especially for procs that carry out multiple INSERTs. I also thought it would be a good way to return accurate error messages to ease debugging. This pattern works well at serving those two goals.
Questions
-
Is this a waste of time?
-
More specifically, should I just rely on FK constraints to validate the FK parameters and rollback if it fails?
-
Is it ultimately going to hurt the performance of my application as the database grows?
Best Answer
Yes, this is a waste of time at least when your stored procedure is just the INSERT statement that may cause the foreign key violation. Where a foreign key is in place, validation of any attempted FK value is made against the reference table by the database engine itself.
If the value does not exist in the reference table, you will both receive an exception and have your INSERT statement aborted.
So, this would be perfectly enough:
On the other hand, if your procedure is more than a single INSERT statement and you would like to abort the entire procedure on an invalid insert at its beginning, then preliminarily checking the FK value may not be entirely pointless. However, valid FK values would still be causing double lookups, and that is neither good nor necessary. To achieve the same effect of aborting the entire procedure, you could just put your procedure's body into a TRY/CATCH block:
More info: