Deleting a row from self-referencing table
- by Jake Rutherford
Came across this the other day and thought “this would be a great interview question!” I’d created a table with a self-referencing foreign key. The application was calling a stored procedure I’d created to delete a row which caused but of course…a foreign key exception. You may say “why not just use a the cascade delete option?” Good question, easy answer. With a typical foreign key relationship between different tables which would work. However, even SQL Server cannot do a cascade delete of a row on a table with self-referencing foreign key. So, what do you do?…… In my case I re-wrote the stored procedure to take advantage of recursion: -- recursively deletes a Foo ALTER PROCEDURE [dbo].[usp_DeleteFoo] @ID int ,@Debug bit = 0 AS SET NOCOUNT ON; BEGIN TRANSACTION BEGIN TRY DECLARE @ChildFoos TABLE ( ID int ) DECLARE @ChildFooID int INSERT INTO @ChildFoos SELECT ID FROM Foo WHERE ParentFooID = @ID WHILE EXISTS (SELECT ID FROM @ChildFoos) BEGIN SELECT TOP 1 @ChildFooID = ID FROM @ChildFoos DELETE FROM @ChildFoos WHERE ID = @ChildFooID EXEC usp_DeleteFoo @ChildFooID END DELETE FROM dbo.[Foo] WHERE [ID] = @ID IF @Debug = 1 PRINT 'DEBUG:usp_DeleteFoo, deleted - ID: ' + CONVERT(VARCHAR, @ID) COMMIT TRANSACTION END TRY BEGIN CATCH ROLLBACK TRANSACTION DECLARE @ErrorMessage VARCHAR(4000), @ErrorSeverity INT, @ErrorState INT SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE() IF @ErrorState <= 0 SET @ErrorState = 1 INSERT INTO ErrorLog(ErrorNumber,ErrorSeverity,ErrorState,ErrorProcedure,ErrorLine,ErrorMessage) VALUES(ERROR_NUMBER(), @ErrorSeverity, @ErrorState, ERROR_PROCEDURE(), ERROR_LINE(), @ErrorMessage) RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState) END CATCH This procedure will first determine any rows which have the row we wish to delete as it’s parent. It then simply iterates each child row calling the procedure recursively in order to delete all ancestors before eventually deleting the row we wish to delete.