In SQL Server 2014, I'm trying to add CASCADE DELETING on 3 FK. If I add a Cascade Delete in one relationship, it works fine. If I add more Cascade Deletes, it doesn't work (Cycle detected error message).
In the above diagram, you can see the Users table, and a Tasks table ("Tareas" in spanish). So, what I need to acomplish is when the user is deleted, I need to set the marked field in Tasks to NULL.
This is something common in a database, so I thought there is a way to handle this.
In my case, most of my tables have a pair of fields holding the UserId of the user that Created or Modified the record. So, I need to solve this pattern to apply it several places.
CASCADE DELETE means that in your situation if you delete a User, then SQL Server will delete any attached Tasks too. That is, entire rows will be deleted. Apart from issues such as unexpected losses of data, loss of referential integrity or the potential of infinitely recursive deletions, this behaviour is not what you want anyway. You have stated you only want to mark the associate User columns in your Tasks table to null.
As a suggestion, have you considered implementing a TRIGGER? Something like this (haven't tested this, treat it as pseudo-code!):
CREATE TRIGGER DeleteTareasTrigger
ON Users
BEFORE DELETE AS
BEGIN
UPDATE t
SET t.CreadaPor = NULL
FROM DELETED as d
INNER JOIN Tareas t ON d.UserID = t.CreadaPor
UPDATE t
SET t.ModifcadaPor = NULL
FROM DELETED as d
INNER JOIN Tareas t ON d.UserID = t.ModifcadaPor
END
GO
Or as another approach, add a bit field on the User table to indicate whether the person is active/deleted.
Related
I have seen something like this asked a number of times but not quite in this configuration. I have a table that has a one to many relation.
Let’s say I have a computer table and a parts table. The user enters a generic info in the computer table then selects parts that are stored in the parts table with a relationship to the computer table of computerId. So the original write is a simple insert. Now let’s say the user select the computer again and changes the part on the pc, adds some new, removes some, and updates a few. Then the user hits save to save the changes. I run a simple update on the computer table but now the issue with the parts table.
Would it be better to delete all the records from the parts table for the computer Id and then do a clean insert of all the parts selected.
Or Run some method that would look at the existing parts in the table and where the part has been updated update the record, where the part no longer exists do a delete, and then insert the remaining parts?
Clearly the simple solution is to delete all and then insert all.
The down side of this SQL traffic, locks, and table fragmentation.
If it is small table and only few concurrent users then fine.
In a high volume environment I do the following
There is no update - that is just an ignore
- delete items gone
- ignore any items not changed
- insert new items
And you can do that in one pass two/three statements.
Or you could define a stored procedure.
Do the delete before the insert to clear space first.
You can get real fancy and use an update for delete / insert but that just gets more complex than it is worth in my mind. You would still have an insert or a delete if the item count is not the same.
delete comp_part
where compID = #compID and partID not in (....);
Insert is a little more tricky:
You can to it with a series of inserts and if you have a PK just let the insert fail
The other way is to create a #table and use it for both the delete and insert
This is only worth the hassle if you have a REALLY busy table.
It all depends upon the business model, if you would want to track the transaction than its not a good option to delete it. If you have all your old transactions with your customers than it would be beneficial for tracking purposes., Your CustomerID would be Primarykey and you can have another Unique key as PartOrderID which will be a unique value for each insert.
Hope this helps
Really you should have three tables. Product, Part, and ProductPart; the ProductPart table would store the association of "this product has these parts". As far as updating, the simplest thing would be to delete all ProductParts for a given Product and re-insert the records you want.
When delete a master record a cascade trigger will get fired and delete the records in the child tables.
And I cannot delete a record manually from the table as it got a foreign key relation with child table.
But how can I prevent the child record from being deleted manually from the table. Currently I am able to delete the child record manually and on page loads as the child record is missing and page load fails.
I'm a bit unclear as to why you'd want to have something like this done. You simply don't allow for the deletion of child records on the UI aside from your cascade delete - just don't give the user the option.
If you're worried about some random DBA going in to your database and writing:
delete from childTable where parentId = 5 -- or whatever
then i think you have more things to worry about... such as why people with production database write access are even thinking about manually writing and executing statements such as this on your prod database.
If you still really needed to do something like this. You could potentially write a before delete trigger on all of your child tables to ensure that the parentId doesn't exist in the parent table prior to delete. This would likely cause your cascade delete to fail (i would guess) so you would need to update your cascade delete functionality to disable the trigger before deletion, re-enabling the trigger after. But this would not prevent your "random dba" from just doing something similar by disabling the trigger, deleting a record, and re-enabling.
If you provide a little more information around the specific scenarios of why a child record would "manually" be deleted, might be able to offer more.
I'm not sure who or what would manually delete records from this table, but I would lean towards using DENY permissions with this requirement, and ensure all roles on that database have no delete permissions on that table.
Sample script:
USE [YOUR_DB]
GO
DENY DELETE ON [dbo].[YOUR_TABLE] TO [DOMAIN\user]
GO
Replace [dbo] with relevant schema name and replace [DOMAIN\user] with the relevant user.
I would like to ask if how do you deal with foreign keys?
Do you cascade delete, or just mark it as deleted but its there?
Here is my sample:
Users(table) 1 ------ * Transactions(Table that has userId)
1
|
|
*
Items(table) 1 ------ * TransactionItems(Table That has ItemId)
(this scenario is for sales transactions )
Do if I delete a user that is being used in a transaction all transactions that have that
user id will be deleted and that is not ok of course..
The simple answer might be to not allow the user of the application to delete a user record that is being referenced. So this means that you must not allow cascade delete right?
So, if I use cascade delete on the relationship between Transactions and TransactionItems
would that be okay? Since its not being referenced.
I would add a bit or bool field to the Users and Items tables called "Active." Set it TRUE for all records. When a User or Item needs to be "deleted," set the bit to FALSE. Change all your queries in your application to filter the Users and Items tables WHERE Active = 'TRUE' so that the application only sees "non-deleted" Users or Items.
This will preserve the userId for things such as historical reports (you can join the Transactions table to the Users table and still match on all usersId's in the Transactions table), but allows for a logical "deletion" from the perspective of your application.
The same applies for itemId in the TransactionItems table; your join to the Items table will still match on all itemId's.
For the relationship of Transactions to TransactionItems, since no two Transactions records can relate to the same TransactionItems record, you can set the relationship to Cascade Delete, so that when a record from the Transacations table is deleted, all related TransactionsItems records are deleted, as well. (Any Items related to those deleted TransactionItems would still remain.)
THE TABLES:
Shop
Product
Category
THE RELATIONSHIPS:
(Shop) 1 <---> n (Categories)
(Shop) 1 <---> n (Products)
(Categories) n <---> n (Products)
THE CASCADE DELETES:
Shop ---> Categories ... I defined this using fluent API
Shop ---> Products ... I defined this using fluent API
Categories <---> Products ... EF 4.1 automatically defines cascade for "Category_Product" join table
THE PROBLEM:
Above results in a "multiple" cascade deletion path exception.
POTENTIAL FIXES:
Remove the ManyToManyConvention, but that means I must manually perform deletes for every join table in the system, which is impractical.
I can remove the cascade delete from Shop->Category or Shop->Products. But then I'll probably have lots of orphaned records.
How are you folks dealing with this problem?
THANKS
This is not a problem of entity framework but the problem of SQL server. I don't think that exception actually means circular cascade delete. It more probably means multiple cascade delete paths because join table records can be deleted from both categories and products side because of cascading from shop. SQL server doesn't allow this because it requires some more complex (and slow) algorithms to correctly compute which records and when have to be deleted when cascading.
Simply you must break this and it will really mean that you will have to manually delete all related records (either categories or products) before you delete shop. This will require stored procedure (or direct SQL DELETE command) otherwise you will have to load all of them first and delete them one by one.
Edit:
As you pointed in the comment this can be also solved by adding BEFORE DELETE trigger which will delete related records if exists as replacement of one cascade path.
hi all i am getting a problem while i attenpting to delete a row from parent table, actuall i have created 2 table custmer and account.i make cutomer id(primary key) in customer and customer id (forigen key ) in account.after created it i have filled data inside both table.
at this point while i am trying to delete a row from first table (customer ) then it give failure message is that it can't be deleted bcs it is refrenced as forigen key some thing like that............but while we delete row from account table then it's delete sucess fully.
.......i want to function like that if i delete a row from parent table(customer) then its in child table that row which has same customer id (account table) is delete automatically............
watch out on the cascade deletes! a user will accidentally click on the application's little trash can icon and delete the customer, and then all the cascades will remove every trace of that customer, orders, invoices, payments, history, etc from your database. After the user call you to tell you about their little mistake, you'll have to restore a backup and try to pull the info back into the database.
I would look into "soft deletes" where you only change the customer's status from "active" to "inactive". the rows is not deleted, preserving all foreign key data. This allows reports to run on the data, because it still exists, as well as easy an "undo".
Soft deletes are not the end all only way to go, it is a business decision on how to handle this, purge the data or mark it inactive. That is only something you can decide, because I don't know your application or business logic. I just thought that I would offer it as an alternative.
You need to set up the foreign key with on delete cascade to achieve this.
For SQL Server 2008 see the article Cascading Referential Integrity Constraints
Edit Just to add a somewhat redundant health warning you should be aware that adding on delete cascade will mean that when you delete the row from the parent table associated rows from the child table will be deleted. However as this is exactly the behaviour you state that you want I can't see that would be an issue.