How to cascade update 2 tables whose foreign key is not set to cascade? - sql

I got 2 tables. They have a foreign key relation. But the foreign key is not set to update cascade. Now I want to update the table's primary key. SQL Server always prevent me from doing that because of the FK. How could I do it in SQL command? I don't have the right to modify the FK.
Thanks.

Why would you want to do this? It strikes me as potentially hazardous.
Your SQL would need to update the related table first NULLing the links back to the PK table and storing the IDs of the records effected. Then you can update the PK in the PK table. Then go back and update the FKs in the related tables.

Create new rows based on the existing row's attribute values but using the new key value. Do the same for all referencing tables. Then, using the new old key value, delete rows from the referencing tables then the referenced table. Something like:
INSERT INTO Table1 (key_col, attrib_col1)
SELECT 'new_key_value', attrib_col1
FROM Table1
WHERE key_col = 'old_key_value';
INSERT INTO Table2 (key_col, attrib_col2)
SELECT 'new_key_value', attrib_col2
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table1
WHERE key_col = 'old_key_value';

You should only need to INSERT new rows for the parent table and UPDATE the child rows after the fact...
INSERT INTO ParentTable (PKColumn, attribute1, attribute2)
SELECT 'NewPKValue', attribute1, attribute2
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
UPDATE ChildTable
SET FKColumn = 'NewPKValue'
WHERE FKColumn = 'OldPKValue'
;
DELETE
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
Now for the gotchas:
1.) The above code wont work if there are any unique indexes defined on non-PK columns in the parent table and you need to use the current records' non-PK data values without modification.
2.) Since you're asking this question, I'm assuming your Parent table is not using an IDENTITY column as the PK.
3.) The code is certainly less than efficient. If your db has to do this infrequently on a few rows, you should be fine. If you need to do this 80 times per second, then I would strongly suggest you talk to the programmer/DBA or vendor, if they're at all available, about updating the FK definition to include ON UPDATE CASCADE.
4.) Make sure that there aren't any triggers on either table that would lead to unintended side effects when you create the new parent records or update the child records.

Related

If I delete a record from a table it is deleted at many other places

I am working on a database built by the previous team. I have to delete certain records from a table (Example shown below).
DELETE FROM table WHERE id = 5541
While doing this process, some records from the other tables with the same id is getting deleted. Could someone help how to overcome this problem?
In SQL Server, there is statement called ON DELETE CASCADE which deletes the record from the child table if the record is deleted from the parent table. This can be set using the ALTER STATEMENT as shown below. To remove the cascading, try altering the child table back to default. The default is NO CASCADING.
ALTER TABLE ChildTable
ADD CONSTRAINT FKey
FOREIGN KEY (col1, col2, ... coln)
REFERENCES ParentTable (Pcol1, Pcol2, ... Pcoln)
ON DELETE CASCADE
There is UPDATE CASCADE as well if the data in the child table should be updated when the parent table is updated.
You database most certainly contains foreign key constraints with cascading deletes see docs.
You may be able to remove these foreign keys, but of course, then deleting some rows will leave you with inconsistent data.
Another possibilty is to just remove the cascading deletes. But then of course, you won't be able to delete any rows which are referenced by records from other tables, as SQL server will ensure the consistency of your data.

SQL Delete Rows with Constraints

I am doing some Exercises and for that I want to delete a Row which is referenced with a Primary/Foreign Key.
Is it possible to delete those Rows without deleting the ones which it has a Relationship with? (like On Delete Cascade)
I also know that I can Disable the constraints, however I want to try deleting without disabling the Constraints.
A row can always be deleted unless its primary key is referenced as a foreign key in a row from another table. In this case, attempting to delete rows will result in an error when the primary/foreign key relationship against data is encountered. The ON DELETE CASCADE option can be used to work around this and delete records from the child table when rows are removed from the parent table.
If you only want to delete the rows that do not have a relationship, then you can exclude the rows that do have a relationship from the delete operation using NOT EXISTS:
DELETE FROM table1 a
WHERE NOT EXISTS (
SELECT FROM table2 b
WHERE b.table1Id = a.id
)

primary keys are shifted

Background Information
For some reason, while inserting a huge data into multiple tables from xml, my primary keys are shifted by an offset.. (Maybe because of multiple failed attempts :P)
I have two tables.. tableA and tableB. They are in one-to-may relationship.
tableA is the parent table and has the Primary key column ...say DataIndex.
now DataIndex has come out like this..
685, 686, 687... and so on.
and corresponding values present in the child table i.e TableB are the same.
Problem
How do I shift the values up so that DataIndex start from 1, 2, 3..and so on; In both tables ?
I'm assuming that the primary key is actually an identity column that auto-increments itself upon insertion. What you will need to do is 'reseed' the identity column. You can do this by renaming the table, create a duplicate table with the original name, then inserting the data from the old table to the new one (the primary key field will be reset and will auto-increment from 1 again). When doing the insertion, make sure to include the old primary key value as an additional column for reference in the other tables.
To match up the related table, all you will need to do is do an UPDATE and join to your new table on the old primary key value:
UPDATE tableB SET
PRIMARYKEYCOLUMN = tableA.PRIMARYKEYCOLUMN
FROM tableA
WHERE
tableA.OLDPRIMARYKEYCOLUMN = tableB.PRIMARYKEYCOLUMN
I would do the following:
construct a list of Foreign Keys pointing to your PK;
ALTER all FKs, adding ON UPDATE CASCADE clause. This step might not work for some databases, you might need to DROP and CREATE constraint again;
Find the smallest current PK values, like: SELECT min(id) FROM tableA;
Refresh PK values: UPDATE tableA SET id = id - min_id + 1;
Remove ON UPDATE CASCADE clause.
Note, that depending on the tableA size and the database engine you're using, it might be faster and easier to completely rebuild the table to avoid bloat of data files.
Remove PK
Walk through records eith DB CURSOR or write a script on any language sequentally reading and changing id's
Restore PK
Setup correct identity seed for the PK
Changing seed might involve identity column drop and recreate.

Copying a 1-to-many relationship between two tables to another two tables (SQL 2005)

I have two tables with a parent-child relationship. I would like to copy some of their records to two other tables, also with a parent-child relationship, but with a slightly different table structure.
There is a foreign key involved with both sets of tables, an integer column. All tables have the identity increment on for their primary key columns.
If I do a SELECT INTO from the source parent table to the destination parent table, the primary key values for the destination records will be different from the source records, and the parent-child relationship will be lost.
Does anyone know how I can preserve this relationship during the copy, given that I'll have new primary key values in the new parent table? I'd prefer not to set the identity increment off for the new tables during this copy procedure, because there's no guarantee the primary key values in the source table won't already be in the destination.
Hope my description makes sense, and thanks for your opinions. Let me know if I can clarify further.
When I have done this, I did so by preserving the old ID after the insert into the new table.
Some people add a temporary column with the old ID, but I know of a better way.
You can use the OUTPUT clause to insert the inserted records into a temp table (with the new and the old IDs as a mapping table. Then join to that when inserting the child records.
Something like this (crude)
DECLARE #MyTableVar TABLE (
NewID INT,
OldID INT
)
INSERT NewParentTable
OUTPUT
INSERTED.ID,
old.ID
INTO #MyTableVar
SELECT *
FROM OldParentTable old
INSERT NewChildTable
SELECT
old.ID,
t.NewID,
old.name
FROM OldChildTable old
INNER JOIN #MyTableVar t ON t.OldID = old.ParentID
You'd have to maintain the original ID in a separate column in the table with the corresponding parentID.
So in your destination table, you'd need to add an parentID_orig column (not auto-number) to retain the link.

Sql Server 2008 check constraint

I have two tables, Table1 and Table2 with same primary key (FKTestID).
If I want to delete one row in Table1 and same FKTestID are in Table2 it will not work. You can only delete a row from Table1 if Table1.FKTestID not equals any FKTestID in Table2.
Please help me with this constraint?
You need to set the constraint to cascade on delete.
You can do this through SQL management studio by modifying the constraint.
Or you can do this via SQL when you created the constraint by including ON DELETE CASCADE at the end
You could also do it with the ALTER TABLE command.
Here is a code sample implementing what Simon suggested above.
CREATE TABLE dbo.Table1 (
FKTestID int NOT NULL PRIMARY KEY
)
GO
CREATE TABLE dbo.Table2 (
FKTestID int NOT NULL PRIMARY KEY
)
GO
ALTER TABLE dbo.Table2
ADD CONSTRAINT FK_Table2_FKTestID
FOREIGN KEY (FKTestID)
REFERENCES dbo.Table1 (FKTestID)
ON DELETE CASCADE
GO
INSERT INTO dbo.Table1 VALUES (1)
INSERT INTO dbo.Table2 VALUES (1)
INSERT INTO dbo.Table1 VALUES (2)
INSERT INTO dbo.Table2 VALUES (2)
DELETE FROM dbo.Table1 WHERE FKTestID = 1
SELECT 'Table1' AS [Table], * FROM dbo.Table1
SELECT 'Table2' AS [Table], * FROM dbo.Table2
=============================================
Table FKTestID
------ -----------
Table1 2
Table FKTestID
------ -----------
Table2 2
Note that I agree with Mitch Wheat's comment about CASCADE DELETE being dangerous. This feature is interesting, but I have never, ever used it in a production system.
If you are asking how to get rid of the constraint so that you can delete, DO NOT consider doing that. The constraint is there for a reason. If you don't know the reason, don't delete it.
Others have suggested adding a cascade delete. I suggest that this is poor idea as you can cause performance problems on the database. It is better to write a script that deletes the records in the correct order. In this case you need to delete the matching records from tables2 first, then run the delete on table 1.
You also need to evaluate the data in table2 before deciding to delete from it either by script or by cascade delte. If you should not be deleting those records from table2, then you should not be delting the records from table1 (by removing the constraint) as this will casue the table 2 records to be orphaned and you will lose the integrity of the data (which should be your first concern in any database actions (security and performance being a very close second and third)).
Let me give you a scenario where the data in table 2 would indicate you should not delete the record. Suppose you have a customer table and and order table. You want to delete customer A, but he has an order in the past. If you delete both records, you will mess up all the accounting information on orders. If you delete the customer but not the order (by eliminating the constraint), then you have an order that you can no longer tell who it was sent to. The correct thing to do in a case like this is to have an ISactive file in the customers table and mark him as an inactive customer. You would of course need to redesign the code that searches for customer information to make sure it includes the flag to only select active customers which is why this sort of thing should be thought out at the beginning of development not later (one reason why it is worth your while to hire database specialists for the design phase as many application developers don't consider maintaining the data over time as part of their design process).