Foreign Key Cascade On Delete - sql

Assume that i have the following tables
User,Article,Comment
A User can Comment on an Article or write an Article.
I wanted the behavior that:
When i delete a User it should delete all of his Articles and
Comments
When i delete an Article it should delete all of its Comments
So i thought i should use FOREIGN KEYS and tried to model the above as the following
CREATE TABLE [User](
UserId int PRIMARY KEY IDENTITY(1,1),
Username nvarchar(50) NOT NULL
)
CREATE TABLE [Article](
ArticleId int PRIMARY KEY IDENTITY(1,1),
UserId int NOT NULL,
FOREIGN KEY(UserId) references [User](UserId) ON DELETE CASCADE
)
CREATE TABLE [Comment](
CommentId int PRIMARY KEY IDENTITY(1,1),
UserId int Not NULL,
ArticleId int NOT NULL ,
FOREIGN KEY(UserId) references [User](UserId) ON DELETE CASCADE,
FOREIGN KEY(ArticleId) references [Article](ArticleId) ON DELETE CASCADE
)
But the problem comes with the Comment causing
Introducing FOREIGN KEY constraint 'FK__Comment__Article__32E0915F' on table 'Comment' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
My question is how would you model this behavior and still use CASCADE ON DELETE AND FOREIGEN KEYS?

You should just be able to remove the Cascade Delete on the User Foreign Key on the Comment.
As when the user is deleted, this will cascade delete the Article, which will in turn Cascade Delete the comments:
CREATE TABLE [Comment](
CommentId int PRIMARY KEY IDENTITY(1,1),
UserId int Not NULL,
ArticleId int NOT NULL ,
FOREIGN KEY(UserId) references [User](UserId),
FOREIGN KEY(ArticleId) references [Article](ArticleId) ON DELETE CASCADE
)

Related

Recover Foreign keys

CREATE TABLE Infrastructure.OBSModules(
OBSId int FOREIGN KEY REFERENCES Infrastructure.OBS(Id) NOT NULL,
ModuleId int FOREIGN KEY REFERENCES Infrastructure.Module(Id) NOT NULL,
Constraint PK_Infrastructure_OBS_Module Primary Key(OBSId,ModuleId)
)
Go
The above code is my code by which I created Infrastructure.OBSType table,but I deleted foreign keys manually so I cant Insert any rows in my table.how can I recover those foreign keys?

PostgreSQL Composite Foreign Key 'columns list must not contain duplicates'

This is my example schema:
CREATE TABLE users (
userid BIGSERIAL PRIMARY KEY,
name varchar(25) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE follows (
userid1 int NOT NULL,
userid2 int NOT NULL,
PRIMARY KEY (userid1, userid2),
FOREIGN KEY (userid1, userid2) REFERENCES users (userid) ON DELETE CASCADE
);
If I run this command, I get:
ERROR: number of referencing and referenced columns for foreign key disagree
And If I add ... REFERENCES users (userid, userid) ON DELETE CASCADE
I get:
ERROR: foreign key referenced-columns list must not contain duplicate
I know it works if I type in each line their respective references, but it would be better to not repeat myself.
How can I achieve that with composite foreign keys using the same dependency?
You need two separate foreign key constraints:
CREATE TABLE follows (
userid1 int NOT NULL,
userid2 int NOT NULL,
PRIMARY KEY (userid1, userid2),
FOREIGN KEY (userid1) REFERENCES users (userid) ON DELETE CASCADE,
FOREIGN KEY (userid2) REFERENCES users (userid) ON DELETE CASCADE
);
Though I prefer the syntax shown by Gordon, if you want a one liner you can compress it as:
CREATE TABLE follows (
userid1 int NOT NULL REFERENCES users ON DELETE CASCADE,
userid2 int NOT NULL REFERENCES users ON DELETE CASCADE,
PRIMARY KEY (userid1, userid2)
);
The down side of this short hand notation is that the FKs don't have names. It could be tricky to manage them in the future should you need to remove them or modify them. Experience has shown me that it's better to name them, and for that you need to use the full syntax, as in:
CREATE TABLE follows (
userid1 int NOT NULL,
userid2 int NOT NULL,
PRIMARY KEY (userid1, userid2),
CONSTRAINT fk1 FOREIGN KEY (userid1)
REFERENCES users (userid) ON DELETE CASCADE,
CONSTRAINT fk2 FOREIGN KEY (userid2)
REFERENCES users (userid) ON DELETE CASCADE
);
...just to get the fk1 and fk2 identifiers.

showing error for creating table with foreign key

CREATE TABLE location (
uid int not null auto_increment primary key,
name varchar(255) NOT NULL,
`state_uid` int not null,
FOREIGN KEY location(state_uid)
REFERENCES state(uid)
ON UPDATE CASCADE
ON DELETE RESTRICT,
`city_uid` int not null,
FOREIGN KEY location(city_uid)
REFERENCES city(uid)
ON UPDATE CASCADE
ON DELETE RESTRICT,
`area_uid` int not null,
FOREIGN KEY location(area_uid)
REFERENCES area(uid)
ON UPDATE CASCADE
ON DELETE RESTRICT
);
CREATE TABLE location (
uid int not null auto_increment primary key,
name varchar(255) NOT NULL,
state_uid int not null,
city_uid int not null,
area_uid int not null,
CONSTRAINT fk_state FOREIGN KEY (state_uid) REFERENCES state(uid) ,
CONSTRAINT fk_city FOREIGN KEY (city_uid) REFERENCES city(uid) ,
CONSTRAINT fk_area FOREIGN KEY (area_uid) REFERENCES area(uid)
);
Try this query
make sure parent tables exists
Your version is almost just fine. The problem is the location in the foreign key reference.
You have no issue with the cascading stuff or the ordering of the columns. So, this works:
CREATE TABLE location (
uid int not null auto_increment primary key,
name varchar(255) NOT NULL,
`state_uid` int not null,
FOREIGN KEY (state_uid) REFERENCES state(uid) ON UPDATE CASCADE ON DELETE RESTRICT,
`city_uid` int not null,
FOREIGN KEY (city_uid) REFERENCES city(uid) ON UPDATE CASCADE ON DELETE RESTRICT,
`area_uid` int not null,
FOREIGN KEY (area_uid) REFERENCES area(uid) ON UPDATE CASCADE ON DELETE RESTRICT
);
Here is a SQL Fiddle.
Note that it is often traditional to put explicit foreign key (and other constraints) after the column definitions, there is no rule or standard for this. In fact, most databases support in-line foreign key definitions. MySQL does not, however.

Composite Foreign Key from multiple related tables

Just beginning to learn about SQL and had a question I couldn't figure out.
I have a setup based on the following tables and their primary keys, the columns with the same name between tables are constrained by foreign keys:
Company:
CompanyId
Division:
CompanyId
DivisionId
Resource:
CompanyId
ResourceId
DivisionResource :
CompanyId
DivisionId
ResourceId
DivisionResource is used to create a many to many relation between division and resource and constrain them so that divisions can only be linked to resources of the same company.
Without the DivisionResource, Division and Resource wouldn't need the CompanyId as a primary key to contain unique records.
So my question is this: Is there a way to create a similar constraint as DivisionResource creates without forcing Division and Resource to have an extra column in its primary key?
ResourceCompany and DivisionCompany in the schema below are connecting tables. They will have CompanyId in their primary key but Resource and Division will have primary keys with one column. This is what you looked for.
Resource -> ResourceCompany
DivisionResource -> ResourceCompany
Division -> DivisionCompany
DivisionResource -> DivisionCompany
create table Company (CompanyId int primary key);
create table DivisionCompany (
CompanyId int foreign key references Company(CompanyId),
DivisionId int,
constraint pk_div_company primary key (DivisionId, CompanyId)
);
create table Division (
DivisionId int primary key,
CompanyId int,
constraint fk_div_company foreign key (DivisionId, CompanyId) references DivisionCompany(DivisionId, CompanyId));
create table ResourceCompany (
CompanyId int foreign key references Company(CompanyId),
ResourceId int,
constraint pk_res primary key (ResourceId, CompanyId));
create table Resource(
ResourceId int primary key,
CompanyId int,
constraint fk_res_company foreign key (ResourceId, CompanyId) references ResourceCompany(ResourceId, CompanyId)
);
create table DivisionResource(
CompanyId int,
DivisionId int,
ResourceId int,
constraint pk_DivRes primary key (DivisionId, ResourceId),
constraint fk_DivCompany foreign key (DivisionId, CompanyId) references DivisionCompany(DivisionId, CompanyId),
constraint fk_ResCompany foreign key (ResourceId, CompanyId) references ResourceCompany(ResourceId, CompanyId)
);
Create INSTEAD OF trigger on insert and update of DivisionResource
The trigger will check if Divistion and Resource have the same company. If they don't, it will fail the modification
Alternatively, it would be even better to have a stored procedure modifying DivisionResource. Then the trigger needs to call it.
I'm assuming that you're trying to create the following schema:
CREATE TABLE company (
companyId int PRIMARY KEY)
CREATE TABLE division (
divisionId int PRIMARY KEY,
companyId int
REFERENCES company (companyId) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE TABLE resource (
resourceId int PRIMARY KEY,
companyId int
REFERENCES company (companyId) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE TABLE divisionResource (
divisionId int
REFERENCES division (divisionId) ON DELETE CASCADE ON UPDATE CASCADE,
resourceId int
REFERENCES resource (resourceId) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (divisionId, resourceId))
Which throws:
Introducing FOREIGN KEY constraint on table 'divisionResource' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Changing either divisionId or resourceId to ON DELETE NO ACTION ON UPDATE NO ACTION will essentially break the referential integrity. What I suggested in my comment was to make a surrogate key. However, it would be better if you created a separate table for resource. This will maintain the referential integrity and also normalize the schema:
CREATE TABLE company (
companyId int PRIMARY KEY)
CREATE TABLE division (
divisionId int PRIMARY KEY,
companyId int
REFERENCES company (companyId) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE TABLE resource (
resourceId int PRIMARY KEY)
CREATE TABLE companyResource (
resourceId int
REFERENCES resource (resourceId) ON DELETE CASCADE ON UPDATE CASCADE,
companyId int
REFERENCES company (companyId) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE TABLE divisionResource (
divisionId int
REFERENCES division (divisionId) ON DELETE CASCADE ON UPDATE CASCADE,
resourceId int
REFERENCES resource (resourceId) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (divisionId, resourceId))

Why is this a cyclical foreign key constraint?

I came upon this code, marked "error," in an application I'm to update. Running it on a test database gives a cyclical reference error:
The referential relationship will result in a cyclical reference that is not allowed (Constraint name = descriptions_fk_2)
I named the constraints to see which one caused the problem.
CREATE TABLE items (
id INT NOT NULL UNIQUE IDENTITY,
name NCHAR(100) NOT NULL UNIQUE,
PRIMARY KEY (id)
);
CREATE TABLE sources (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
CREATE TABLE descriptions (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
source_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT descriptions_fk_1 FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT descriptions_fk_2 FOREIGN KEY (source_id)
REFERENCES sources(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
Why is this a cyclical reference? The descriptions table is linked to two separate tables, but none of them link back to descriptions.
It's not strictly cyclical - but there are multiple cascade paths. So you could cascade delete a row in items two ways:
1) description -> item
2) description -> source -> item
And, for that reason, it's disallowed.
I believe it's a performance concern, as PostGres will allow cycles like that and will just work it out, but deletes under those circumstances can be quite slow.
For some further reading about why it's disallowed, please see this answer.