Why is on delete cascade impossible for nullable columns? - sql-server

I was trying to create a table via SSMS 11.0.6020.0 using SQL Server, via the table designer. I have defined the primary key, the columns and foreign keys. All was well, but when I have hit Ctrl+s to save the script, I received an error message stating that On Delete Cascade may cause cycles and change the remove behavior. I have followed the instructions given and changed the on delete and on update to do nothing for the nullable column used as foreign key, but I wonder what is the problem with on delete cascade and on update cascade for nullable foreign key.
My question is related to this one, but as far as I know it is not a duplicate, since I have a single reference to a given table and the problem was that it was nullable and the other question is about multiple references. The error message is similar though.
I believe that if that column is null for a record, then it does not have a reference in the referenced table, therefore there is no reference which could trigger delete/update on that record. However, if there is a reference, that could act as if the column was not nullable in that case. Is there an objective reason for this behavior (like performance, indexing, etc.)? I can simply not see the possible cycles when a nullable column is used as a foreign key, as the null values should be immune to changes at the reference table according to my opinion.

Related

Update table primary key and propagate changes to other tables

I have Table A with Column A1Id as primary key, Column A1 and Column A2.
I have Table B with Column A1Id as foreign key, Column B1, and Column B2.
I have Table C with Column AI1d as foreign key, Column C1, and Column C2.
How can i update all the columns with a single command?
How can i propagate changes to all foreign keys if i update the primary key?
I'm new to SQL is this a trivial task?
I'm currently having difficulty updating a single table because of the foreign key constraint.
How can i update all the columns with a single command?
How can i propagate changes to all foreign keys if i update the primary key?
is this a trivial task?
You can't do this with a single command, it is not a trivial task, it typically takes a script of sql commands with the changes repeated for each table; usually involving UPDATEs and new columns to "migrate" the old data to the new format. Changing primary keys is something that is done very infrequently, and is almost always considered a major change even when the specifics of that change are fairly minor.
The closest (physical) analogy I can think of offhand is trying to take something apart and replace all it's screws with bolts.
Edit: If you are just talking about changing the values, not the fields or data type of the values, used as primary keys; then R VISHAL's answer is pointing in the right direction. You'll need to drop all your current constraints and recreate them to cascade the updates. You can also disable foreign key checks, change the values and then re-enable foreign key checks; but that is generally not recommended as re-enabling them does not recheck them, so if your updates put the data in an inconsistent state, it will not be corrected or even detected; and the setting is global, so any other activity going on will also be free to ignore their own fk constraints.
... but you shouldn't typically be changing primary key values either. If the value changes, it probably isn't a good candidate for a foreign key; and if it is a synthetic value (like an auto-incremented id) then you should almost never be changing it (many newer users are tempted to try to "condense" them when holes appear, but it is seldom a good idea.)
If, in your case, table B(A1id) and table C(A1id) both reference table A(A1id), then to propagate either update or delete operations to all foreign keys of tables B,C they should have the CASCADE constraint in them. In MySQL, for example, you could do this:
ALTER TABLE B
ADD CONSTRAINT FKA
FOREIGN KEY B(A1id)
REFERENCES A(A1id)
ON UPDATE CASCADE
ON DELETE CASCADE
Notice the word cascade. Is this what u want or am I missing something here? :/

An exception of type 'System.InvalidOperationException' occurred in EntityFramework

I am trying to remove all data from a table and re-add it, but I get the below exception:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code Additional information: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
At first I though it was pretty obvious, until I looked at my database (below). To check if I was missing something I deleted all relevant rows in SSMS successfully. If I can delete the rows without foreign key constraints in SQL, why cant I do it in EF?
EDIT:
I am trying to delete from the rooms table
Based on the error message and that you are attempting to delete from the Rooms table, it seems likely that you have an enforced foreign key constraint on the Images table tied to RoomID.
You can determine what relationship is causing the issue by right-clicking the Rooms table in SSMS and clicking "Show Dependencies". This will show you all of the schema-bound dependencies which will be based on foreign keys.
Assuming that is the case, deleting those rows from Rooms would orphan the records in Images or potentially tie them to the records you are inserting (if doing an identity insert), not certain what your intention is. You have two options if you wish to proceed:
Delete the records constrained by the foreign key.
Disable the enforcement of the foreign key in SSMS by right-clicking the foreign key, click Modify, and set "Enforce Foreign Key Contraint" to "No". You may also need to set the Insert and Update specifications to No Action.
Ideally you are just deleting some records and removing the constraint is not necessary, since that compromises the referential integrity of your database.

Can't work around this foreign key constraint rule using TRIGGER in SQLite?

First, I want to talk a little about the Foreign key constraint rule and how helpful it is. Suppose I have two tables, a primary table with the primary column called ID, the other table is the foreign one which also has a primary column called ID. This column in the foreign table refers to the ID column in the primary table. If we don't establish any Foreign key relation/constraint between those tables, we may fall foul of many problems related to integrity.
If we create the foreign key relation for them, any changes to the ID column in primary table will 'auto' reflect to the ID column in the foreign table, changes here can be made by DELETE, UPDATE queries. Moreover, any changes to the ID in the foreign table should be constrained by the ID column in the primary table, for example there shouldn't any new value inserted or updated in the ID column of the foreign table unless it does exist in the ID column of the primary table.
I know that SQLite doesn't support foreign key constraint (with full functions as detailed above) and I have to use TRIGGER to work around this problem. I have used TRIGGER to work around successfully in one way (Any changes to the ID column in the primary table will refect to the ID column in the foreign table) but the reverse way (should throw/raise any error if there is a confict occurs, for example, there are only values 1,2,3 in the ID column of the primary table, but the value 2 in the ID column of the foreign table is updated to 4 -> not exist in the primary table -> should throw error) is not easy. The difficult is SQLite doesn't also support IF statement and RAISERROR function. If these features were supported, I could work around easily.
I wonder how you can use SQLite if it doesn't support some important features? Even working around by using TRIGGER is not easy and I think it's impossible, except that you don't care about the reverse way. (In fact, the reverse way is not really necessary if you set up your SQL queries carefully, but who can make sure? Raising error is a mechanism reminding us to fix and correct and making it work exactly without corrupting data and the bugs can't be invisible.
If you still don't know what I want, I would like to have some last words, my purpose is to achieve the full functionality of the Foreign key constraint which is not supported in SQLite (even you can create such a relationship but it's fake, not real as you can benefit from it in SQL Server, SQL Server Ce, MS Access or MySQL).
Your help would be highly appreciated.
PS: I really like SQLite because it is file-based, easy to deploy, supports large file size (an advantage over SQL Server Ce) but some missing features have made me re-think many times, I'm afraid if going for it, my application may be unreliable and corrupt unpredictably.
To answer the question that you have skillfully hidden in your rant:
SQLite allows the RAISE function inside triggers; because of the lack of control flow statements, this must be used with a SELECT:
CREATE TRIGGER check_that_id_exists_in_parent
BEFORE UPDATE OF id ON child_table
FOR EACH ROW
BEGIN
SELECT RAISE(ABORT, 'parent ID does not exist')
WHERE NOT EXISTS (SELECT 1
FROM parent_table
WHERE id = NEW.id);
END;

SQL Server Foreign Key cause cycles or multiple cascade paths

I'm having problems adding a cascade delete onto a foreign key in SQL Server. Table A has three columns. Column 1 and 2 in Table A are foreign key look ups to the same column in Table B. I want a delete of a row in Table B to cascade a delete on a row on Table A based on these foreign keys.
The other column in Table A has a foreign key lookup to table C. If a row in table C is deleted then I want the corresponding cell to be set to null in Table A.
When I add in these constraints I am thrown the error:
Introducing FOREIGN KEY constraint 'FK_RDU_TODELIVERABLEUNITREF' on table 'RelatedDeliverableUnit' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I am a little stuck with this, Oracle seems perfectly happy with this logic. I am adding in these constraints using Liquibase. I think the error is down to my logic and not syntax but for completeness here is the liquidbase script that manages the foreign keys:
<addForeignKeyConstraint constraintName="FK_RDU_FROMDELIVERABLEUNITREF" baseTableName="relatedDeliverableUnit"
baseColumnNames="FROMDELIVERABLEUNITREF" referencedTableName="DELIVERABLEUNIT" referencedColumnNames="DELIVERABLEUNITREF" onDelete="CASCADE"/>
<addForeignKeyConstraint constraintName="FK_RDU_TODELIVERABLEUNITREF" baseTableName="relatedDeliverableUnit"
baseColumnNames="TODELIVERABLEUNITREF" referencedTableName="DELIVERABLEUNIT" referencedColumnNames="DELIVERABLEUNITREF" onDelete="CASCADE"/>
<addForeignKeyConstraint constraintName="FK_RDU_RELATIONSHIPREF"
baseTableName="relatedDeliverableUnit" baseColumnNames="RELATIONSHIPREF" referencedTableName="RELATIONSHIPTYPES" referencedColumnNames="RELATIONSHIPREF" onDelete="SET NULL"/>
Thanks in advance for any help
I can't find corresponding documentation for later versions, but the SQL Server 2000 BOL addresses this issue:
The series of cascading referential actions triggered by a single DELETE or UPDATE must form a tree containing no circular references. No table can appear more than once in the list of all cascading referential actions that result from the DELETE or UPDATE. The tree of cascading referential actions must not have more than one path to any given table. Any branch of the tree is terminated when it encounters a table for which NO ACTION has been specified or is the default.
And later versions haven't changed this. You're falling foul of this:
The tree of cascading referential actions must not have more than one path to any given table
The only way I know of to accomplish this is to implement one of the cascades between B and A using an INSTEAD OF trigger, rather than using ON DELETE....
The relation between tables A and C shouldn't be impacted by any of this.
(2008 BOL)

Delete a table referenced by a foreign keys

What is the best way to delete a table referenced by a foreign keys?
Is the intended goal to orphan those records and never use the foreign key again? If so the method mentioned before about disabling the key is fine, otherwise you may want to instead delete the records referencing the table you want to delete first (or update the to point to a more appropriate record, or NULL if that makes sense in this case). I seem to be coming at this from a different direction than others, are you sure the foreign key is pointless, and if so why not just remove it? At some point someone wanted to constraint this behavior, before just disabling constraints I make sure I understand their purpose and have a good justification for bypassing those safeguards.
Remove the foreign key constraint and then delete the table once no-one is forced to recognize it. If the column in the second table (the one not being deleted) is not used elsewhere, then you should probably delete the whole column after removing the constraint.
You need to remove the constraint before you're allowed to delete the table referenced by it. SQL Server uses the following syntax:
ALTER TABLE <table_name> DROP FOREIGN KEY <foreignkey_name>
Keep in mind that the constraint exists on the table that references the one you want to delete so that's the table you should be altering.
Do NOT delete a table with foreign key constraints without considering the impact on the foreign key tables. Let me explain the impact of simply deleting the foreign key and then the table with an example.
Consider two tables - parts and orderdetails. There is a foreign key constraint that says a part must exist before it can be put into the orderdetails table. What is stored in the orderdetail table is the id for the part from the parts table, not the part name or description. Suppose you drop the foreign key and then drop the parts table. Now all the data in the orderdetail table is totally useless because you have no way of knowing what the part ordered was. This would include orders not yet shipped and orders that the customer might call and ask questions about. Further you now have no way to recreate that data except by restoring a backup (hope you have one).
Further suppose you want to drop the table and recreate it to make a change to the table. Then reload the information and put the foreign key back on. In this case you should probaly use alter table instead of drop and recreate but if you don't you may end up with id numbers that are not the same as they were originally and thus now the orders will reference the wrong ids. This can be done safely but you would have to do it very carefully and with a lot of thought as to the consequences.
by using On Delete Cascade

Resources