I've learned that SQLAlchemy implements some foreign key handling such as setting them null when the parent is deleted, separately from the database, meaning they can be set to different behaviors, and different ways of doing the same thing could get either one. Example:
I have Comment and Subscription, where Subscription has a foreign key relationship to Comment. I started by setting backref = backref('subs', cascade = 'delete, delete-orphan') on the Subscription's relationship, and the result was that session.delete(comment) would delete the subscriptions on the comment properly, but session.query(Comment).filter_by(id = id).delete() would fail with a foreign key violation, because the cascade was set at the ORM level instead of the Postgres level.
Needless to say I find this very confusing, and I want to disable it so that all foreign key handling on deletes is done by Postgres. I'm not finding an obvious way to do it looking at the docs. I read about passive deletes, which sounds like it does what I want except that it doesn't apply to objects already loaded into the session.
Is there a way to disable all ORM-level foreign key handling on deletes? And is there a good reason not to?
Huh, I tried out passive deletes and it seems like it does what I need after all. The words "The cascade="all, delete-orphan" will take effect for instances of MyOtherClass which are currently present in the session" made me think that it would still manually issue deletes for dependent objects if they were added to the session, but if I fetch the depended object and then the dependent one and then modify the dependent, verifying that it's in session.dirty, and then delete the depended and commit, SQLAlchemy doesn't manually issue a delete for the dependent object.
Related
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)?
I am trying to make a delete call that triggers a recursive foreign key (which doesn't have an index). The query is very slow.
I've been searching for a while and it seems my options are
add index on fk -- this is not ideal because the write speed for this table is very important, and already not very good
disable trigger for session -- again not ideal because it's exposed to other transactions, would prefer this only applies to an isolated transaction where others are not affected
extend trigger --- this one i'm curious about. Is it possible to store a local variable with set_config which we then check against i.e if var=== true run trigger else don't? Something like this answer https://stackoverflow.com/a/62010745/7530306
You can change the parameter session_replication_role to replica, then only replica triggers will fire, and foreign key constraints won't be checked. That requires superuser permissions, because it endangers the integrity of the database.
I don't see your point. If you disable the foreign key, why keep it around at all? If you are not ready to pay the price, do without referential integrity.
My advice is:
If you need to delete rows frequently, create the index. The risk of violating the constaint by repeatedly disabling it is too high.
If this is a one-time affair, accept the sequential scan on the referencing table.
I designing a SQL db system(with Postgre) and I have a question about what is the common practice to create a relationship / reference that can persist even when referenced objects are deleted.
For example, there is a UserORM, and ActivityORM, and UserActivityRelation. ActivityORM holds user.id as foreign key to tell who created the activity, and relation table is about which users should know about the activity.
Now, if I want to remove the actor from db, I still want ActivityORM and the relation table to persist so that other users can still know about the activities. I want to know what is the most common / best practice to design such system. Simple answers might be not assigning them as foreign keys, or create an inactive state, but I wonder if there is any better ways. Thank you.
As you mentioned, removing the problematical foreign key constraints would solve the immediate problem of deleting either users or activities on either side of the bridge table. But then this would allow broken relationships to end up in your database.
I propose using soft deletion here. Add an active bit column to both the UserORM and ActivityORM tables. Then, when you need to delete a user or activity, just mark that record as inactive. This would guarantee that the key relationships do not get broken during the deletion. And this approach would also let you view relationships which used to exist, prior to any deletions.
I'm trying to figure out why NHibernate handles one-to-many cascading (using cascade=all-delete-orphan) the way it does. I ran into the same issue as this guy:
Forcing NHibernate to cascade delete before inserts
As far as I can tell NHibernate always performs inserts first, then updates, then deletes. There may be a very good reason for this, but I can't for the life of me figure out what that reason is. I'm hoping that a better understanding of this will help me come up with a solution that I don't hate :)
Are there any good theories on this behavior? In what scenario would deleting orphans first not work? Do all ORMs work this way?
EDIT: After saying there is no reason, here is a reason.
Lets say you have the following scenario:
public class Dog {
public DogLeg StrongestLeg {get;set;}
public IList<DogLeg> Legs {get;set;
}
If you were to delete first, and lets say you delete all of Dog.Legs, then you may delete the StrongestLeg which would cause a reference violation. Hence you cannot DELETE before you UPDATE.
Lets say you add a new leg, and that new leg is also the StrongestLeg. Then you must INSERT before you UPDATE so that the Leg has an Id that can be inserted into Dog.StrongestLegId.
So you must INSERT, UPDATE, then DELETE.
Also as nHibernate is based on Hibernate, I had a look into Hibernate and found several people talking about the same issue.
Support one-to-many list associations with constraints on both (owner_id, position) and (child_id)
Non lazy loaded List updates done in wrong order, cause exception
wrong insert/delete order when updating record-set
Why does Hibernate perform Inserts before Deletes?
Unidirection OneToMany causes duplicate key entry violation when removing from list
And here is the best answer from them:
Gail Badner added a comment - 21/Feb/08 2:30 PM: The problem arises when a new
association entity with a generated ID
is added to the collection. The first
step, when merging an entity
containing this collection, is to
cascade save the new association
entity. The cascade must occur before
other changes to the collection.
Because the unique key for this new
association entity is the same as an
entity that is already persisted, a
ConstraintViolationException is
thrown. This is expected behavior.
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)