SQL Cascades
How to cascade UPDATEs and DELETEs to related tables¶
In most cases we use stored procedures to update or delete rows from related tables. But, you can also cascade the actions using foreign key constraints. This is generally not used due to the fact that it is transparent, and leads to mysterious results. Let’s see how it works.
Consider two different tables dbo.Albums and dbo.Tracks, they contains a list of Albums and a list of Tracks in each Album respectively. dbo.Tracks contains a foreign key constraint to AlbumID in dbo.Albums. Now, if we update an AlbumID in dbo.Albums, this also need to be updated in dbo.Tracks for all Tracks belonging to AlbumID.
Cascading can be defined for UPDATE and DELETE. There are four different options available:
- SET NULL:
This action specifies that the column will be set to NULL when the referenced column is updated/deleted.
- CASCADE:
CASCADE specifies that the column will be updated when the referenced column is updated, and rows will be deleted when the referenced rows are deleted.
- SET DEFAULT:
Column will be set to DEFAULT value when UPDATE/DELETE is performed on referenced rows.
- NO ACTION:
This is the default behavior. If a DELETE/UPDATE is executed on referenced rows, the operation is denied. An error is raised.
Let’s look at this with an example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
In the above example, AlbumID in dbo.Tracks references AlbumID in dbo.Albums. There are two cascading actions specified here.
ON DELETE SET NULL = When a row is deleted from dbo.Albums, AlbumID will be set to NULL for all matching rows in dbo.Tracks.
ON UPDATE CASCADE = When AlbumID is updated in dbo.Albums, all matching rows in dbo.Tracks will also have the updated AlbumID.
Now, let’s try and update an AlbumID in dbo.Albums:
1 2 3 4 5 6 7 8 | |
Result Set:
(1 row(s) affected)
1 2 3 4 5 6 7 | |
From the above output we can see that AlbumID has also been set to 2 in dbo.Tracks, where it was 4 earlier.
For delete also it works in the same way:
1 2 3 4 5 6 7 | |
Result Set:
(1 row(s) affected)
1 2 3 4 5 6 7 | |
You can check if any foreign key is defined in your database with cascading actions using sys.foreign_keys:
1 2 3 4 5 6 | |