Need to change my SQL structure for deletion - sql-server-2005

Ok. I've tried the trigger, but it didn't work.
I have Cascades from A to Linker and from B to Linker, Cascade from Users to A, No Action from Users to B.
My trigger is on Users and is as follows:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER TRIGGER [trig_delUser] ON [dbo].[aspnet_Users]
FOR DELETE AS
SET NOCOUNT ON;
DELETE FROM B WHERE B.UserId = Deleted.UserId
I get the exception: The DELETE statement conflicted with the REFERENCE constraint "FK_B_aspnet_Users"
I am working with a modified aspnetdb
SQL database:
[Partial DB diagram][2]
I have cascade deletion on the
B_Linker relationship and the A_Linker
relationship and there doesn't seem to
be any danger of cycles occuring.
When I delete a user, I would like all
A entries and B entries to be deleted
along with any associated linker
entries; unfortunately, SQL mgmt
studio will only let me put a cascade
delete rule on EITHER aspnet_Users_A
or aspnet_Users_B, not both.
What do I need to do?
Many Thanks.
[2]:
http://i48.tinypic.com/2nsnc3k.png

This is one of the unfortunate and frankly rather annoying limitations of SQL Server.
It's not the fact that there could be cycles that's the problem, it's simply as the error says - you have multiple cascade paths to the Linker table. The first is aspnet_Users -> A -> Linker and the second is aspnet_Users -> B -> Linker.
You only really have a couple of choices:
Choose one path to implement the CASCADE on and set the other to NO ACTION. Then write a Stored Procedure that deletes the non-cascaded child entities before deleting the parent entities in order to prevent a foreign key error. Or, don't CASCADE either and have your SP do the cascading for both.
Don't add a foreign key at all on the second relationship; instead, use a FOR DELETE trigger on the parent to delete the child entities. I very much dislike using triggers for RI, but this isn't much worse than the first option. In some ways it's better, because database clients don't have to worry about your specific implementation of the FK relationships.
Neither are ideal, but there is no perfect solution other than to change your design. If it is possible for you to change your schema such that you don't have multiple cascade paths, that would be the best thing to do - but I recognize that there are (many) real-world situations where this is not possible. Not knowing the specifics of your schema, I can't say for sure whether or not there's a more optimal design.

I would not implement this through on delete cascade. Or through a trigger. What would happen if someone needed to delete 40,0000 records all at once. You would lock up the parent and all the child tables possibly for minutes possibly hours while it runs through and does it's thing.
So really what you should do is write the deletes to the child tables first then the delete to the parent table and put them in a transaction. This will work well enough when you are delting one record at a time. Large deletes should be written separately (don't loop throuhg the existing proc) and are another subject, but at least now you can do them without killing your server.

You just have to put the ON DELETE CASCADE rules on the foreign keys.

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)?

Ordered DELETE of records in self-referencing table

I need to delete a subset of records from a self referencing table. The subset will always be self contained (that is, records will only have references to other records in the subset being deleted, not to any records that will still exist when the statement is complete).
My understanding is that this might cause an error if one of the records is deleted before the record referencing it is deleted.
First question: does postgres do this operation one-record-at-a-time, or as a whole transaction? Maybe I don't have to worry about this problem?
Second question: is the order of deletion of records consistent or predictable?
I am obviously able to write specific SQL to delete these records without any errors, but my ultimate goal is to write a regression test to show the next person after me why I wrote it that way. I want to set up the test data in such a way that a simplistic delete statement will consistently fail because of the records referencing the same table. That way if someone else messes with the SQL later, they'll get notified by the test suite that I wrote it that way for a reason.
Anyone have any insight?
EDIT: just to clarify, I'm not trying to work out how to delete the records safely (that's simple enough). I'm trying to figure out what set of circumstances will cause such a DELETE statement to consistently fail.
EDIT 2: Abbreviated answer for future readers: this is not a problem. By default, postgres checks the constraints at the end of each statement (not per-record, not per-transaction). Confirmed in the docs here: http://www.postgresql.org/docs/current/static/sql-set-constraints.html And by the SQLFiddle here: http://sqlfiddle.com/#!15/11b8d/1
In standard SQL, and I believe PostgreSQL follows this, each statement should be processed "as if" all changes occur at the same time, in parallel.
So the following code works:
CREATE TABLE T (ID1 int not null primary key,ID2 int not null references T(ID1));
INSERT INTO T(ID1,ID2) VALUES (1,2),(2,1),(3,3);
DELETE FROM T WHERE ID2 in (1,2);
Where we've got circular references involved in both the INSERT and the DELETE, and yet it works just fine.
fiddle
A single DELETE with a WHERE clause matching a set of records will delete those records in an implementation-defined order. This order may change based on query planner decisions, statistics, etc. No ordering guarantees are made. Just like SELECT without ORDER BY. The DELETE executes in its own transaction if not wrapped in an explicit transaction, so it'll succeed or fail as a unit.
To force order of deletion in PostgreSQL you must do one DELETE per record. You can wrap them in an explicit transaction to reduce the overhead of doing this and to make sure they all happen or none happen.
PostgreSQL can check foreign keys at three different points:
The default, NOT DEFERRABLE: checks for each row as the row is inserted/updated/deleted
DEFERRABLE INITIALLY IMMEDIATE: Same, but affected by SET CONSTRAINTS DEFERRED to instead check at end of transaction / SET CONSTRAINTS IMMEDIATE
DEFERRABLE INITIALLY DEFERRED: checks all rows at the end of the transaction
In your case, I'd define your FOREIGN KEY constraint as DEFERRABLE INITIALLY IMMEDIATE, and do a SET CONSTRAINTS DEFERRED before deleting.
(Actually if I vaguely recall correctly, despite the name IMMEDIATE, DEFERRABLE INITIALLY IMMEDIATE actually runs the check at the end of the statement instead of the default of after each row change. So if you delete the whole set in a single DELETE the checks will then succeed. I'll need to double check).
(The mildly insane meaning of DEFERRABLE is IIRC defined by the SQL standard, along with gems like a TIMESTAMP WITH TIME ZONE that doesn't have a time zone).
If you issue a single DELETE that affects multiple records (like delete from x where id>100), that will be handled as a single transaction and either all will succeed or fail. If multiple DELETEs, you have to put them in a transaction yourself.
There will be problems. If you have a constraint with DELETE CASCADE, you might delete more than you want with a single DELETE. If you don't, the integrity check might stop you from deleting. Constraints other than NO ACTION are not deferrable, so you'd have to disable the constraint before delete and enable it afterwards (basically drop/create, which might be slow).
If you have multiple DELETEs, then the order is as the DELETE statements are sent. If a single DELETE, the database will delete in the order it happens to find them (index, oids, something else...).
So I would also suggest thinking about the logic and maybe handling the deletes differently. Can you elaborate more on the actual logic? A tree in database?
1) It will do as transaction if enclosed within "BEGIN/COMMIT". Otherwise in general no.
For more see http://www.postgresql.org/docs/current/static/tutorial-transactions.html
The answer in general to your question depends on how is self-referencing implemented.
If it is within application logic, it is solely your responsibility to check the things yourself.
Otherwise, it is in general possible to restrict or cascade deletes for rows with foreign keys and DELETE CASCADE . However, as far as PG docs go, I understand we are talking about referencing columns in other tables, not sure if same-table foreign keys are supported:
http://www.postgresql.org/docs/current/static/ddl-constraints.html#DDL-CONSTRAINTS-FK
2) In general, the order of deletion will be the order in which you issue delete statements. If you want them all to be "uninterruptible" with no other statements modifying table in between, you enclose them in a transaction.
As a warning, I may be wrong, but what you seem to be trying to do, must not be done. You should not have to rely on some esoteric "order of deletion" or some other undocumented and/or implicit features of database. The underlying logic does not seem sound, there should be another way.

INSTEAD OF Triggers and CASCADE paths

Let's say I have 3 tables in a hierarchy:
TableA -> TableB -> TableC
TableC has a foreign key relationship with TableB, and TableB has a foreign key relationship with TableA.
If i delete a record in TableA, it should cascade delete down through the hierarchy. Using ON DELETE CASCADE would work fine.
However let's say I need to put an INSTEAD OF trigger on TableC. My understanding is that an INSTEAD OF trigger can not be put on a table that has a delete cascade going to it. Taken from MSDN:
For INSTEAD OF triggers, the DELETE option is not allowed on tables that have a referential relationship specifying a cascade action ON DELETE.
If I have to take the cascade delete off TableB->TableC, I would need to use an INSTEAD OF trigger to enforce Referential Integrity, and then I have the same problem with TableB->TableA. This is a simple example, but imagine the cascade path being much larger. It seems like it could easily snowball throughout a long cascade path.
So what are the best practices for dealing with this scenario?
Assuming you must use INSTEAD OF triggers, and AFTER triggers are not an option, the best approach is to a) tightly control the schema so that you can b) script the INSTEAD OF triggers out in a regular fashion to implement the CASCADE DELETE and whatever other operations you need.
Create the FK constraints as before, but w/out any cascade behavior. In the FK name, use some convention to indicate what kind of cascade behavior and custom behavior should occur, eg:
FK_UC_DC_Table1_Table2 -- update cascade, delete cascade
FK_UC_DN_Table1_Table3 -- update cascade, delete set null
Use whatever makes sense, but do create the FKs, they are useful metadata for code generation, and you can use the FK names to record directives for the code-generator.
I'd then take it a step further and isolate these tables in their own schema. They won't behave the same way as other tables, and they will be more buggy at first as you test and fine-tune the code generation. Best to keep all this quarantined, and easily identifiable by a common container.
A dedicated schema will also inform anyone modifying the data that different rules and behavior apply.
The standard best-practice is to define INSTEAD OF triggers on views, not on tables.
If you have to use a trigger on a FK update/delete you are best to use AFTER, since it will always execute.
If you want to cancel the cascading actions but retain the FKs, just set the FK action to NO ACTION.

Delete data from a complexed DB

Let's say I have a Customers table with columns ID and CompanyId.
I need to delete some companies from the DB.
The problem is, the table has a lot of child tables and those tables also has a lot of child tables and so on...
Just to clarify, all the relationships are with constraints.
How can I accomplish that ?
Thanks.
EDIT: Notice that what i'm trying to do is a one time operation.
Whether i will change the constraints or add triggers or anything like that, I'm planning on removing it in the end.
The inbuilt solution to this problem is to set up your FK constraints with ON DELETE CASCADE.
However many people (myself included) are somewhat uneasy about doing this as a mistaken delete will silently propagate through the database.
Here are three ways:
Use a stored procedure to delete child first then up to the parent row in a transation.
I personally wouldn't make it dynamic and would have a specific "DeleteCompany" proc. Your may need a rule that such as "no delete if sales > 100 million" that needs checked
CASCADE DELETEs on your foreign keys
This can be tricky if you have multiple cascade paths, but simple otherwise
INSTEAD OF trigger
An INSTEAD OF trigger is like a stored procedure in operation. Note: You'll get an FK violation before an AFTER trigger fires
Personally, I'd use a stored proc so I have explicit deletes. The effect is the same as cascading FKs but more obvious.
For SQL Server 2008, this is the solution:
Generate Delete Statement From Foreign Key Relationships in SQL 2008?
With this solution, you can easily find the correct sequence of DELETE respecting, in the meantime, the foreign keys' relationships.
If you are interested in this theme, you can read also the ORACLE PL/SQL solution:
How to generate DELETE statements in PL/SQL, based on the tables FK relations?

How do the Postgres foreign key 'on update' and 'on delete' options work?

Can anyone provide a clear explanation / example of what these functions do, and when it's appropriate to use them?
Straight from the manual...
We know that the foreign keys disallow creation of orders that do not relate to any products. But what if a product is removed after an order is created that references it? SQL allows you to handle that as well. Intuitively, we have a few options:
Disallow deleting a referenced product
Delete the orders as well
Something else?
CREATE TABLE order_items (
product_no integer REFERENCES products ON DELETE RESTRICT,
order_id integer REFERENCES orders ON DELETE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
Restricting and cascading deletes are the two most common options. RESTRICT prevents deletion of a referenced row. NO ACTION means that if any referencing rows still exist when the constraint is checked, an error is raised; this is the default behavior if you do not specify anything. (The essential difference between these two choices is that NO ACTION allows the check to be deferred until later in the transaction, whereas RESTRICT does not.) CASCADE specifies that when a referenced row is deleted, row(s) referencing it should be automatically deleted as well. There are two other options: SET NULL and SET DEFAULT. These cause the referencing columns to be set to nulls or default values, respectively, when the referenced row is deleted. Note that these do not excuse you from observing any constraints. For example, if an action specifies SET DEFAULT but the default value would not satisfy the foreign key, the operation will fail.
Analogous to ON DELETE there is also ON UPDATE which is invoked when a referenced column is changed (updated). The possible actions are the same.
edit: You might want to take a look at this related question: When/Why to use Cascading in SQL Server?. The concepts behind the question/answers are the same.
I have a PostGreSQL database and I use On Delete when I have a user that I delete from the database and I need to delete it's information from other table. This ways I need to do only 1 delete and FK that has ON delete will delete information from other table.
You can do the same with ON Update. If you update the table and the field have a FK with On Update, if a change is made on the FK you will be noticed on the FK table.
What Daok says is true... it can be rather convenient. On the other hand, having things happen automagically in the database can be a real problem, especially when it comes to eliminating data. It's possible that in the future someone will count on the fact that FKs usually prevent deletion of parents when there are children and not realize that your use of On Delete Cascade not only doesn't prevent deletion, it makes huge amounts of data in dozens of other tables go away thanks to a waterfall of cascading deletes.
#Arthur's comment.
The more frequently "hidden" things happen in the database the less likely it becomes that anyone will ever have a good handle on what is going on. Triggers (and this is essentially a trigger) can cause my simple action of deleting a row, to have wide ranging consequences throughout my database. I issue a Delete statement and 17 tables are affected with cascades of triggers and constraints and none of this is immediately apparent to the issuer of the command. OTOH, If I place the deletion of the parent and all its children in a procedure then it is very easy and clear for anyone to see EXACTLY what is going to happen when I issue the command.
It has absolutely nothing to do with how well I design a database. It has everything to do with the operational issues introduced by triggers.
Instead of writing the method to do all the work, of the cascade delete or cascade update, you could simply write a warning message instead. A lot easier than reinventing the wheel, and it makes it clear to the client (and new developers picking up the code)