Delete row with foreign key constraints and no cascade deleting SQL - sql

Trying to figure out how to delete a row in a SQL table that has multiple foreign keys pointing to it, and more keys pointing to those, etc, etc. Cascading Deletes aren't turned on (I have no control to turn them on), and I'm trying to avoid performing a delete on EVERY single row that is affected by this one delete.
So if I have table XXX, with columns YYY and ZZZ, where YYY is the primary key and ZZZ is a column that has multiple foreign keys pointing to it, how would I go about deleting a row based on the primary key value?
Syntax would be:
DELETE FROM XXX
WHERE YYY = some_value
Is this even possible (without performing a ton of individual deletes)? And if so, how would I do it?

No.
Either you need the foreign key constraints to cascade the delete (something I'm not terribly fond of, it makes it too easy for some application/ developer to think they can delete and re-insert some data rather than updating it in place without inadvertently causing all the child rows to be deleted) or you have to delete the child rows before you delete the parent.
Normally, if you want to delete the data from the child tables, it is easier to just manually write the various DELETE statements. It would be possible to query the data dictionary (dba_constraints, dba_cons_columns, etc.) and dynamic SQL to walk all the constraints and generate the appropriate DELETE statements. In the vast majority of cases, it wouldn't make sense to do that unless you're trying to generate delete statements for a large number of tables.

How about turning off the foreign key constraint check?
SET FOREIGN_KEY_CHECKS = 0;
Then turning it on back when you delete the row?
SET FOREIGN_KEY_CHECKS = 1;

Related

MSSQL Multiple FKs in table: cannot have multiple cascade/set nulls?

I have a fairly simple design, as follows:
What I want to achieve in my grouping_individual_history is marked in red:
when a session is deleted, I want to cascade delete the grouping_history....
when a grouping is deleted, I just want the child field to be nullified
It seems that MSSQL will not allow me to have more than one FK that does something else than no action ... It'll complain with:
Introducing FOREIGN KEY constraint 'FK_grouping_individual_history_grouping' on table 'grouping_individual_history' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I've already read this post (https://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/), although it's not quite the same scenario it seems to me.
I've tried doing a INSTEAD OF DELETE trigger on my grouping table, but it wont accept it, because in turn, my grouping table has another FK (fkSessionID) that does a cascade delete... So, the fix would be to change it all, in all affected tables with FKs. The chain is long though, and we cannot consider it.
For one thing, can someone explain to me why SQL Server is giving me the issue for this very simple scenario in the first place? I just don't understand it.
Is there another workaround I could use (besides just removing the foreign key link from my grouping_individual_history table)?

Delete rows from binding source with foreign key

I have a DataSource with 2 tables one projectDiamter and other diameterSet. ProjectDiameter id contains primary key and diameterset has foreign key constraint with same key.
Now when I select row from projectdiameter table diameterset DataGridView get filtered accordingly. I have lots of rows in diameterset (lets say 15000 for selected project only) which I want to delete when delete button is pressed.
I wanted to know which is the fastest way to delete it from DataGridView and SQL table.
I tried following code but wanted to check if there is any better way of getting the same.
FKProjectDiameterBindingSource.MoveFirst()
For j As Int16 = 0 To FKProjectDiameterBindingSource.Count - 1
For i As Int16 = 0 To FKDiameterDiameterSetBindingSource.Count - 1
FKDiameterDiameterSetBindingSource.Clear()
FKDiameterDiameterSetBindingSource.MoveFirst()
FKDiameterDiameterSetBindingSource.RemoveCurrent()
Next
FKProjectDiameterBindingSource.MoveNext()
Next
Me.Validate()
DiameterTableAdapter.Update(RSM3DDB1.Diameter)
DiameterSetTableAdapter.Update(RSM3DDB1.DiameterSet)
Does the foreign key relationship exist formally in the database? That is, is there an actual relationship defined in SQL? If so, I'd suggest setting that relationship to CASCADE DELETE and then all you have to do is call DiameterSetTableAdapter.UPDATE after you update Diameter.
A good explanation on CASCADE DELETE is here
https://www.mssqltips.com/sqlservertip/2365/sql-server-foreign-key-update-and-delete-rules/ Note that the keyword is just "Cascade" but you apply it to DELETE, the article explains ("It is not necessary that the same rule be applied for both update and delete operations. There may be different rules for each of the update and delete operations on a single FK constraint.")
If the relationship isn't formal, that is it logically exists but isn't enforced, then I'd put a trigger on the Diameter table and in that trigger delete all the rows in DiameterSet where the DiameterID equals the deleted DiameterID. Note that in a trigger a row that is modified (as opposed to deleted) will be in both the INSERTED and DELETED temporary tables, so you have to take steps to make sure you're not removing rows that reference a modified Diameter.
If you take this second approach, you can still just run the 2 UPDATE procedures on the 2 table adapters in VB, it's just more coding in SQL than setting the CASCADE DELETE.
Both approaches are MUCH more efficient than doing it in VB, and it can add up if you have hundreds of rows affected.
Using either of these also has the advantage of being atomic, whereas your approach is not.

SQL Server - Updating a table that has foreign keys, using DELETE/INSERT instead of UPDATE

I have a main table with many associated tables linked to it using an "id" foreign key.
I need to update a row in this main table.
Instead of updating all the fields of the row, one by one, it would be easier for me to simply deleting the whole row and recreating it with the new values (by keeping the original primary key!).
Is there a way, inside a transaction, to delete such row that has foreign key constraints if the row is recreated, with the same primary key, before the transaction is actually commited?
I tried it, and it doesn't seem to work...
Is there something I can do to achieve that other than dropping the constraints before my DELETE operation? Some kind of lock?
No.
Without dropping/disabling the constraint, SQL Server will enforce the relationship and prevent you from the deleting the referenced row.
It is possible to disable the constraint, but you'll incur the overhead when enabling it that SQL Server must verify EVERY REFERENCE to that key before it will consider the relationships trusted again.
You are much better off taking the time to develop a separate update/upsert function than to incur that additional processing overhead every time you need to change a record.
You could alter the foreign key to use a CASCADE DELETE, but that has its own overhead and baggage.

How do I rename primary key values in Oracle?

Our application uses an Oracle 10g database where several primary keys are exposed to the end user. Productcodes and such. Unfortunately it's to late to do anything with this, as there are tons of reports and custom scripts out there that we do not have control over. We can't redefine the primary keys or mess up the database structure.
Now some customer want to change some of the primary key values. What they initially wanted to call P23A1 should now be called CAT23MOD1 (not a real example, but you get my meaning.)
Is there an easy way to do this? I would prefer a script of some sort, that could be parametrized to fit other tables and keys, but external tools would be acceptable if no other way exists.
The problem is presumably with the foreign keys that reference the PK. You must define the foreign keys as "deferrable initially immediate", as described in this Tom Kyte article: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html
That lets you ...
Defer the constraints
Modify the parent value
Modify the child values
Commit the change
Simple.
Oops. A little googling makes it appear that, inexplicably, Oracle does not implement ON UPDATE CASCADE, only ON DELETE CASCADE. To find workarounds google ORACLE ON UPDATE CASCADE. Here's a link on Creating A Cascade Update Set of Tables in Oracle.
Original answer:
If I understand correctly, you want to change the values of data in primary key columns, not the actual constraint names of the keys themselves.
If this is true it can most easily be accomplished redefining ALL the foreign keys that reference the affected primary key constraint as ON UPDATE CASCADE. This means that when you make a change to the primary key value, the engine will automatically update all related values in foreign key tables.
Be aware that if this results in a lot of changes it could be prohibitively expensive in a production system.
If you have to do this on a live system with no DDL changes to the tables involved, then I think your only option is to (for each value of the PK that needs to be changed):
Insert into the parent table a copy of the row with the PK value replaced
For each child table, update the FK value to the new PK value
Delete the parent table row with the old PK value
If you have a list of parent tables and the PK values to be renamed, it shouldn't be too hard to write a procedure that does this - the information in USER_CONSTRAINTS can be used to get the FK-related tables for a given parent table.

When to use "ON UPDATE CASCADE"

I use ON DELETE CASCADE regularly but I never use ON UPDATE CASCADE as I am not so sure in what situation it will be useful.
For the sake of discussion let see some code.
CREATE TABLE parent (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
CREATE TABLE child (
id INT NOT NULL AUTO_INCREMENT, parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id)
REFERENCES parent(id)
ON DELETE CASCADE
);
For ON DELETE CASCADE, if a parent with an id is deleted, a record in child with parent_id = parent.id will be automatically deleted. This should be no problem.
This means that ON UPDATE CASCADE will do the same thing when id of the parent is updated?
If (1) is true, it means that there is no need to use ON UPDATE CASCADE if parent.id is not updatable (or will never be updated) like when it is AUTO_INCREMENT or always set to be TIMESTAMP. Is that right?
If (2) is not true, in what other kind of situation should we use ON UPDATE CASCADE?
What if I (for some reason) update the child.parent_id to be something not existing, will it then be automatically deleted?
Well, I know, some of the question above can be test programmatically to understand but I want also know if any of this is database vendor dependent or not.
Please shed some light.
It's true that if your primary key is just an identity value auto incremented, you would have no real use for ON UPDATE CASCADE.
However, let's say that your primary key is a 10 digit UPC bar code and because of expansion, you need to change it to a 13-digit UPC bar code. In that case, ON UPDATE CASCADE would allow you to change the primary key value and any tables that have foreign key references to the value will be changed accordingly.
In reference to #4, if you change the child ID to something that doesn't exist in the parent table (and you have referential integrity), you should get a foreign key error.
Yes, it means that for example if you do UPDATE parent SET id = 20 WHERE id = 10 all children parent_id's of 10 will also be updated to 20
If you don't update the field the foreign key refers to, this setting is not needed
Can't think of any other use.
You can't do that as the foreign key constraint would fail.
I think you've pretty much nailed the points!
If you follow database design best practices and your primary key is never updatable (which I think should always be the case anyway), then you never really need the ON UPDATE CASCADE clause.
Zed made a good point, that if you use a natural key (e.g. a regular field from your database table) as your primary key, then there might be certain situations where you need to update your primary keys. Another recent example would be the ISBN (International Standard Book Numbers) which changed from 10 to 13 digits+characters not too long ago.
This is not the case if you choose to use surrogate (e.g. artifically system-generated) keys as your primary key (which would be my preferred choice in all but the most rare occasions).
So in the end: if your primary key never changes, then you never need the ON UPDATE CASCADE clause.
Marc
A few days ago I've had an issue with triggers, and I've figured out that ON UPDATE CASCADE can be useful. Take a look at this example (PostgreSQL):
CREATE TABLE club
(
key SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
CREATE TABLE band
(
key SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
CREATE TABLE concert
(
key SERIAL PRIMARY KEY,
club_name TEXT REFERENCES club(name) ON UPDATE CASCADE,
band_name TEXT REFERENCES band(name) ON UPDATE CASCADE,
concert_date DATE
);
In my issue, I had to define some additional operations (trigger) for updating the concert's table. Those operations had to modify club_name and band_name. I was unable to do it, because of reference. I couldn't modify concert and then deal with club and band tables. I couldn't also do it the other way. ON UPDATE CASCADE was the key to solve the problem.
The ON UPDATE and ON DELETE specify which action will execute when a row in the parent table is updated and deleted. The following are permitted actions : NO ACTION, CASCADE, SET NULL, and SET DEFAULT.
Delete actions of rows in the parent table
If you delete one or more rows in the parent table, you can set one of the following actions:
ON DELETE NO ACTION: SQL Server raises an error and rolls back the delete action on the row in the parent table.
ON DELETE CASCADE: SQL Server deletes the rows in the child table that is corresponding to the row deleted from the parent table.
ON DELETE SET NULL: SQL Server sets the rows in the child table to NULL if the corresponding rows in the parent table are deleted. To execute this action, the foreign key columns must be nullable.
ON DELETE SET DEFAULT: SQL Server sets the rows in the child table to their default values if the corresponding rows in the parent table are deleted. To execute this action, the foreign key columns must have default definitions. Note that a nullable column has a default value of NULL if no default value specified.
By default, SQL Server appliesON DELETE NO ACTION if you don’t explicitly specify any action.
Update action of rows in the parent table
If you update one or more rows in the parent table, you can set one of the following actions:
ON UPDATE NO ACTION: SQL Server raises an error and rolls back the update action on the row in the parent table.
ON UPDATE CASCADE: SQL Server updates the corresponding rows in the child table when the rows in the parent table are updated.
ON UPDATE SET NULL: SQL Server sets the rows in the child table to NULL when the corresponding row in the parent table is updated. Note that the foreign key columns must be nullable for this action to execute.
ON UPDATE SET DEFAULT: SQL Server sets the default values for the rows in the child table that have the corresponding rows in the parent table updated.
FOREIGN KEY (foreign_key_columns)
REFERENCES parent_table(parent_key_columns)
ON UPDATE <action>
ON DELETE <action>;
See the reference tutorial.
It's an excellent question, I had the same question yesterday. I thought about this problem, specifically SEARCHED if existed something like "ON UPDATE CASCADE" and fortunately the designers of SQL had also thought about that. I agree with Ted.strauss, and I also commented Noran's case.
When did I use it? Like Ted pointed out, when you are treating several databases at one time, and the modification in one of them, in one table, has any kind of reproduction in what Ted calls "satellite database", can't be kept with the very original ID, and for any reason you have to create a new one, in case you can't update the data on the old one (for example due to permissions, or in case you are searching for fastness in a case that is so ephemeral that doesn't deserve the absolute and utter respect for the total rules of normalization, simply because will be a very short-lived utility)
So, I agree in two points:
(A.) Yes, in many times a better design can avoid it; BUT
(B.) In cases of migrations, replicating databases, or solving emergencies, it's a GREAT TOOL that fortunately was there when I went to search if it existed.
My comment is mainly in reference to point #3: under what circumstances is ON UPDATE CASCADE applicable if we're assuming that the parent key is not updateable? Here is one case.
I am dealing with a replication scenario in which multiple satellite databases need to be merged with a master. Each satellite is generating data on the same tables, so merging of the tables to the master leads to violations of the uniqueness constraint. I'm trying to use ON UPDATE CASCADE as part of a solution in which I re-increment the keys during each merge. ON UPDATE CASCADE should simplify this process by automating part of the process.
To add to other great answers here it is important to use ON UPDATE CASCADE (or on DELETE CASCADE...) cautiously. Operations on tables with this specification require exclusive lock on underlaying relations.
If you have multiple CASCADE definitions in one table (as in other answer), and especially multiple tables using same definitions, and multiple users updating, this can create a deadlock when one process acquires exclusive lock on first underlaying table, other exclusive lock on second, and they block out each other by none of them being able to get both (all) exclusive locks to perform operation.