How can I delete rows from table connected with foreign key? - mysql

I have table Songs with columns(ID_Song, Title, ID_Artist, ID_Image, ID_Audio) where ID_Artist, ID_Image, ID_Audio are foreign key.
Table Artists with columns (ID_Artist, Name)
Table Images with columns (ID_Image,filename, extension, size)
Table Audios with columns (ID_Audio,filename, extension, size)
Columns songs.ID_Artist references Artists.ID_Artist, songs.ID_Image references Images.ID_Image and songs.ID_Audio references Audios.ID_Audio, so when I delete a row from table Songs I want the data connected with that row from other tables be deleted as well, but I can't because of the foreign key, is there a way to do it?
Table Songs
CREATE TABLE `songs` (
`ID_Song` int(11) NOT NULL AUTO_INCREMENT,
`SongTitle` varchar(100) NOT NULL,
`ID_Artist` int(11) NOT NULL,
`ID_Img` int(11) NOT NULL,
`ID_SongFile` int(11) NOT NULL,
`Approved` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`ID_Song`),
KEY `ID_Artist` (`ID_Artist`),
KEY `ID_SongFile` (`ID_SongFile`),
KEY `ID_Img` (`ID_Img`),
CONSTRAINT `songs_ibfk_1` FOREIGN KEY (`ID_Artist`) REFERENCES `artists` (`ID_Artist`) ON DELETE CASCADE,
CONSTRAINT `songs_ibfk_2` FOREIGN KEY (`ID_SongFile`) REFERENCES `files` (`ID_File`) ON DELETE CASCADE,
CONSTRAINT `songs_ibfk_3` FOREIGN KEY (`ID_Img`) REFERENCES `images` (`ID_Img`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Table Images
CREATE TABLE `images` (
`ID_Img` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`extension` varchar(10) NOT NULL,
`size` float NOT NULL,
PRIMARY KEY (`ID_Img`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

For the given schema, you need to fetch ID_SongFile and ID_Img before you delete a song. Assuming you want to delete the song with ID=123. In pure SQL it could be:
select ID_SongFile, ID_Img into #file_id, #img_id
from songs
where ID_Song = 123;
delete from songs where ID_Song = 123;
delete from images where ID_Img = #img_id;
delete from files where ID_File = #file_id;
Depending on your data logic, it might be better to "reverse" the relation. If files and images are only related to songs, and it's always a one-to-one relation, I would remove the ID_SongFile and ID_Img columns from the songs table and have a ID_Song column as foreign key in images and files tables. If you define those new FKs with ON DELETE CASCADE, you would just need to delete the song, and the related file and image will "disappear".
You could also just store all image and file information in the songs table. But IMHO having the columns image_name, image_extension, image_size, file_name, file_extension and file_size doesn't look very good. But there is nothing really wrong with that design.

Related

How to migrate IDs from JOIN table into foreign key column in MySQL

I have the following tables in my MySQL database:
CREATE TABLE `User` (
`id` char(25) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email_UNIQUE` (`email`(191))
);
CREATE TABLE `Post` (
`id` char(25) NOT NULL,
`authorId` char(25) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `authorId` (`authorId`),
CONSTRAINT `Post_ibfk_1` FOREIGN KEY (`authorId`) REFERENCES `User` (`id`)
);
CREATE TABLE `_PostToUser` (
`A` char(25) CHARACTER SET utf8 NOT NULL,
`B` char(25) CHARACTER SET utf8 NOT NULL,
UNIQUE KEY `PostToUser_AB_unique` (`A`,`B`),
KEY `B` (`B`),
CONSTRAINT `_PostToUser_ibfk_1` FOREIGN KEY (`A`) REFERENCES `Post` (`id`) ON DELETE CASCADE,
CONSTRAINT `_PostToUser_ibfk_2` FOREIGN KEY (`B`) REFERENCES `User` (`id`) ON DELETE CASCADE
);
The relationship between User and Post right now is managed via the _PostToUser JOIN table.
However, I want to get rid of this extra JOIN table and simply have a foreign key reference from Post to User, so I ran this query to create the foreign key:
ALTER TABLE `Post` ADD COLUMN `authorId` char(25);
ALTER TABLE `Post` ADD FOREIGN KEY (`authorId`) REFERENCES `User` (`id`);
Now, I'm wondering what SQL query I need to run in order to migrate the data from the JOIN table to the new authorId column? If I understand correctly, I need a query that reads all the rows from the _PostToUser relation table and for each row:
Finds the respective Post record by looking up the value from column A
Inserts the value from column B as the value for authorId into that Post record
Note that I am aware that this changes the relationship from m-n and restricts it to 1-n: One post can at most have one author. One author/user can write many posts.
I'm basically looking for the equivalent of this PostgreSQL statement:
UPDATE "Post" post
SET "authorId" = post_to_user."B"
FROM "_PostToUser" post_to_user
WHERE post_to_user."A" = post."id";
Ensure that _PostToUser.A values are unique (no duplicated values). If exists - edit your data (remove excess records, for example).
Execute
UPDATE Post, _PostToUser
SET Post.authorId = _PostToUser.B
WHERE Post.id = _PostToUser.A
Ensure that there is no NULLs in Post.authorId

Records are not getting deleted from the related tables- ON DELETE CASCADE does not work

Here are my three tables
CREATE TABLE IF NOT EXISTS Song (
song_id INT(10) NOT NULL,
song_name VARCHAR(255) NOT NULL,
lyrics TEXT,
genre_id INT(10) NOT NULL,
year_released INT(10),
FOREIGN KEY gfk_id(genre_id)
REFERENCES Genre(genre_id) ON DELETE CASCADE
ON UPDATE CASCADE,
PRIMARY KEY (song_id)
);
CREATE TABLE IF NOT EXISTS Artist (
artist_id VARCHAR(25) NOT NULL,
artist_fname VARCHAR(10),
artist_lname VARCHAR(10),
PRIMARY KEY (artist_id)
);
create table Song_Artist
(
song_id int(10) NOT NULL,
artist_id varchar(25) NOT NULL,
FOREIGN KEY fk_song(song_id)
REFERENCES Song(song_id) ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY fk_artist(artist_id)
REFERENCES Artist(artist_id) ON DELETE CASCADE
ON UPDATE CASCADE
);
Song and Artist tables have many to many relationship and I have used cross reference approach to join those two tables using a separate table called Song_Artist
My doubt is, If I delete a record from the song table then does it mean the corresponding record pertaining to deleted song(song_id) should also be removed from the Song_Artist table ?? Or should I set the value of song_id after deleting to Null in the Song_Artist Table.
I tried using this trigger
DELIMITER $$
CREATE TRIGGER song_id_delete BEFORE DELETE ON Song
FOR EACH ROW
BEGIN
DELETE FROM Song_Artist
WHERE song_id = OLD.song_id;
END
DELIMITER ;
MySql workbench does not show that this trigger is created. And there are no errors shown.
Seems to work for me; I set default for genre_ID to 0 and removed FK relationship since I didn't have a genre table. But you can see the deletes and updates work. artist 1 and song 1 are updated to 10 and artist 5 and song 4 deleted.
I personally think Song_artist should get a composite primary key; but your call.
Is the issue you don't have a genre table created before the song table is created? thus it can't create the FK reference? I doubted it as you said the deletes were not working; not that you encountered a table create error...
demo on 5.7.12 Not sure how to debug the error when I can't recreate it.
CREATE TABLE IF NOT EXISTS Song (
song_id INT(10) NOT NULL,
song_name VARCHAR(255) NOT NULL,
lyrics TEXT,
genre_id INT(10) NOT NULL default 0,
year_released INT(10),
PRIMARY KEY (song_id)
);
CREATE TABLE IF NOT EXISTS Artist (
artist_id VARCHAR(25) NOT NULL,
artist_fname VARCHAR(10),
artist_lname VARCHAR(10),
PRIMARY KEY (artist_id)
);
create table Song_Artist
(
song_id int(10) NOT NULL,
artist_id varchar(25) NOT NULL,
#PRIMARY KEY PK_song_artist (song_ID, artist_id),
FOREIGN KEY FK_Song(song_id)
REFERENCES Song(song_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY FK_Artist(artist_id)
REFERENCES Artist(artist_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);

MySql - Product Variants Table (Wide Table) - Unique with NULLs

I have a products table, and a product_variants table (one-to-many).
The product_variants table has the following structure:
CREATE TABLE product_variants (
id int(11) NOT NULL AUTO_INCREMENT,
id_product int(11) NOT NULL,
id_colourSet int(11) DEFAULT NULL,
id_size int(11) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY UNIQUE (id_product,id_colourSet,id_size),
KEY idx_prod (id_product),
KEY idx_colourSet (id_colourSet),
KEY idx_size (id_size),
CONSTRAINT fk_df_product_variants_id_colurSet FOREIGN KEY (id_colourSet) REFERENCES df_colour_sets (id_colourSet) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_product FOREIGN KEY (id_product) REFERENCES df_products (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_size FOREIGN KEY (id_size) REFERENCES df_sizes (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB
The options are known at compile-time. Each option is foreign-keyed to a dedicated table, and the unique key is the combination of all options.
I then insert products with an "ON DUPLICATE KEY UPDATE ..." statement, and if a variant already exists the query will use an existing variant.
The problem is that certain products do not have a color, nor a size. In this case the unique constraint fails and I insert lots of almost-empty rows in the product_variants table.
In order to solve this problem I am creating a "NULL" value for each option (e.g. "NO_COLOR", "NO_SIZE") in the respective option tables, and using that as the default value for the option columns in the product_variants table.
Would this be the recommended solution? Is there a better way of structuring this data? I would really like to avoid an EAV design.
Thank you
Designating a magic value that means "missing value" is not the right solution in almost every case. That's what NULL is for.
It's also not clear how "NO_COLOR" is used for an integer. I guess it would map to the value 0, which is typically not used in an auto-increment column.
You can create another column to be a hash of the three unique key columns, defaulted to '' to avoid null problems. Then put a unique constraint on that hash.
CREATE TABLE product_variants (
id int(11) NOT NULL AUTO_INCREMENT,
id_product int(11) NOT NULL,
id_colourSet int(11) DEFAULT NULL,
id_size int(11) DEFAULT NULL,
option_hash binary(16) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (option_hash),
KEY idx_prod (id_product),
KEY idx_colourSet (id_colourSet),
KEY idx_size (id_size),
CONSTRAINT fk_df_product_variants_id_colurSet FOREIGN KEY (id_colourSet) REFERENCES df_colour_sets (id_colourSet) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_product FOREIGN KEY (id_product) REFERENCES df_products (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_size FOREIGN KEY (id_size) REFERENCES df_sizes (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
CREATE TRIGGER product_variants_ins BEFORE INSERT ON product_variants
FOR EACH ROW SET option_hash = UNHEX(MD5(CONCAT_WS('|',
COALESCE(id_product, ''),
COALESCE(id_colourSet, ''),
COALESCE(id_size, ''))));
CREATE TRIGGER product_variants_upd BEFORE UPDATE ON product_variants
FOR EACH ROW SET option_hash = UNHEX(MD5(CONCAT_WS('|',
COALESCE(id_product, ''),
COALESCE(id_colourSet, ''),
COALESCE(id_size, ''))));

how to write a trigger that forces a foreign key

I have a table name Service(product, loca, from_agent, to_agent).
product reference Product(pno)
from_agent references Customer(cno) U Driver(drno) U Airline(Ano)
to_agent references Customer(cno) U Driver(drno) U Airline(ano)
cno = Customer Number which is another table name "Customer"which has other details such as name, address etc
drno = Driver Number which is another table name "Driver" which has other details such as name, address etc
ano = Airline Number which is another table name "Airline"which has other details such as depport, depttime,arrtime etc..
would like to write a trigger that will force, the foreign key in the product table to be checked before any changes are made. Assuming local mapping transparency
Please help, I'm just learning about triggers and distribution database
Let's imagine a database for a shop: Items, Products, Brands (For exemple: "Yellow Peugeot bicycle with stripes", referenced as an item of "Peugeot bicycle", as a brand of "Peugeot".
CREATE TABLE IF NOT EXISTS `brands` (
`brandId` int(11) NOT NULL AUTO_INCREMENT,
`brandName` varchar(30) NOT NULL,
PRIMARY KEY (`brandId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `items` (
`itemId` int(11) NOT NULL AUTO_INCREMENT,
`generalProductId` int(11) NOT NULL,
PRIMARY KEY (`itemId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `products` (
`productId` int(11) NOT NULL AUTO_INCREMENT,
`productName` varchar(200) NOT NULL,
`productBrand` int(11) NOT NULL,
PRIMARY KEY (`productId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
ALTER TABLE products ADD FOREIGN KEY(productBrand) REFERENCES brands(brandId) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE items ADD FOREIGN KEY(itemBrand) REFERENCES product(productBrand) ON DELETE RESTRICT ON UPDATE CASCADE;
So here, you have a foreign key constraint, which makes Items inherit the brand specified in Product, which inherits itself from Brands. In this case, if you modify Name for a special BrandId, child tables will have the akncowledge of that. ON DELETE RESTRICT means that you cannot break the chain and database will not allow you to delete an entry that is referenced elsewhere :)

mysql innodb basic design for joomla component

I have four entities:
person
center
activity
address
So my idea is:
A person may have an associated address.
A center may have an associated address.
An activity may have an associated address.
Is this mysql design correct for a joomla component?
CREATE TABLE `#__person` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__center` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__activity` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__address` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`line1` varchar(30),
`line2` varchar(30),
`locality` varchar(10),
`region` varchar(10),
`country` varchar(10),
`postcode` varchar(10),
`person_id` int(10),
`center_id` int(10),
`activity_id` int(10),
PRIMARY KEY (id),
FOREIGN KEY `person_id` REFERENCES `#__person` (id) ON DELETE CASCADE,
FOREIGN KEY `center_id` REFERENCES `#__center` (id) ON DELETE CASCADE,
FOREIGN KEY `activity_id` REFERENCES `#__activity` (id) ON DELETE CASCADE,
other fields...
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
So i.e:
If I delete a person, automatically his/her corresponding address will be deleted?
And if I delete an address, what will happen to their references? (hope nothing)
What happens if the same address is an address for a center and for an activity?
Are the foreign keys in the correct table or should i put an address_id field in person,center and activity?
I'm a bit confused about my design.
Thank you for your suggestions.
Instead of placing an id for person, center and activity into the address table, I would suggest placing an address_id into each of the other tables (person, center, and activity). Then, you could reuse the same address across the other tables and in different combinations of person, center, and activity.
And about cascade, I think the MySQL Reference manual explains it well:
CASCADE: Delete or update the row from the parent table, and
automatically delete or update the matching rows in the child table.
Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between
two tables, do not define several ON UPDATE CASCADE clauses that act
on the same column in the parent table or in the child table.
So if you did change address_id to be in each of the other tables and then you applied the foreign key constraint
FOREIGN KEY `address_id` REFERENCES `#__address` (id) ON DELETE CASCADE
then when you deleted the row in the address table (the one WITHOUT the foreign key constraint), the matching rows in either person, center, or activity (where the foreign keys are defined), would be deleted. BUT, I am guessing that this might not be what you want, because you said a person, center and activity "may" have an address. So if you deleted the address, you do not necessarily want to delete the person, center or activity that was using that address. If that is true, then I would change your foreign key constraint in each of the 3 tables to:
FOREIGN KEY `address_id` REFERENCES `#__address` (id) ON DELETE SET NULL