views:

46

answers:

3

Ok. I've tried the trigger, but it didn't work.

I have Cascades from A to Linker and from B to Linker, Cascade from Users to A, No Action from Users to B.

My trigger is on Users and is as follows:

set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go ALTER TRIGGER [trig_delUser] ON [dbo].[aspnet_Users] FOR DELETE AS SET NOCOUNT ON; DELETE FROM B WHERE B.UserId = Deleted.UserId

I get the exception: The DELETE statement conflicted with the REFERENCE constraint "FK_B_aspnet_Users"

I am working with a modified aspnetdb SQL database:

[Partial DB diagram][2]

I have cascade deletion on the B_Linker relationship and the A_Linker relationship and there doesn't seem to be any danger of cycles occuring.

When I delete a user, I would like all A entries and B entries to be deleted along with any associated linker entries; unfortunately, SQL mgmt studio will only let me put a cascade delete rule on EITHER aspnet_Users_A or aspnet_Users_B, not both.

What do I need to do?

Many Thanks.

[2]: http://i48.tinypic.com/2nsnc3k.png

A: 

You just have to put the ON DELETE CASCADE rules on the foreign keys.

klausbyskov
As I mentioned, I tried this, but SQL managenet studio won't let me.
KR
A: 

This is one of the unfortunate and frankly rather annoying limitations of SQL Server.

It's not the fact that there could be cycles that's the problem, it's simply as the error says - you have multiple cascade paths to the Linker table. The first is aspnet_Users -> A -> Linker and the second is aspnet_Users -> B -> Linker.

You only really have a couple of choices:

  • Choose one path to implement the CASCADE on and set the other to NO ACTION. Then write a Stored Procedure that deletes the non-cascaded child entities before deleting the parent entities in order to prevent a foreign key error. Or, don't CASCADE either and have your SP do the cascading for both.

  • Don't add a foreign key at all on the second relationship; instead, use a FOR DELETE trigger on the parent to delete the child entities. I very much dislike using triggers for RI, but this isn't much worse than the first option. In some ways it's better, because database clients don't have to worry about your specific implementation of the FK relationships.

Neither are ideal, but there is no perfect solution other than to change your design. If it is possible for you to change your schema such that you don't have multiple cascade paths, that would be the best thing to do - but I recognize that there are (many) real-world situations where this is not possible. Not knowing the specifics of your schema, I can't say for sure whether or not there's a more optimal design.

Aaronaught
I also need to be able to delete entries from A or B, deleting associated Linker entries at the same time. Will I need a proc/trigger for that too?
KR
@KR: Using the SP approach, yes. Using the trigger approach, you can create the trigger *only* on A or B; SQL Server won't complain about a cascade relationship from `aspnet_Users` to both `A` and `B` as long as at least one of them does not have the cascade to `Linker`.
Aaronaught
Ok. I've tried the trigger, but it didn't work. I have Cascades from A to Linker and from B to Linker, Cascade from Users to A, No Action from Users to B. My trigger is on Users and is as follows:set ANSI_NULLS ONset QUOTED_IDENTIFIER ONgoALTER TRIGGER [trig_delUser]ON [dbo].[aspnet_Users]FOR DELETEASSET NOCOUNT ON;DELETE FROM B WHERE B.UserId = Deleted.UserIdI get the exception:The DELETE statement conflicted with the REFERENCE constraint "FK_B_aspnet_Users"
KR
I've set Enforce Foreign Key Constraint to No on the relationship FK_B_aspnet_Users and it works. But somehow this makes me cringe. Is this bad, and is so is there a better way to do it?
KR
@KR: Yeah, as mentioned, if you use the trigger approach then you can't use an FK at all (or you can't enforce it). It makes me cringe too; if this bothers you then it might be better to enforce the FK but don't cascade, and route all the deletes through a Stored Procedure that deletes all the child entities first (my first bullet in the answer). AFAIK, those are (sadly) your only two options in SQL Server.
Aaronaught
A: 

I would not implement this through on delete cascade. Or through a trigger. What would happen if someone needed to delete 40,0000 records all at once. You would lock up the parent and all the child tables possibly for minutes possibly hours while it runs through and does it's thing.

So really what you should do is write the deletes to the child tables first then the delete to the parent table and put them in a transaction. This will work well enough when you are delting one record at a time. Large deletes should be written separately (don't loop throuhg the existing proc) and are another subject, but at least now you can do them without killing your server.

HLGEM
A and B will only ever contain 10 - 15 entries for each user, so I think a trigger is safe enough, but I'll keep it in mind for the future, thanks.
KR