Use two foreign key in table with 'on delete cascade' - sql

CREATE TABLE Comments(
Id INT PRIMARY KEY IDENTITY(0,1),
TEXT NOT NULL,
Date Date NOT NULL ,
Point INT NOT NULL DEFAULT(0),
ID_User INT FOREIGN KEY REFERENCES Users(Id) ON DELETE CASCADE NOT NULL,
ID_Post INT FOREIGN KEY REFERENCES Posts(Id) NOT NULL
)
When I delete User from Users table it show me error that Comments table has
other Reference Key. What i have to do ?
The DELETE statement conflicted with the REFERENCE constraint "FK__Comments__ID_Pos__76969D2E". The conflict occurred in database "Facebook", table "dbo.Comments", column 'ID_Post'.

If you want to delete a user record, you need to delete the records in the foreign key tables.
In this case, you need to delete records in Comments table.
DELETE from dbo.Commnts
Where ID_User = "userid"
Then, you can remove the user record from Users table

I did some work on this, there is no error in the foreign keys you referenced here. you may probably have reference to comment id in some other tables.
this is what I tried
CREATE TABLE Users(
Id int primary key
)
CREATE TABLE posts(
Id int primary key
)
insert into Users values(1);
insert into Users values(2);
insert into posts values(3);
insert into posts values(4);
CREATE TABLE Comments(
Id INT PRIMARY KEY IDENTITY(0,1),
ID_User INT FOREIGN KEY REFERENCES Users(Id) ON DELETE CASCADE NOT NULL,
ID_Post INT FOREIGN KEY REFERENCES Posts(Id) NOT NULL
)
insert into Comments values(1,3);
insert into Comments values(2,4);
DELETE
FROM Users
WHERE id = 1 --this works fine

Related

Invalid Column Name when Create trigger using SQL server

I encountered several errors when I tried to create a simple trigger using SQL server. Tables are created using:
CREATE TABLE person
(
person_id INT NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
phone INT,
birth_date DATE,
address VARCHAR
);
CREATE TABLE volunteers
(
person_id INT NOT NULL PRIMARY KEY,
skill VARCHAR,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE physicians
(
person_id INT NOT NULL PRIMARY KEY,
speciality VARCHAR,
phone_number INT,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE patients
(
person_id INT NOT NULL PRIMARY KEY,
contact_date DATE,
physician_id INT, -- TODO determine whether NULL able
-- TODO determine ON DELETE ACTION
CHECK (person_id <> physician_id),
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (physician_id) REFERENCES physicians(person_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE outpatients
(
person_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (person_id) REFERENCES patients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE visits
(
person_id INT NOT NULL,
date DATE NOT NULL,
comments VARCHAR,
PRIMARY KEY (person_id, date),
FOREIGN KEY (person_id) REFERENCES outpatients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE employees
(
person_id INT NOT NULL PRIMARY KEY,
date_hire DATE,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE nurses
(
person_id INT NOT NULL PRIMARY KEY,
certificate VARCHAR,
care_center_name VARCHAR,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
-- FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
-- ON DELETE CASCADE
-- ON UPDATE CASCADE
);
CREATE TABLE care_centers
(
name VARCHAR PRIMARY KEY NOT NULL,
nurses_in_charge INT NOT NULL,
location VARCHAR,
type VARCHAR
-- FOREIGN KEY (nurses_in_charge) REFERENCES registered_nurse(person_id)
);
CREATE TABLE registered_nurse
(
person_id INT NOT NULL PRIMARY KEY,
care_center_name VARCHAR,
FOREIGN KEY (person_id) REFERENCES nurses(person_id),
-- ON DELETE CASCADE
-- ON UPDATE CASCADE,
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
-- ON DELETE CASCADE
-- ON UPDATE CASCADE
);
ALTER TABLE nurses
ADD constraint nurses__care_centers_FK
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE care_centers
ADD constraint care_centers__registered_nurse_FK
FOREIGN KEY (nurses_in_charge) REFERENCES registered_nurse(person_id);
CREATE TABLE beds
(
bed_number INT NOT NULL,
room_number INT NOT NULL,
care_center_name VARCHAR,
PRIMARY KEY (bed_number,room_number),
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE residents
(
person_id INT NOT NULL PRIMARY KEY,
date_admitted DATE,
bed_number INT,
room_number INT,
FOREIGN KEY (person_id) REFERENCES patients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (bed_number, room_number) REFERENCES beds(bed_number, room_number)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE staffs
(
person_id INT NOT NULL PRIMARY KEY,
job_class DATE,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE technicians
(
person_id INT NOT NULL PRIMARY KEY,
skill VARCHAR,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE laboratories
(
name VARCHAR NOT NULL PRIMARY KEY,
location VARCHAR
);
CREATE TABLE assignTechnicianToLab
(
person_id INT NOT NULL,
laboratories_name VARCHAR NOT NULL,
PRIMARY KEY(person_id, laboratories_name),
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (laboratories_name) REFERENCES laboratories(name)
ON DELETE CASCADE
ON UPDATE CASCADE
)
And the trigger I am trying to create:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger
ON nurses
FOR INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted where certificate = "RN")
BEGIN
INSERT INTO registered_nurse
VALUES(inserted.person_id, nurses.care_center_name);
END
END;
And I keep getting errors saying that:
Msg 207, Level 16, State 1, Procedure registered_nurse_trigger, Line 6
Invalid column name 'RN'.
Msg 4104, Level 16, State 1, Procedure registered_nurse_trigger, Line 8
The multi-part identifier "inserted.person_id" could not be bound.
Msg 4104, Level 16, State 1, Procedure registered_nurse_trigger, Line 8
The multi-part identifier "nurses.care_center_name" could not be bound.
I've been hanging over here for several hours and I really appreciate anyone can help me with it. Thank you very much!
You can't use VALUES() like that. Use INSERT..SELECT instead:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger ON nurses
For INSERT, UPDATE
AS BEGIN
INSERT INTO registered_nurse (person_id, care_center_name)
SELECT person_id, care_center_name
FROM inserted
WHERE certificate = 'RN'
END;
I don't think you need to reference nurses or have that EXISTS() either, but maybe I'm overlooking something.
You are using double quote instead of single quotes in your Create Trigger statement try this and see if it works:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger ON nurses
FOR INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted where certificate = 'RN')
BEGIN
INSERT INTO registered_nurse
SELECT person_id, care_center_name FROM Inserted;
END
END;

How to model ownership of a resource (e.g. credit card)

I'm trying to figure out how to model ownership of a resource. An example can be a credit card. In a scenario a credit card can be owned by a company, a business or a business_admin which is used to pay for a service that belongs to a business.
What I came up with is a table business_payment that references company_payment_method, business_payment_method, business_admin_payment_method as well as the business that gets the payment method assigned. They are all nullable which requires a trigger to check if the insert is valid - exactly one ID must be not null before insert:
CREATE TABLE business_payment (
business_id BIGINT NOT NULL,
CONSTRAINT fk__business_payment__business
FOREIGN KEY (business_id)
REFERENCES business(id)
ON DELETE CASCADE,
company_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__company_payment_method
FOREIGN KEY (company_payment_method_id)
REFERENCES company_payment_method(id)
ON DELETE CASCADE,
business_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__business_payment_method
FOREIGN KEY (business_payment_method_id)
REFERENCES business_payment_method(id)
ON DELETE CASCADE,
business_admin_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__business_admin_payment_method
FOREIGN KEY (business_admin_payment_method_id)
REFERENCES business_admin_payment_method(id)
ON DELETE CASCADE
);
Pro:
The advantage I see here is that if any of the credit cards are deleted e.g.
DELETE FROM company_credit_card WHERE id = #companyCreditCardId;
also the business_payment_method will get deleted. On the application layer nobody has to take care of the cleaning this up.
Con:
On the other hand I now have a table which has NULL values and if Ever there was a new entity x_credit_card I'd have to add this column to this table as well as make sure the trigger does its job correctly.
So this is the solution I was thinking about but I'm not sure if this is very elegant - especially because I'm creating an M*N matrix with (M*N - M) NULL entries.
Can I do better than this setup?
Below is the complete code if you want to check it out. Unfortunately I can't make it run on SQLFiddle because of this DELIMITER thing.
DROP TABLE IF EXISTS company_business;
DROP TABLE IF EXISTS company_employee;
DROP TABLE IF EXISTS payment_method;
DROP TABLE IF EXISTS business_payment;
DROP TABLE IF EXISTS business_admin_payment_method;
DROP TABLE IF EXISTS company_payment_method;
DROP TABLE IF EXISTS business_payment_method;
DROP TABLE IF EXISTS company;
DROP TABLE IF EXISTS business_admin;
DROP TABLE IF EXISTS business;
CREATE TABLE company (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE business_admin (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE business (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE company_employee (
company_id BIGINT NOT NULL,
CONSTRAINT fk__company_employee__company
FOREIGN KEY (company_id)
REFERENCES company(id)
ON DELETE CASCADE,
business_admin_id BIGINT NOT NULL,
CONSTRAINT fk_company_employee__business_admin
FOREIGN KEY (business_admin_id)
REFERENCES business_admin(id)
ON DELETE CASCADE,
PRIMARY KEY (company_id, business_admin_id)
);
CREATE TABLE company_business (
company_id BIGINT NOT NULL,
CONSTRAINT fk__company_business__company
FOREIGN KEY (company_id)
REFERENCES company(id)
ON DELETE CASCADE,
business_id BIGINT NOT NULL,
CONSTRAINT fk__company_business__business
FOREIGN KEY (business_id)
REFERENCES business(id)
ON DELETE CASCADE,
PRIMARY KEY (company_id, business_id)
);
SET #businessAdminId1 = 1;
INSERT INTO business_admin(id) VALUES (#businessAdminId1);
SET #companyId1 = 1;
INSERT INTO company(id) VALUES (#companyId1);
INSERT INTO company_employee(company_id, business_admin_id) VALUES (#companyId1,#businessAdminId1);
SET #businessId1 = 1;
INSERT INTO business VALUES (#businessId1);
INSERT INTO company_business VALUES(#companyId1, #businessId1);
CREATE TABLE company_payment_method (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
company_id BIGINT NOT NULL,
CONSTRAINT fk__company_payment_method__company
FOREIGN KEY (company_id)
REFERENCES company(id)
ON DELETE CASCADE,
payment_method_token VARCHAR(128) NOT NULL
);
CREATE TABLE business_payment_method (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
business_id BIGINT NOT NULL,
CONSTRAINT fk__business_payment_method__business
FOREIGN KEY (business_id)
REFERENCES business(id)
ON DELETE CASCADE,
payment_method_token VARCHAR(128) NOT NULL
);
CREATE TABLE business_admin_payment_method (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
business_admin_id BIGINT NOT NULL,
CONSTRAINT fk__business_admin_payment_method__business_admin
FOREIGN KEY (business_admin_id)
REFERENCES business_admin(id)
ON DELETE CASCADE,
payment_method_token VARCHAR(128) NOT NULL
);
CREATE TABLE business_payment (
business_id BIGINT NOT NULL,
CONSTRAINT fk__business_payment__business
FOREIGN KEY (business_id)
REFERENCES business(id)
ON DELETE CASCADE,
company_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__company_payment_method
FOREIGN KEY (company_payment_method_id)
REFERENCES company_payment_method(id)
ON DELETE CASCADE,
business_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__business_payment_method
FOREIGN KEY (business_payment_method_id)
REFERENCES business_payment_method(id)
ON DELETE CASCADE,
business_admin_payment_method_id BIGINT,
CONSTRAINT fk__business_payment__business_admin_payment_method
FOREIGN KEY (business_admin_payment_method_id)
REFERENCES business_admin_payment_method(id)
ON DELETE CASCADE
);
DELIMITER //
CREATE TRIGGER before_insert_business_payment_method BEFORE INSERT ON business_payment
FOR EACH ROW BEGIN
DECLARE notNullForeignKeyFound BOOLEAN;
DECLARE errorMessage VARCHAR(100);
SET errorMessage = 'Exact one foreign key must be not null!';
SET notNullForeignKeyFound = FALSE;
-- Company credit card ID
IF NEW.company_payment_method_id IS NOT NULL THEN
SET notNullForeignKeyFound = TRUE;
END IF;
-- Business credit card ID
IF NEW.business_payment_method_id IS NOT NULL THEN
IF notNullForeignKeyFound IS TRUE THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
END IF;
SET notNullForeignKeyFound = TRUE;
END IF;
-- Business admin credit card ID
IF NEW.business_admin_payment_method_id IS NOT NULL THEN
IF notNullForeignKeyFound IS TRUE THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
END IF;
SET notNullForeignKeyFound = TRUE;
END IF;
-- Check if at least one ID is not null
IF notNullForeignKeyFound IS FALSE THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
END IF;
END//
DELIMITER ;
SET #companyCreditCardId1 = 1;
INSERT INTO company_payment_method (id, company_id, payment_method_token) VALUES (#companyCreditCardId1, #companyId1, 'wergef');
SET #businessCreditCardId1 = 1;
INSERT INTO business_payment_method (id, business_id, payment_method_token) VALUES (#businessCreditCardId1, #businessId1, 'asjio');
--
-- Here comes the actual action ..
--
-- Succeeds
INSERT INTO business_payment (business_id, business_payment_method_id) VALUES (#businessId1, #companyCreditCardId1);
-- Fails
-- INSERT INTO business_payment (business_id, company_payment_method_id, business_payment_method_id) VALUES (#businessId1, #companyCreditCardId1, #businessCreditCardId1);
-- The following will delete:
-- + business_payment_method.payment_method_id = 1
-- + busuiness_payment_method.payment_method_id = 1
DELETE FROM business_payment_method WHERE id = #companyCreditCardId1;
Another way would be for you to have a business_entity table with an entity_type(business, employee, admin, etc.). Payment could have a single Foreign Key back to entity. You would then have a detail table for each entity type that would have the detail information for that particular type of entity.
business_entity--1----M--business_payment
| |--1-----1--employee
|--1-------1--admin

Foreign Key Cascade On Delete

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
)

Want to update foreign key table at same time when primary key update in SQL

I have a scenario with a table RegisteredUsers and a primary key on userid.
Here is code:
create table RegisteredUsers
(
userId INT not null IDENTITY(1,1),
userName varchar(255) not null,
userpassword varchar(255) not null,
primary key (userId)
);
A second table Answers has a foreign key reference to RegisteredUsers:
create table answers (
surveyid int not null
foreign key (userId) references RegisteredUsers (userId)
);
Whenever I insert a row into the first table, I want to also insert a row into table Answers. When I insert value now, it only inserts into the first table. What do I need to do?
I am using SQL Server 2008. Please help. Thanks

Check if data exists in another table on insert?

Table A
(
Table_A_ID int
)
Table B
(
Table_B_ID int
Value int
)
Say I want to insert data into Table B, where 'Value' would be the same as a Table_A_ID.
How would I make a constraint or check that the data actually exists in the table on insertion?
You probably need to enforce data integrity not only on INSERT into Table B, but also on UPDATE and DELETE in both tables.
Anyway options are:
FOREIGN KEY CONSTRAINT on Table B
TRIGGERs on both tables
As a last resort if for some reason 1 and 2 is not an option STORED PROCEDUREs for all insert, delete update operations for both tables
The preferred way to go in most cases is FOREIGN KEY CONSTRAINT.
Yap, I agree with #peterm.
Cause, if your both Table_A_ID and Table_B_Id are primary keys for both tables, then you don't even need two tables to store the value. Since, your two tables are seems to be on 'one-to-one' relationship. It's one of the database integrity issues.
I think you didn't do proper normalisation for this database.
Just suggesting a good idea!
I found this example which demonstrates how to setup a foreign key constraint.
Create employee table
CREATE TABLE employee (
id smallint(5) unsigned NOT NULL,
firstname varchar(30),
lastname varchar(30),
birthdate date,
PRIMARY KEY (id),
KEY idx_lastname (lastname)
) ENGINE=InnoDB;
Create borrowed table
CREATE TABLE borrowed (
ref int(10) unsigned NOT NULL auto_increment,
employeeid smallint(5) unsigned NOT NULL,
book varchar(50),
PRIMARY KEY (ref)
) ENGINE=InnoDB;
Add a constraint to borrowed table
ALTER TABLE borrowed
ADD CONSTRAINT FK_borrowed
FOREIGN KEY (employeeid) REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
NOTE: This tells MySQL that we want to alter the borrowed table by adding a constraint called ‘FK_borrowed’. The employeeid column will reference the id column in the employee table – in other words, an employee must exist before they can borrow a book.
The final two lines are perhaps the most interesting. They state that if an employee ID is updated or an employee is deleted, the changes should be applied to the borrowed table.
NOTE: See the above URL for more details, this is just an excerpt from that article!
Create a foreign key constraint on the column 'Value' on table B that references the 'Table_A_ID' column.
Doing this will only allow values that exist in table A to be added into the 'Value' field of table B.
To accomplish this you first need to make Table_A_ID column the primary key for table A, or it at least has to have some sort of unique constraint applied to it to be a foreign key candidate.
BEGIN TRANSACTION -- REMOVE TRANSACTION AND ROLLBACK AFTER DONE TESTING
--PUT A PRIMARY KEY ON TABLE A
CREATE TABLE A
( Table_A_ID int CONSTRAINT PK_A_Table_A_ID PRIMARY KEY)
--ON VALUE ADD A FOREIGN KEY CONSTRAINT THAT REFERENCEs TABLE A
CREATE TABLE B
( Table_B_ID int,
[Value] int CONSTRAINT FK_B_Value_A REFERENCES A(Table_A_ID)
)
-- TEST VALID INSERT
INSERT A (Table_A_ID) VALUES (1)
INSERT B (Table_B_ID, [Value]) VALUES (1,1)
--NOT ALLOW TO INSERT A VALUE THAT DOES NOT EXIST IN A
--THIS WILL THROW A FOREIGN KEY CONSTRAINT ERROR
INSERT B (Table_B_ID, [Value]) VALUES (1,2) -- 2 DNE in table A
ROLLBACK
Note: there is no magic to 'FK_B_Value_A' or 'PK_A_Table_A_ID' it simply a naming convention and be called anything. The syntax on the foreign key and primary key lines work like this:
column-definition CONSTRAINT give-the-constraint-a-name REFERENCES table-name ( table-column )
column-definition CONSTRAINT give-the-constraint-a-name PRIMARY KEY