SQL Server - Maintain Referential Integrity without CASCADE and INSTEAD OF trigger - sql-server

I have a table (TableB) that has a foreign key relationship with a parent table (TableA).
When I delete a record in Table A, I want to preserve referential integrity by deleting all records in TableB that reference the deleted record in TableA.
Normally I would ON DELETE CASCADE. However due to the table structure and the overprotective safeguards against multiple cascade paths in SQL Server, this is not possible for this specific relationship.
I also cannot use an INSTEAD OF trigger as TableA itself has a CASCADE foreign key relationship on it.
What I'm thinking of doing is changing the relationship between TableA and TableB to ON DELETE SET NULL, then creating an AFTER trigger to cleanup the NULL records in TableB.
Are there better ways of dealing with this scenario?

Can you change the other constraint that is preventing you from adding this one as ON DELETE CASCADE?
Can you add a DeleteMe column, then issue an UPDATE A SET DeleteMe = 1, then use an after trigger to delete Table B rows first, then the requested table A rows?
Can you split or merge tables (vertically, that is) in some way that separates their mutually exclusive dependencies?

Related

How to delete record in Parent table without affecting the Child table?

I have 2 tables in my database, Table A and Table B.
Table A is a Master table and table B is a Transaction table. Table B have Foreign Key (IdTableA)
Example :
I already have a record in table B with IdTableA in it.
INSERT INTO Table_B VALUES (IdTableB, IdTableA, 500000);
and when I tried to delete record in Table A which is IdTableA there is an error.
I want to delete a record in Table A without affecting record in Table B
what should i do ? is it possible ?
You didn't post the error message, but most likely the IdTableA column is a foreign key to Table A. That means a row in Table A can't be deleted if any row in Table B references that row. There is no way to get around this except to permanently remove the foreign key.
The purpose of a foreign key is to prevent exactly this scenario. It prevents data integrity problems where rows would otherwise make no logical sense because other rows they reference do not actually exist.
http://en.wikipedia.org/wiki/Foreign_key

Cascade Update & Delete

I have a large database and I didn't add cascade on update/delete.
Can we do it with T-SQL without deleting and then recreating all the FK objects?
You should drop and recreate foreign keys.
this is the only way.
but you can use sys.foreign_keys and sys.foreign_key_columns to get the FK name and columns.
then you can write a loop to fetch names and columns and drop an recreate it with cascade option in every time loop executed.

Deleting related records in another table with "Where"-like considerations

I have a data table with a primary key called OptDefID. When a record in this table is deleted I need to go and delete all records from the Permissions table that have that OptDefID in the defID field (in Permissions). The tricky part for me is that the Permissions table does not have a primary key and holds lots of different kinds of permissions, and has a permissiontype field. I need to delete rows that have the OptDefID AND a permissiontype of OptDef.
Because I need to consider the permissiontype, I don't believe a Foreign Key Constraint is appropriate here (or is it?).
I've also considered creating a trigger, but am unsure how to get the OptDefID passed into the trigger.
I can do this via the application itself, but I feel like this should be a database level solution.
What's the best solution?
Say I want to delete from Permissions where defID is 20 and permissiontype is 'OptDef'. There may be another row in Permissions that has a defID of 20, but has a permissiontype of 'Member'. That show should not be deleted because it pertains to Members and not Opt data.
Storing table names in fields prevents foreign keys from working properly, as you have discovered.
I recommend you fix the root problem and separate these two foreign keys, so each of them can be individually enforced. For example:
CREATE TABLE Permissions (
...
OptDefId int,
MemberId int,
FOREIGN KEY (OptDefId) REFERENCES OptDef ON DELETE CASCADE,
FOREIGN KEY (MemberId) REFERENCES Members ON DELETE CASCADE,
CHECK (
(OptDefId IS NOT NULL AND MemberId IS NULL)
OR (OptDefId IS NULL AND MemberId IS NOT NULL)
)
)
The CHECK makes sure only one of the FKs is non-NULL and only non-NULL FKs are enforced.
Alternatively, you could avoid changing your current design and enforce this "special" FK through a trigger, but declarative constraints should be preferred to triggers when possible - declarative constraints tend to leave less room for error and be more "self-documenting".
In case the OptDefId column is only filled when the record in question references the Permissions table, a foreign key should be appropriate. I.e. you have another column MemberId, which in turn could be a foreign key on a Members table.
It is only when you have a single column - let's call it ObjectId - which takes on other meanings as the contents of the type column change, that you cannot use foreign keys.
In that case, a trigger would probably be the best approach, as you already guessed. I only know about triggers in Oracle PL/SQL, where they are passed in as separate, complete rows representing the old and new state. I guess it will be analogous in MS-SQL-Server.
In addition to using join with SELECT statements, you can also join multiple tables in DELETE & UPDATE statements as well.
As I understand the issue, you should be able to join the Permissions table to the table with the OptDefID column & add a WHERE clause similar to the this:
DELETE MyTable
...
WHERE [Permissions].permissiontype = 'OptDef'
Also, these links may be of interest too:
SQL DELETE with JOIN another table for WHERE condition (for MySQL, but still relevant)
Using A SQL JOIN In A SQL DELETE Statement
Using A SQL JOIN In A SQL UPDATE Statement

foreign key data deletion should display error for primary key

i have two tables Table1 and Table2. Where table1 column primary key is referred to table2 column as foreign key.
Now i have to display a error message of constraint violation when ever i delete records from table2 which is having foreign key column of table1.
If I get it right your column A (say) in table 1 references column B (say) in table 2.
What you can do is set the ON DELETE to NO ACTION which will prevent deletion of records from table 2 if any children of it still exists in table 1.
You can can do this by:
ALTER TABLE TABLE1 ADD FOREIGN KEY (A) REFERENCES TABLE2 (B) ON DELETE NO ACTION;
You don't have a constraint violation if you delete records from the child table and not the parent. It is normal to delete child records. For instance if I have a user table and a photos tables that contains the userid from the users table, why would I want to stop that action and throw an error if I want to delete a photo? Deleting a child record doesn't also delete the parent.
If you really want to do that, then you must do it through a trigger (make sure to handle multiple record deletes) or if the FK is a required field, then simply don't grant permissions to delete to the table. Be aware that this may mean you can never delete any records even when you try to delete. A simple method may be to not have a delete function available in the application.
I suspect what you really need to a to get a better definition of what is needed in the requirements document. In over 30 years of dealing with hundreds of databases, I have never seen anyone need this functionality.

SQL Server update primary key that's also a foreign key in two tables

I need to update the primary key for a record but it's also the foreign key in two other tables. And I need the updated primary key to be reflected in the child tables as well.
Here is my query and the error:
begin tran
update question set questionparent = 10000, questionid= 10005 where questionid = 11000;
Error 9/4/2009 10:04:49 AM 0:00:00.000 SQL Server Database Error: The UPDATE statement conflicted with the REFERENCE constraint "FK_GoalRequirement_Question". The conflict occurred in database "numgmttest", table "dbo.GoalRequirement", column 'QuestionID'. 14 0
I don't remember how to go about doing this so that's why I'm here. Any help?
Are your relationships using
ON UPDATE CASCADE
If they are then changing the key in the primary table will update the foreign keys.
e.g.
ALTER TABLE Books
ADD CONSTRAINT fk_author
FOREIGN KEY (AuthorID)
REFERENCES Authors (AuthorID) ON UPDATE CASCADE
You may:
disable enforcing FK constraints temporarily (see here or here)
update your PK
update your FKs
enable back enforcing FK constraints
do it all within a transaction and make sure that if transaction fails, you roll it back properly and still enforce the FK constraints back.
But... why do you need to change a PK? I hope this is an action that is executed rarely (legacy data import or something like that).
If you would like to set the Cascade rule graphically then Set Cascade Rule on SQL Management Studio
Open table in design mode
Click Relationship button from top toolbar
Select the required FK relations (one by one)
Right Side - Expand INSERT or UPDATE Specification
Change the UPDATE Rule to - Cascade
Close and Save, Done!
(Tried on SQL 2008)
As I'm not too confident disabling FK constraints, I prefer too :
Duplicate the row with the old PK with one with the new PK
Update the FKs
Delete the row with the old PK
Advantage : No constraint violated during the process.
Go to foreign Key Relations of each child tables and on Insert and Update specification change delete and update rules to cascade.
create a New row with the same data and a different primary key.
update all the children tables.
remove the row that you repeated its data
And its done.

Resources