How do I fix foreign key constraints - sql

I am creating a multiple choice quiz database and when I am trying to create CorrectAnswer table I am getting the following error:
Msg 1785, Level 16, State 0, Line 15
Introducing FOREIGN KEY constraint 'FK__CorrectAn__Answe__5BE2A6F2' on table 'CorrectAnswer' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 1, Line 15
Could not create constraint or index. See previous errors.
CREATE DATABASE PeriodicTableQuiz
CREATE TABLE Question(
QuestionID INT IDENTITY(1,1) PRIMARY KEY,
QuizQuestion VARCHAR(MAX) DEFAULT NULL
);
CREATE TABLE AnswerChoices(
AnswerID INT IDENTITY(1,1) PRIMARY KEY,
Answer VARCHAR(MAX) DEFAULT NULL,
QuestionID INT NOT NULL,
FOREIGN KEY(QuestionID) REFERENCES Question(QuestionID) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE CorrectAnswer(
CorrectAnswerID INT IDENTITY(1,1) PRIMARY KEY,
QuestionID INT NOT NULL,
FOREIGN KEY(QuestionID) REFERENCES Question(QuestionID) ON DELETE CASCADE ON UPDATE CASCADE,
AnswerID INT NOT NULL,
FOREIGN KEY(AnswerID) REFERENCES AnswerChoices(AnswerID) ON DELETE CASCADE ON UPDATE CASCADE
);
I want to insert questions in the Question table and answers in the AnswersChoices table. CorrectAnswer should have QuestionID and AnswerID referencing Question and AnswerChoices tables, respectively.

While it's true that you simply don't need a QuestionID column in CorrectAnswer since it is redundant, and the following design would solve the current problem:
CREATE TABLE dbo.CorrectAnswer(
CorrectAnswerID INT IDENTITY(1,1) PRIMARY KEY,
AnswerID INT NOT NULL,
FOREIGN KEY(AnswerID) REFERENCES dbo.AnswerChoices(AnswerID)
ON DELETE CASCADE ON UPDATE CASCADE
);
I propose this design instead:
CREATE TABLE dbo.Questions
(
QuestionID INT IDENTITY(1,1) PRIMARY KEY,
Question VARCHAR(MAX) DEFAULT NULL
);
CREATE TABLE dbo.Answers
(
AnswerID INT IDENTITY(1,1) PRIMARY KEY,
Answer VARCHAR(MAX) DEFAULT NULL,
QuestionID INT NOT NULL,
IsCorrectAnswer bit NOT NULL,
CONSTRAINT FK_Question FOREIGN KEY(QuestionID)
REFERENCES dbo.Questions(QuestionID)
ON DELETE CASCADE ON UPDATE CASCADE
);
A constraint can't enforce that only one row can be correct per question, but you can enforce this through a trigger (scope creep for this question). Enforcing at least one row can be a little trickier (much like enforcing that a row must exist in CorrectAnswer in your original design, because when can you do that?), simply because it would mean you have to insert the correct answer first.
You're probably also in need of some metadata to define the order choices appear in the quiz, especially if you do intend to insert correct / incorrect answers in some predictable order.

Because Question is required, all one-to-many relationships where Question is involved is having cascading delete enabled. It means, if you delete a Question
the delete will cascade directly to AnswerChoices
the delete will cascade directly to CorrectAnswer and because CorrectAnswer and
AnswerChoices have a required one-to-many relationship with cascading
delete enabled it will then cascade from AnswerChoices to
CorrectAnswer
So, you have two cascading delete paths from Question to CorrectAnswer - which causes the exception.
You must either make the QuestionId optional in at least one of the tables.
I propose the following:
CREATE DATABASE PeriodicTableQuiz
CREATE TABLE Question(
QuestionID INT IDENTITY(1,1) PRIMARY KEY,
QuizQuestion VARCHAR(MAX) DEFAULT NULL
);
CREATE TABLE Answer(
AnswerID INT IDENTITY(1,1) PRIMARY KEY,
Answer VARCHAR(MAX) DEFAULT NULL,
);
CREATE TABLE CorrectAnswer(
CorrectAnswerID INT IDENTITY(1,1) PRIMARY KEY,
QuestionID INT NOT NULL,
FOREIGN KEY(QuestionID) REFERENCES Question(QuestionID) ON DELETE CASCADE ON UPDATE CASCADE,
AnswerID INT NOT NULL,
FOREIGN KEY(AnswerID) REFERENCES AnswerChoices(AnswerID) ON DELETE CASCADE ON UPDATE CASCADE
);
CorrectAnswer is now having one to many relationship. Deleting a question will delete the correctanswer rows. An answer can now be associated with different questions.
Again, it depends on your requirements whether an answer is a child of question or is independent.

Related

MySQL Incorrect Foreign Key [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
MySQL (mariadb Ver 15.1 Distrib 10.1.44-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2) gave me this error:
ERROR 1005 (HY000) at line 129: Can't create table `Houdini`.`stamp` (errno: 150 "Foreign key constraint is incorrectly formed")
when trying to create this table:
DROP TABLE IF EXISTS stamp;
CREATE TABLE stamp (
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
group_id SMALLINT NOT NULL,
member BOOLEAN NOT NULL DEFAULT FALSE,
rank SMALLINT NOT NULL DEFAULT 1,
description VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY(id),
CONSTRAINT stamp_ibfk_1 FOREIGN KEY (group_id) REFERENCES stamp_group (id) ON DELETE CASCADE ON UPDATE CASCADE
);
What is the correct foreign key constraint for this? I presume group_id can't be used for some reason
The foreign keys are members of the primary key of the quest_award_item table, so they must be the same data type as themselves, but that's not the problem.
You seem to think that they must have the same data type as the primary key of their own table, but this is in fact not required.
The requirement is that foreign key column(s) must have the same data type as the column(s) they reference.
In this case, quest_award_item.quest_id must be the same data type as quest.id.
Likewise, quest_award_item.item_id must be the same data type as item.id.
You haven't shown the table definitions of the quest or item tables, so we can only guess at their data types. But one or other other may have an incompatible data type.
Re your comment:
So how do I fix this?
You posted in a comment below that the quest.id column is defined as SERIAL, which MySQL translates into BIGINT UNSIGNED AUTO_INCREMENT.
The foreign key column that references quest.id must be the same data type as the column it references (the AUTO_INCREMENT part is not necessary to match the data type).
So change your CREATE TABLE:
CREATE TABLE quest_award_item (
quest_id BIGINT UNSIGNED NOT NULL,
item_id INT NOT NULL,
PRIMARY KEY (quest_id, item_id),
CONSTRAINT quest_award_item_ibfk_1 FOREIGN KEY (quest_id) REFERENCES quest (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT quest_award_item_ibfk_2 FOREIGN KEY (item_id) REFERENCES item (id) ON DELETE CASCADE ON UPDATE CASCADE
);
An alternative solution is to modify the quest.id to be an INT, and then your original CREATE TABLE quest_award_item will work.
ALTER TABLE quest MODIFY COLUMN id INT AUTO_INCREMENT;
But if you already have rows in that table with id values great enough that they need to be BIGINT UNSIGNED (i.e. greater than 231-1), then you can't do that.
could be the data type between the two key are not the same
DROP TABLE IF EXISTS stamp;
CREATE TABLE stamp (
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
group_id INT NOT NULL,
member BOOLEAN NOT NULL DEFAULT FALSE,
rank SMALLINT NOT NULL DEFAULT 1,
description VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY(id),
CONSTRAINT stamp_ibfk_1 FOREIGN KEY (group_id) REFERENCES stamp_group (id) ON DELETE CASCADE ON UPDATE CASCADE
);
if you use int for id you should use int for group_id

SQL Server perform a delete on all child records when deleting from a parent

I have 3 tables:
Create TABLE Subjects
(
SubjectID INT PRIMARY KEY NOT NULL IDENTITY(1,1),
SubjectName VARCHAR(20) NOT NULL,
ClassID VARCHAR(10) FOREIGN KEY REFERENCES Classes(ClassID) NOT NULL
);
Create TABLE Topic
(
TopicID INT PRIMARY KEY NOT NULL IDENTITY(1,1),
TopicName VARCHAR(100),
SubjectID INT FOREIGN KEY REFERENCES Subjects(SubjectID)
);
Create Table Worksheet
(
WorksheetName varchar(100) PRIMARY KEY,
TopicID INT Foreign KEY References Topic(TopicID),
Num_Q INT NOT NULL,
W_Type varchar(30)
);
Each one is a one to many relationship. When I try to delete from Subjects I get a foreign key constraint which is fine. What I want to know is how to get around this and perform a query to delete all relating aspects in a cascading style. I looked it up and there's but I am not sure how it works there seems to be multiple queries. Would it be better to create a trigger or is there a basic cascading function to do it all? I'm using visual studio to perform queries but not sure where the options to perform tasks like this are?
You can add the ON DELETE CASCADE right after the foreign key definition:
Create TABLE Subjects (
SubjectID INT PRIMARY KEY NOT NULL IDENTITY(1, 1),
SubjectName VARCHAR(20) NOT NULL,
ClassID VARCHAR(10) NOT NULL
FOREIGN KEY REFERENCES Classes(ClassID) ON DELETE CASCADE
);
You can also define it as a separate constraint, if you like, either within the CREATE TABLE statement or using ALTER TABLE ADD CONSTRAINT.
Here is the DDL for your Topic table with a CASCADE for delete. Its just a matter of defining it in your FK but using a slightly different syntax. This is for MS Sql Server.
CREATE TABLE Topic
(
TopicID INT PRIMARY KEY NOT NULL IDENTITY(1,1),
TopicName VARCHAR(100),
SubjectID INT,
CONSTRAINT FK_Subjects_Topic FOREIGN KEY (SubjectID)
REFERENCES Subjects (SubjectID)
ON DELETE CASCADE
ON UPDATE NO ACTION
)
EDIT - added DELETE CASCADE on Worksheet table based on comment feedback.
Create Table Worksheet
(
WorksheetName varchar(100) PRIMARY KEY,
TopicID INT,
Num_Q INT NOT NULL,
W_Type varchar(30),
CONSTRAINT FK_Topic_Worksheet FOREIGN KEY (TopicID)
REFERENCES Topic (TopicID)
ON DELETE CASCADE
ON UPDATE NO ACTION
);
With this updated definition a delete on table Subjects will also delete child records in table Topics.

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
)

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.

Problems understanding FOREIGN KEY / CASCADE constraints

I need some help at understanding how foreign keys and cascades work. I understood the theory but I'm having troubles to apply these to a real world example.
Let's assume I've got the following tables (and an arbitrary number of other tables that may reference table tags):
CREATE TABLE tags (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) UNIQUE
) Engine=InnoDB;
CREATE TABLE news (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(63),
content TEXT,
INDEX (title)
) Engine=InnoDB;
So I create a further table to provide the many-to-many relation between news and tags:
CREATE TABLE news_tags (
news_id INT UNSIGNED,
tags_id INT UNSIGNED,
FOREIGN KEY (news_id) REFERENCES news (id) ON DELETE ...,
FOREIGN KEY (tags_id) REFERENCES tags (id) ON DELETE ...
) Engine=InnoDB;
My requirements to the cascades:
If I delete a news, all corresponding entries in news_tags should be removed as well.
Same applies for table x that may be added later with x_tags-table.
If I delete a tag, all corresponding entries in news_tags and in every further table x_tags should be removed as well.
I'm afraid that I may have to revisit my table structure for this purpose, but that's alright since I'm only trying to figure out how stuff works.
Any links to good tutorials, SQL-queries or JPA-examples appreciated!
You seem to be proposing something like this, which sounds reasonable to me:
CREATE TABLE tags
(
id INTEGER NOT NULL,
name VARCHAR(20) NOT NULL,
UNIQUE (id),
UNIQUE (name)
);
CREATE TABLE news
(
id INTEGER NOT NULL,
title VARCHAR(30) NOT NULL,
content VARCHAR(200) NOT NULL,
UNIQUE (id)
);
CREATE TABLE news_tags
(
news_id INTEGER NOT NULL,
tags_id INTEGER NOT NULL,
UNIQUE (tags_id, news_id),
FOREIGN KEY (news_id)
REFERENCES news (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (tags_id)
REFERENCES tags (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);