MySQL: 1:n relationship with auto increasing PK - mysql

I've two tables: users and files. A user can create files (1:n relationship).
When a user is deleted, all the user files refered to the user should be deleted, too. Deleting single files of a user should also be possible.
Try 1
CREATE TABLE `users` (
`user_id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`password` VARCHAR(60) NOT NULL,
`salt` VARCHAR(33) NOT NULL,
`user_group_id` INT NOT NULL,
PRIMARY KEY (`user_id`),
CONSTRAINT `fk_u_user_group` FOREIGN KEY (`user_group_id`) REFERENCES `user_groups` (`user_group_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `user_file` (
`user_id` INT NOT NULL,
`user_file_id` INT NOT NULL,
`name` VARCHAR(50) NOT NULL,
`content` JSON DEFAULT NULL,
PRIMARY KEY ( `user_id`, `user_file_id`),
CONSTRAINT `fk_uf_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON UPDATE CASCADE ON DELETE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
The table user_files contains files of all users so the primary key have to be a composition of user_id and user_file_id.
This solution works for me but there is one problem: The user_file_id is not increased automatically (because it's not the primary key). But I do not want to know the user_file_id when inserting new files to the table.
INSERT INTO user_files VALUES(1, ???, 'New file', default)
The default keyword instead of ??? is not working here.
Try 2
Another solution I though of would be to create a new table user_user_files that merges the tables users and user_files.
CREATE TABLE `user_file` (
`user_file_id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`content` JSON DEFAULT NULL,
PRIMARY KEY (`user_file_id`),
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `user_user_files`(
`user_id` INT NOT NULL,
`user_file_id` INT NOT NULL,
PRIMARY KEY(`user_id`, `user_file_id`),
CONSTRAINT `fk_uuf_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `fk_uuf_user_file_id` FOREIGN KEY (`user_file_id`) REFERENCES `user_files` (`user_file_id`) ON UPDATE CASCADE ON DELETE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
With that I could create a trigger when inserting a new row into user_files and that trigger would add a new row to user_user_files.
DELIMITER $$
CREATE TRIGGER after_user_insert
AFTER INSERT
ON user_files FOR EACH ROW
BEGIN
INSERT INTO `user_user_files` VALUES(NEW.user_file_id,default, default);
END$$
DELIMITER ;
But is this possible when I want the following behavior?
Delete all user files when the user is deleted
Option to delete single user files from a user
Summary
So with try 1 there is the problem that the primary key is not increased automatically.
With try 2 I do not know if my desired behavior works with deleting / adding rows.

Related

How to cascade delete from same MySQL table

Is it possible to cascade delete from same MySQL table? I want to cascade delete in a table that references itself in relations.
Example:
I have a comments table that has comment_id and parent_id, where parent_id is the same id in comment_id.
Parent_id could be either another comment_id or null if it's a root
comment.
I want to be able to select any node in this tree and delete it. In the process, all child nodes and sub-child nodes should be deleted.
I have tried to alter my table using this query
ALTER TABLE `comment`
ADD FOREIGN KEY (`comment_id`)
REFERENCES `comment`(`parent_id`) ON DELETE CASCADE ON UPDATE RESTRICT;
but I end up with an error
Cannot add or update a child row: a foreign key constraint fails
(zendaya001.#sql-6c1_1044ab, CONSTRAINT comment_ibfk_3 FOREIGN
KEY (comment_id) REFERENCES comment (parent_id) ON DELETE
CASCADE ON UPDATE RESTRICT)
For reproduction, this is my comment table:
CREATE TABLE `comment` (
`comment_id` int NOT NULL,
`post_id` int UNSIGNED NOT NULL,
`user_id` int NOT NULL,
`parent_id` int DEFAULT NULL,
`content` text NOT NULL,
`type` varchar(50) DEFAULT NULL,
`count_replies` int NOT NULL DEFAULT '0',
`count_likes` int NOT NULL DEFAULT '0',
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for table `comment`
--
ALTER TABLE `comment`
ADD PRIMARY KEY (`comment_id`),
ADD KEY `user_id` (`user_id`),
ADD KEY `post_id` (`post_id`),
ADD KEY `parent_id` (`parent_id`);
You try to create the foreign key with incorrect direction.
Must be:
ALTER TABLE `comment`
ADD FOREIGN KEY (`parent_id`)
REFERENCES `comment`(`comment_id`) ON DELETE CASCADE ON UPDATE RESTRICT;
https://dbfiddle.uk/iJRlziwL
comment_id must be defined at least UNIQUE (maybe PRIMARY KEY).
I cannot insert a new record into the table even if ON UPDATE RESTRICT is also set to CASCADE – Freesoul
You do something wrongly.
https://dbfiddle.uk/rcUupB2b

How to link one to many relationship with multiple keys pointing on the same table id?

I have a table files and a table users and staff and client.
The file table contain a row made_by a row published_byand a row about_client pointing to a staff, a staff and a client respectively.
One user must be either a staff or a client.
One file can have multiple author (made_by row), but only one user can publish it (published_by), and it can only be about one client (about_client).
First, I put 3 foreign key on files:
CONSTRAINT `made_by_fk` FOREIGN KEY (`made_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `published_by_fk` FOREIGN KEY (`published_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `about_client_fk` FOREIGN KEY (`about_client`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
but after a little research, I found out that I should put the foreign key on staff / client, and here come the problem, the fk are OPTIONAL. A staff is not obligatory an author of a file, or the client maybe don't have any file attached to it.
I am a bit lost.
DROP TABLE IF EXISTS `files`;
CREATE TABLE IF NOT EXISTS `files` (
`id` INT NOT NULL AUTO_INCREMENT
PRIMARY KEY,
`made_by` INT NOT NULL,
`published_by` INT NOT NULL,
`about_client` INT NOT NULL,
`creation_date` DATETIME NOT NULL,
`modification_date` DATETIME NULL
ON UPDATE CURRENT_TIMESTAMP,
`path` VARCHAR(255) NOT NULL,
`title` VARCHAR(100) NOT NULL UNIQUE,
`category` INT NOT NULL,
`type` VARCHAR(30) NOT NULL,
`size` INT NOT NULL,
CONSTRAINT `made_by_fk` FOREIGN KEY (`made_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `published_by_fk` FOREIGN KEY (`published_by`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `about_client_fk` FOREIGN KEY (`about_client`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)
ENGINE = InnoDB
COLLATE = utf8_unicode_ci;
DROP TABLE IF EXISTS `staff`;
CREATE TABLE IF NOT EXISTS `staff` (
`id` INT NOT NULL AUTO_INCREMENT
PRIMARY KEY,
`job` INT NOT NULL,
`password` VARCHAR(255) NOT NULL
)
ENGINE = InnoDB
COLLATE = utf8_unicode_ci;
DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` (
`id` INT NOT NULL AUTO_INCREMENT
PRIMARY KEY,
`name` VARCHAR(100) NOT NULL,
`surname` VARCHAR(100) NOT NULL,
`email` VARCHAR(50) NOT NULL UNIQUE
)
ENGINE = InnoDB
COLLATE = utf8_unicode_ci;
DROP TABLE IF EXISTS `clients`;
CREATE TABLE IF NOT EXISTS `clients` (
`id` INT NOT NULL AUTO_INCREMENT
PRIMARY KEY,
`phone` VARCHAR(30) NOT NULL,
`age` INT NOT NULL,
`date_of_birth` DATE NOT NULL,
`security_number` VARCHAR(99) NOT NULL UNIQUE
)
ENGINE = InnoDB
COLLATE = utf8_unicode_ci;
If there can be multiple made_by, it can't be a column in the file table, since that can only contain one value.
You need another table, file_made_by that represents the many-to-many relationship between files and authors.
CREATE TABLE file_made_by (
file_id INT NOT NULL,
author_id INT NOT NULL,
PRIMARY KEY (file_id, author_id),
FOREIGN KEY (file_id) REFERENCES files (id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE
);
The clients and staff tables should also include a user_id column that's a foreign key to users.id.
If about_client has to be about a client, not staff, then it should be a foreign key to clients.id rather than users.id.
And if only staff are allowed to be authors, file_made_by.author_id should be a FK to staff.id rather than users.id. The same with files.published_by.

Creating foreign key between 2 tables

Ok, so it's like this. I have 2 tables in phpmyadmin. One is for personal details and the other is for login information. Both tables have AccountID, so I tried using foreign key constraints to connect the tables. After I did that it seems like I cannot update the table with new data. Before the constraint, updating the tables worked fine.
What I'm trying to do is store user login info and personal info in these table. Then whenever the user wants to delete their current account, the personal details and login details of are deleted from both tables or when they wanted to search for their login and personal info the search engine can search from both tables with AccountID.
so far.i have make 2 new tables.1 table which is personal information have 'AccountID'[A_I][PRIMARY] and 'loginID'.another table is login info.it has 'loginID'[A_i][PRIMARY]
i already make the 'loginID' at personal info and index but i cannot assign foreign key constraint for it bcause it did not detect 'loginID' in personal info.
Your AccountId should be a primary key in one table say Personal Info table.
This AccountId should be the foreign key in another table (Login) and make sure you set on Delete Cascade and on Update Cascade.
So in this structure, when personal info is deleted, its corresponding record in the login table will be automatically deleted.
DROP TABLE IF EXISTS `stacktest`.`personal_info`;
CREATE TABLE `stacktest`.`personal_info` (
`account_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`age` varchar(45) DEFAULT NULL,
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `stacktest`.`login_info`;
CREATE TABLE `stacktest`.`login_info` (
`loginId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`account_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`loginId`),
KEY `FK_login_info_1` (`account_id`),
CONSTRAINT `FK_login_info_1` FOREIGN KEY (`account_id`) REFERENCES `personal_info` (`account_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
Above is the way how the 2 tables should be created.
Then insert some values in both table,
Note that account_id value has to be same in both the tables.
After that you can fire a delete query like:
delete from personal_info where accound_id=2;
This will delete rows from parent table personal_info and also from child table login_info where account_id is 2
Keeping the account_id as NOT NULL in child table:
DROP TABLE IF EXISTS `stacktest`.`login_info`;
CREATE TABLE `stacktest`.`login_info` (
`loginId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`account_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`loginId`),
KEY `FK_login_info_1` (`account_id`),
CONSTRAINT `FK_login_info_1` FOREIGN KEY (`account_id`) REFERENCES `personal_info` (`account_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

On Cascade Delete does not seem to be applying in my DB

I don't know what I am doing wrong as I've been looking at previous answers on this site concerning ON CASCADE DELETE.
Basically this is my table:
CREATE TABLE `directorycolumntags` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`directorycolumn_id` INT(11) NOT NULL,
`tag_id` INT(11) NOT NULL,
`description` TEXT,
`created` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`directorycolumn_id`) REFERENCES directorycolumn(id),
CONSTRAINT FOREIGN KEY (`tag_id`) REFERENCES tag(id)
ON DELETE CASCADE
) ENGINE=MYISAM AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
The Foreign key references the id of the tag table:
CREATE TABLE `tag` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(200) DEFAULT NULL,
`description` TEXT,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
Now, If I perform this query to INSERT some data into the directorycolumntags table it works:
INSERT INTO directorycolumntags (directorycolumn_id, tag_id) VALUES (178,32);
However, when I DELETE the entry from the tag table with the id of 32 it does not remove the row from the directorycolumntags table. Can anyone point out where I am going wrong?
It's because your table directorycolumntags is MYISAM, not INNODB. MyISAM doesn't support foreign keys. You can write your foreign key statements, but MySQL silently ignores them.
Try this:
ALTER TABLE `directorycolumntags` ENGINE = 'InnoDB';

how to define foreign key constraints

I have three mysql tables. Tables are already created.
Requests - request_id, request_message, user_id
Responses - response_id, response_message, user_id
users - user_id, user_name
Now i want to define foreign key constraints on that, such that
1. If user_id is not present in Users table, and someone is inserting the data in Requests or Responses for that user_id -- then error
2. If request_id is not present in Requests table, then if someone is inserting in responses table for that request_id -- then error
3. If someone deletes an user_id, all associated requests and responses with that user_id should be deleted automatically.
4. If someone deletes an request_id, all the associated responses with it, should be deleted automatically.
If i am missing any thing please let me know.
How to achieve this functionality?
Thanks
Devesh
Here is full sql to create your tables:
CREATE TABLE IF NOT EXISTS `reponses` (
`response_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`response_message` varchar(45) DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`response_id`,`user_id`),
KEY `fk_reponses_users1` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `requests` (
`request_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`request_message` varchar(45) DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`request_id`,`user_id`),
KEY `fk_requests_users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=3 ;
ALTER TABLE `reponses`
ADD CONSTRAINT `reponses_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE NO ACTION;
ALTER TABLE `requests`
ADD CONSTRAINT `requests_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE NO ACTION;
Option that allows you to delete records related to user is ON DELETE CASCADE. By default MySql sets NO ACTION which refers to RESTRICT and doesn't allow parent record to be deleted while it has related objects. I think that you didn't mention the relation between responses and requests but you should get the idea ;).