Lets say I have two tables - User and Post
I introduced a customized Join table Vote to have a many-to-many relationship between User and Post.
The tables have following structure:
User (UseId, Name)
Post (PostId, UserId, Content)
Vote(Id, UserId, PostId, Value)
Notes:
The emphasized columns of each table is a PK.
The UserId in Post is a FK to User table.
The UserId and PostId columns in Vote table are FK to the
respective tables.
Other columns like Value, Content, Name, etc. are varchar.
Considering the above design is appropriate (if not, suggestions are welcomed :) .....
What I want is:
If a row in Post Table is deleted, the related row in Vote should also be deleted.
If a row in User Table is deleted, the related row in Vote should also be deleted.
If a row in User Table is deleted, the related row's UserId column in Post should be set to NULL.
Can I achieve such kind of relationships, without any Cyclic-Redundancy? If yes, How?
UPDATE:
Check out this awesome answer if you too have faced multiple cascade paths:
You are looking for cascading foreign key relationships. For the first two:
alter table vote
add constraint fk_vote_user
foreign key (userid) references user(userid) on delete cascade;
alter table vote
add constraint fk_vote_post
foreign key (postid) references post(postid) on delete cascade;
For the third:
alter table post
add constraint fk_post_user
foreign key (userid) references user(userid) on delete set null;
These are explained in the documentation.
One way is to add isdeleted bit, changed datetime columns to each table and use triggers to update column values on delete. In that case you will keep history of your votes, posts and users.
Or just on delete triggers.
Or to use cascade relationship as Gordon Linoff posted.
Related
I have a table (multisite network) where I use blog_id as one of the columns which is also needed on some of my custom php functions. Until now, I haven't used FK which should be appropriate for my tables.
So, can I FK to wordpress wp_blogs table? if yes, if ever I delete that blog will it also delete the entries on my other tables that references wp_blogs?
CREATE TABLE wp_blogs (blog_id INT NOT NULL...)
FOREIGN KEY (blog_id)
REFERENCES wp_options(blog_id)
ON DELETE CASCADE;
If you created a table like above then every time when blog_id will remove from wp_options then all rows from wp_blogs will remove too.
When you remove rows from wp_blogs then no rows won't remove from wp_options
Read about ON DELETE CASCADE in MySQL documentation.
On SQLServer:
Table: FinanceiroLancamento
Id
Name
IdFinanceiroLancamentoCaixa
IdFinanceiroLancamentoBanco
Table: FinanceiroLancamentoCaixa
Id
Money
On EF - Code First for "FinanceiroLancamento":
Property(p => p.Name)
.IsRequired();
HasOptional(p => p.FinanceiroLancamentoCaixa)
.WithMany()
.HasForeignKey(p => p.IdFinanceiroLancamentoCaixa);
On EF - Code First for "FinanceiroLancamentoCaixa":
Property(p => p.Money)
.HasPrecision(15, 2)
.IsRequired();
My SQLServer relationship:
ALTER TABLE FinanceiroLancamento ADD CONSTRAINT [FinanceiroLancamento_003_FKEY] FOREIGN KEY ([IdFinanceiroLancamentoCaixa]) REFERENCES FinanceiroLancamentoCaixa([Id]) ON DELETE CASCADE ON UPDATE CASCADE;
So in the source code I can do:
var money = financeiroLancamento.FinanceiroLancamentoCaixa.Money:
The problem in my relationship is that when I delete "FinanceiroLancamento" it´s not deleting "FinanceiroLancamentoCaixa". But if I delete "FinanceiroLancamentoCaixa" it´s delete "FinanceiroLancamento".
I want the opposite, I would like to delete "FinanceiroLancamento" and it´s delete "FinanceiroLancamentoCaixa". How can I do it?
(I would like to continue with EF navigating from FinanceiroLancamento to FinanceiroLancamentoCaixa)
Thanks.
Best regards,
Wilton Ruffato Wonrath
This is very strangely named. I'm not very fit in EF, but I gather that you want many (or zero or one) FinanceiroLancamentoCaixa records for each FinanceiroLancamento record. The normal way to do this in SQL would have an Id column in both tables plus a "IdFinanceiroLancamento" column in the FinanceiroLancamentoCaixa table.
Then you would define the foreign key as
ALTER TABLE FinanceiroLancamentoCaixa
ADD CONSTRAINT FK_FinanceiroLancamentoCaixa_FinanceiroLancamento
FOREIGN KEY (IdFinanceiroLancamento)
REFERENCES FinanceiroLancamento(Id)
ON DELETE CASCADE
ON UPDATE CASCADE;
Then, when you delete the FinanceiroLancamento record, the cascade deletes also the FinanceiroLancamentoCaixa record(s).
I was trying to create a table that has a one to many relationships. but it seems that adding a foreign key in Personal is not working. I am trying to link a Personal Information table to a address table? what is the solution for this error?
Address table saved successfully
Personal table
Unable to create relationship 'FK_Personal_Address'.
Cascading foreign key 'FK_Personal_Address' cannot be created where the
referencing column 'Personal.ID' is an identity column. Could not
create constraint. See previous errors.
The primary key in the Person table is presumably an identity. This is an auto-incrementing integer field.
You need to make the foreign key in the address table of type int, not identity. It will hold integers which correspond to Person records, but you don't want the foreign key to auto-increment. For each record in the child table (address) you will set a specific value for the foreign key indicating to which parent record (Person) it belongs.
Example:
INSERT person (firstname, lastname) VALUES ('John', 'Smith')
This will insert the new person record and the field personid will be filled automatically because it is an IDENTITYfield.
Now to insert an address from John Smith you need to know his personid. For example:
-- Say, for example, personid of John Smith is 55
INSERT address (personid, street, city) VALUES (55, 'High Street', 'London')
So in the person table the personid is generated automatically but in the address table you specify the value that matches an existing person. That's the whole point of a foreign key.
Without more information about your schema it's hard to guess the problem.
I made sure to follow identity, int and primary key discussed in above answer. However, I was still getting the same error.
'xReason' table saved successfully
'xAddress' table
- Unable to create relationship 'FK_xAddress_xReason'.
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_xAddress_xReason". The conflict occurred in database "databaseName", table "dbo.xReason", column 'xReasonID'.
This error resolved when I inserted some data into a Reason table. (table that had a primary key)
If you read this far, this might be your problem.
Without seeing the structure of the tables in the question, I believe the most likely cause is the column in your child table (Address) is marked as an Identity column. In a foreign-key relationship, the parent determines the value of the field, not the child. The column may be the PK in the child table, but not an Identity.
it seem that you try to create a foreign key on Personal.ID related to itself.
You probably want to do something like :
ALTER TABLE Adress WITH NOCHECK ADD CONSTRAINT [FK_Adress_Personnal] FOREIGN KEY(Personal_Id)
REFERENCES Personal (ID)
I got the same error with adding foreign key constraints to one of my tables.
I found the workaround was to add it WITH NOCHECK. why I was able to add the other two foreign keys WITH CHECK but not the third foreign? I found that it was not the table but the order of the foreign key to be added. Any insight to this will be much appreciated.
I have a 1..* relationship between User and Post. (one user has many posts)
Post has a FK called "UserId", which maps to the "UserId" field on User table.
I tried to set this FK as Cascade UPDATE/DELETE, but i get this error:
'Users' table saved successfully
'Posts' table
- Unable to create relationship 'FK_Posts_Users'.
Introducing FOREIGN KEY constraint 'FK_Posts_Users' on table 'Posts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
I have a table called PostHelpful. One Post has many Helpful's.
Helpful has a cascading FK to User (so when a User is deleted, their Helpful's are also deleted).
But i think this is the cause of complaint for "multiple cascade paths".
Because if i delete a User (currently), it will delete their helpfuls. But im trying to add a cacade to Post also, do it would delete the Post, then try and delete the Helpful's for that Post (as Helpful also has a cascading FK to Post). In that scenario, which cascading FK would SQL choose?
Here is the database diagram of the three tables in question:
As you can see, "PostHelpful" is a child to both "Post" and "User" (has FK's to both).
So i can't make both keys cascading? Do i need a trigger on "Users" (AFTER DELETE) to manually delete the helpfuls (and other tables referencing User).
It's not a matter of which path will SQL Server choose, it does not allow it so that it won't wind up in a compromising position. When we ran into this situation, we had to resort to a trigger.
1) As the error message stated, change the Users_PostHelpfuls FK to ON DELETE NO ACTION.
2) Add an INSTEAD OF DELETE trigger to Users:
CREATE TRIGGER dbo.Users_IO_Delete
ON dbo.Users
INSTEAD OF DELETE
AS
BEGIN;
DELETE FROM dbo.PostHelpfuls WHERE UserId IN (SELECT UserId FROM deleted);
DELETE FROM dbo.Users WHERE UserId IN (SELECT UserId FROM deleted);
END;
Now, the FK will still enforce DRI, but the trigger is cascading the delete rather than the FK constraint.
You could replace PostHelpfuls with Posts in the above steps. But when doing this it's best to use the trigger to remove the less independent entity's records. In other words, it's more likely that Posts are related to tables beside Users and PostHelpfuls than PostHelpfuls is related to tables beside Users and Posts.
I have a database table called Lesson:
columns: [LessonID, LessonNumber, Description] ...plus some other columns
I have another table called Lesson_ScoreBasedSelection:
columns: [LessonID,NextLessonID_1,NextLessonID_2,NextLessonID_3]
When a lesson is completed, its LessonID is looked up in the Lesson_ScoreBasedSelection table to get the three possible next lessons, each of which are associated with a particular range of scores. If the score was 0-33, the LessonID stored in NextLessonID_1 would be used. If the score was 34-66, the LessonID stored in NextLessonID_2 would be used, and so on.
I want to constrain all the columns in the Lesson_ScoreBasedSelection table with foreign keys referencing the LessonID column in the lesson table, since every value in the Lesson_ScoreBasedSelection table must have an entry in the LessonID column of the Lesson table. I also want cascade updates turned on, so that if a LessonID changes in the Lesson table, all references to it in the Lesson_ScoreBasedSelection table get updated.
This particular cascade update seems like a very straightforward, one-way update, but when I try to apply a foreign key constraint to each field in the Lesson_ScoreBasedSelection table referencing the LessonID field in the Lesson table, I get the error:
Introducing FOREIGN KEY constraint 'c_name' on table 'Lesson_ScoreBasedSelection' may cause cycles or multiple cascade paths.
Can anyone explain why I'm getting this error or how I can achieve the constraints and cascading updating I described?
You can't have more than one cascading RI link to a single table in any given linked table. Microsoft explains this:
You receive this error message because
in SQL Server, a table cannot appear
more than one time in a list of all
the cascading referential actions that
are started by either a DELETE or an
UPDATE statement. For example, the
tree of cascading referential actions
must only have one path to a
particular table on the cascading
referential actions tree.
Given the SQL Server constraint on this, why don't you solve this problem by creating a table with SelectionID (PK), LessonID, Next_LessonID, QualifyingScore as the columns. Use a constraint to ensure LessonID and QualifyingScore are unique.
In the QualifyingScore column, I'd use a tinyint, and make it 0, 1, or 2. That, or you could do a QualifyingMinScore and QualifyingMaxScore column so you could say,
SELECT * FROM NextLesson
WHERE LessonID = #MyLesson
AND QualifyingMinScore <= #MyScore
AND #MyScore <= QualifyingMaxScore
Cheers,
Eric