I managed a relational database and I want to delete a table and their sons (reference as a reference value like fk_table_value).
When I'm doing a simple "DELETE FROM companies WHERE id=4" I have that error message : "Cannot delete or update a parent row: a foreign key constraint fails".
Here you can see my database map.
CREATE TABLE buildings (
building_no INT PRIMARY KEY AUTO_INCREMENT,
building_name VARCHAR(255) NOT NULL,
address VARCHAR(255) NOT NULL
);
CREATE TABLE rooms (
room_no INT PRIMARY KEY AUTO_INCREMENT,
room_name VARCHAR(255) NOT NULL,
building_no INT NOT NULL,
FOREIGN KEY (building_no)
REFERENCES buildings (building_no)
ON DELETE CASCADE
);
Lets say you have defined two tables like this. So we can see that rooms have a foreign key on buildings and it is set ON DELETE CASCADE. That means that if entity from buildings is deleted all the room with that building_no will be also deleted.
This will also fix your problem.
Related
I am having trouble figuring out the best design for my many-to-many relationship in my database. My project allows users to create what we are calling log alarms. A log alarm will check if a given log meets certain criteria and, if so, it will send a message to an AWS SNS topic. What I want to do is relate log alarms to AWS SNS topics. I also want to relate which user assigned that log alarm to that AWS SNS topic.
I have a table class XRefUserLogAlarmSNSTopic. It has three foreign keys. The goal of this table is to relate which SNS topics are related to what log alarms and to indicate which user made the relation. This seems rather messy to me and I get all sorts of errors when I try to create new log alarms or join tables in Spring JPA. My question is, is there are better database structure for what I am trying to achieve
UserId INT NOT NULL AUTO_INCREMENT,
Username VARCHAR(50) NOT NULL,
Password TEXT NOT NULL,
Email VARCHAR(255) NOT NULL,
Dashboard LONGTEXT NOT NULL,
PRIMARY KEY (UserId),
UNIQUE (Username),
UNIQUE (Email)
);
CREATE TABLE SNSTopics (
SNSTopicId INT NOT NULL AUTO_INCREMENT,
TopicName VARCHAR(50) NOT NULL,
TopicArn VARCHAR(255) NOT NULL,
PRIMARY KEY (SNSTopicId),
UNIQUE (TopicName),
UNIQUE (TopicArn)
);
CREATE TABLE LogGroups (
LogGroupId INT NOT NULL AUTO_INCREMENT,
Name VARCHAR(255) NOT NULL,
PRIMARY KEY (LogGroupId),
UNIQUE (Name)
);
CREATE TABLE Keywords (
KeywordId INT NOT NULL AUTO_INCREMENT,
Word VARCHAR(70),
PRIMARY KEY (KeywordId),
UNIQUE (Word)
);
CREATE TABLE LogAlarms (
LogAlarmId INT NOT NULL AUTO_INCREMENT,
LogLevel VARCHAR(5) NOT NULL CHECK (LogLevel IN ('TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR')),
Comparison VARCHAR(2) CHECK (Comparison IN ('==', '<', '<=', '>', '>=')),
AlarmName VARCHAR(255) NOT NULL,
KeywordRelationship CHAR(3) CHECK (KeywordRelationship IN ('ANY', 'ALL', NULL)),
PRIMARY KEY (LogAlarmId),
UNIQUE (AlarmName)
);
CREATE TABLE MetricAlarms (
MetricAlarmId INT NOT NULL AUTO_INCREMENT,
AlarmArn VARCHAR(100) NOT NULL,
PRIMARY KEY (MetricAlarmId),
UNIQUE (AlarmArn)
);
CREATE TABLE XRefUserMetricAlarm (
UserMetricAlarmId INT NOT NULL AUTO_INCREMENT,
UserId INT NOT NULL,
MetricAlarmId INT NOT NULL,
PRIMARY KEY (UserMetricAlarmId),
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE CASCADE,
FOREIGN KEY (MetricAlarmId) REFERENCES MetricAlarms(MetricAlarmId) ON DELETE CASCADE,
UNIQUE (UserId, MetricAlarmId)
);
CREATE TABLE XRefLogAlarmLogGroup (
LogAlarmLogGroupId INT NOT NULL AUTO_INCREMENT,
LogAlarmId INT NOT NULL,
LogGroupId INT NOT NULL,
PRIMARY KEY (LogAlarmLogGroupId),
FOREIGN KEY (LogAlarmId) REFERENCES LogAlarms(LogAlarmId) ON DELETE CASCADE,
FOREIGN KEY (LogGroupId) REFERENCES LogGroups(LogGroupId) ON DELETE CASCADE,
UNIQUE (LogAlarmId, LogGroupId)
);
CREATE TABLE XRefLogAlarmKeyword (
LogAlarmKeywordId INT NOT NULL AUTO_INCREMENT,
LogAlarmId INT NOT NULL,
KeywordId INT NOT NULL,
PRIMARY KEY (LogAlarmKeywordId),
FOREIGN KEY (LogAlarmId) REFERENCES LogAlarms(LogAlarmId) ON DELETE CASCADE,
FOREIGN KEY (KeywordId) REFERENCES Keywords(KeywordId) ON DELETE CASCADE,
UNIQUE (LogAlarmId, KeywordId)
);
CREATE TABLE XRefUserLogAlarmSNSTopic (
UserLogAlarmSNSTopicId INT NOT NULL AUTO_INCREMENT,
LogAlarmId INT NOT NULL,
SNSTopicId INT NOT NULL,
UserId INT NOT NULL,
PRIMARY KEY (UserLogAlarmSNSTopicId),
FOREIGN KEY (LogAlarmId) REFERENCES LogAlarms(LogAlarmId) ON DELETE CASCADE,
FOREIGN KEY (SNSTopicId) REFERENCES SNSTopics(SNSTopicId) ON DELETE CASCADE,
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE CASCADE,
UNIQUE (LogAlarmId, SNSTopicId, UserId)
);```
To match your description, your XRefUserLogAlarmSNSTopic is not correct.
You do not actually want to link three entities, just two: you want to relate log alarms to AWS SNS topics (which are the two values that identify that relationship), and then add a user as an attribute to that relation. Although in this case this specific attribute refers to another entity, it is logically not fundamentally different than e.g. a timestamp that stores when that relationsship was created (by that user).
The difference to your current table is the primary key/unique key: your current table allows you to add a relationship between an alarm and a topic several times if different users add them, as only (alarm, topic, user) needs to be unique, instead of (alarm, topic) being unique, and user being an attribute to that relation.
I would also consider if you want an ON DELETE CASCADE for the UserId column. While cascading for LogAlarmId and SNSTopicId makes sense, it's not obvious that you need to remove all relations when you delete the user that created them (although it of course depends on your requirements). It may be better to just set them to null. So I would propose a table like
CREATE TABLE XRefLogAlarmSNSTopic ( -- user not part of table name
LogAlarmSNSTopicId INT NOT NULL AUTO_INCREMENT,
LogAlarmId INT NOT NULL,
SNSTopicId INT NOT NULL,
UserId INT NULL, -- null
PRIMARY KEY (UserLogAlarmSNSTopicId),
FOREIGN KEY (LogAlarmId) REFERENCES LogAlarms(LogAlarmId) ON DELETE CASCADE,
FOREIGN KEY (SNSTopicId) REFERENCES SNSTopics(SNSTopicId) ON DELETE CASCADE,
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE SET NULL, -- set null
UNIQUE (LogAlarmId, SNSTopicId) -- user not part of primary key candidate
)
It is obviously also possible that you want that 3-way-relationship, e.g. each user creates their own alarm-topic-relations, in which case your table would be correct, just your description would not be precise enough.
I found this article on cascading deletes (http://www.mysqltutorial.org/mysql-on-delete-cascade/) and it answered a lot of my initial questions but now I have another one. I don't have anyone to confirm with except you folks so I do appreciate the feedback.
Scenario:
Master User Table (masterTable)
Supporting Table 1 (subTable1)
Supporting Table 2 (subTable2)
Support... you get the idea. (subTable...)
If a user is removed from masterTable and cascading is setup, the user records should be removed from the supporting table.
Now, back to reference back to the link I posted, I took their code and added additional tables to test the multiple cascading delete using their code plus a little extra that I added (the additional supporting tables).
Question While I did confirm that my sql statements below do work and achieve the result I'm after, is what I did below valid or is there better/more efficient way of doing multiple cascade deletes?
CREATE TABLE buildings (
building_no INT PRIMARY KEY AUTO_INCREMENT,
building_name VARCHAR(255) NOT NULL,
address VARCHAR(255) NOT NULL
);
CREATE TABLE rooms (
room_no INT PRIMARY KEY AUTO_INCREMENT,
room_name VARCHAR(255) NOT NULL,
building_no INT NOT NULL,
FOREIGN KEY (building_no)
REFERENCES buildings (building_no)
ON DELETE CASCADE
);
CREATE TABLE rooms1 (
room_no INT PRIMARY KEY AUTO_INCREMENT,
room_name VARCHAR(255) NOT NULL,
building_no INT NOT NULL,
FOREIGN KEY (building_no)
REFERENCES buildings (building_no)
ON DELETE CASCADE
);
CREATE TABLE rooms2 (
room_no INT PRIMARY KEY AUTO_INCREMENT,
room_name VARCHAR(255) NOT NULL,
building_no INT NOT NULL,
FOREIGN KEY (building_no)
REFERENCES buildings (building_no)
ON DELETE CASCADE
);
INSERT INTO buildings(building_name,address)
VALUES('ACME Headquaters','3950 North 1st Street CA 95134'),
('ACME Sales','5000 North 1st Street CA 95134');
SELECT * FROM buildings;
INSERT INTO rooms(room_name,building_no)
VALUES('Amazon',1),
('War Room',1),
('Office of CEO',1),
('Marketing',2),
('Showroom',2);
INSERT INTO rooms1(room_name,building_no)
VALUES('Bedroom',1),
('Kitchen',1),
('Game Room',1),
('Lounge',2),
('Gym',2);
INSERT INTO rooms2(room_name,building_no)
VALUES('Purple',1),
('Blue',1),
('Red',1),
('Yellow',2),
('Green',2);
SELECT * FROM rooms;
SELECT * FROM rooms1;
SELECT * FROM rooms2;
DELETE FROM buildings
WHERE
building_no = 2;
So I don't understand why I cannot insert data in my table that have foreign constraint keys or even modify anything in it.
Here is an example of the tables that are created. I am trying to insert data in the addresses table:
///////////////////////ADDRESSES TABLE ////////////////////////
CREATE TABLE IF NOT EXISTS addresses (
id INT NOT NULL AUTO_INCREMENT,
addressline1 VARCHAR(255) NOT NULL,
addressline2 VARCHAR(255) NOT NULL,
postcode VARCHAR(255) NOT NULL,
phonenumber INT(13) NOT NULL,
country_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (country_id) REFERENCES countries(id)
ON UPDATE CASCADE
ON DELETE RESTRICT
) ENGINE=InnoDB ";
///////////////////////COUNTRIES TABLE ////////////////////////
CREATE TABLE IF NOT EXISTS countries (
id INT NOT NULL AUTO_INCREMENT,
countryname VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
)
The issue here is that you are trying to insert into a referencing table (addresses) when the referenced entry (the country you reference) does not exist. That's what's triggering the FOREIGN KEY CONSTRAINT exception.
Try first inserting some countries into the countries table, then inserting some addresses where you reference those countries you entered in the first step.
As for your second question, that's a choice for you to make. I would probably choose to have the User have an Address (address field in the User table), but some of that depends on how the data is being used/updated.
Have a quick look through this resource if you're new to relational database design. It covers (in brief) topics like relationship types, key constraints, and normal forms.
I created two tables in mysql,
customer
house table with houseID being foreign key in my customer table.
Create customer table(
id int not null primary key auto_increment,
name varchar not null,
houseId int not null,
telephoneNo, int not null,
CONSTRAINT FOREIGN KEY (houseId) REFERENCES house(id) ON DELETE CASCADE);
CREATE house table(id int not null primary key auto_increment,
houseNo int not null,
address varchar not null);
However, when I delete customer with a specific houseId, the row in house table doesn't get deleted though I put on delete cascade in the customer table. Any idea why?
Your foreign key is on the wrong table. The way you got it set up is that if you delete a house, the corresponding cutomer will be cascaded.
You will want to put a customerId foreign key in the house table and have ON DELETE CASCADE foreign key trigger from side.
With a foreign key the ON DELETE is asking: what if the foreign key I am referencing is deleted (cascade, set null, do nothing)? not what to do when this row get's deleted.
In database design, can 2 entities have 2 relationships among themselves? i.e for example there are 2 entities donor and admin.. there are 2 relationships
1. admin accesses donor details
2. admin can contact donor and vice versa
can we join them with 2 relationships?
Definitely, although how much sense it makes to model "accesses" and "contacts" relations in a database depends on your application. I'll stay with your example though and assume these relations are n to n. Here is how the SQL could look like (warning, syntax not tested):
CREATE TABLE admin (
id int unsigned AUTO_INCREMENT PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE donor (
id int unsigned AUTO_INCREMENT PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE admin_donor_access_details (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE admin_donor_contact (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
The two relations could also be expressed in a single join table with boolean flags, like this:
CREATE TABLE admin_donor (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
detail_access tinyint(1) NOT NULL,
contact tinyint(1) NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
This will put some extra effort on your code because you need to determine whether to insert or update a row when adding a relationship, and whether to delete or update a row when removing a relationship, but in my opinion this is still a usable alternative.