I'm trying to make relationship between CAR and ITEM tables.
Since a car can have the same component as other cars, and an item can be used in many cars. I decide to use many-to-many relationship for this issue.
This issue force me to remove PK from both car_idcar and item_list_iditem_list on the middle table.
But...How I can prevent entry of duplicate pair(red box below)from being inserted
Additional info as #Barmar requested:
CREATE TABLE `car_item` (
`id_car_item` int(11) NOT NULL AUTO_INCREMENT,
`car_idcar` int(11) NOT NULL,
`item_list_iditem_list` int(11) NOT NULL,
PRIMARY KEY (`id_car_item`),
UNIQUE KEY `car_idcar_UNIQUE` (`car_idcar`),
UNIQUE KEY `item_list_iditem_list_UNIQUE` (`item_list_iditem_list`),
KEY `fk_car_has_item_list_item_list1_idx` (`item_list_iditem_list`),
KEY `fk_car_has_item_list_car1_idx` (`car_idcar`),
CONSTRAINT `fk_car_has_item_list_car1` FOREIGN KEY (`car_idcar`) REFERENCES `car` (`idcar`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_car_has_item_list_item_list1` FOREIGN KEY (`item_list_iditem_list`) REFERENCES `item_list` (`iditem_list`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
Full schema creating script here: http://pastebin.com/1vPYxswd
You should not have unique keys on each column, you should have a unique key on the combination of columns. This allows each column to be duplicated by itself, but not the pair of them. So you can have multiple references to the same car, and multiple references to the same item_list, but not multiple links between the same car and item_list.
CREATE TABLE `car_item` (
`id_car_item` int(11) NOT NULL AUTO_INCREMENT,
`car_idcar` int(11) NOT NULL,
`item_list_iditem_list` int(11) NOT NULL,
PRIMARY KEY (`id_car_item`),
UNIQUE KEY `car_idcar_iditem_list_UNIQUE` (`car_idcar`, `item_list_iditem_list`),
CONSTRAINT `fk_car_has_item_list_car1` FOREIGN KEY (`car_idcar`) REFERENCES `car` (`idcar`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_car_has_item_list_item_list1` FOREIGN KEY (`item_list_iditem_list`) REFERENCES `item_list` (`iditem_list`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
See Creating UNIQUE constraint on multiple columns in MySQL Workbench EER diagram
Related
I am creating the table with this syntax:
CREATE TABLE movies_genres
(
id BIGINT AUTO_INCREMENT,
movie_id INT NOT NULL,
genre_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT `fk_movie_id` FOREIGN KEY (movie_id) REFERENCES movies(id),
CONSTRAINT `fk_genre_id` FOREIGN KEY (genre_id) REFERENCES genres(id),
CONSTRAINT unique_id_pair UNIQUE(movie_id, genre_id)
);
But then I look at the info about the table in MySQL Workbench I see:
CREATE TABLE `movies_genres` (
`id` bigint NOT NULL AUTO_INCREMENT,
`movie_id` int NOT NULL,
`genre_id` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_id_pair` (`movie_id`,`genre_id`),
KEY `fk_genre_id` (`genre_id`),
CONSTRAINT `fk_genre_id` FOREIGN KEY (`genre_id`) REFERENCES `genres` (`id`),
CONSTRAINT `fk_movie_id` FOREIGN KEY (`movie_id`) REFERENCES `movies` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Why this line of code has generated?
KEY `fk_genre_id` (`genre_id`)
Also I see that extra index was created that I didn't order...
Screenshot with extra index
https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html says:
MySQL requires indexes on foreign keys and referenced keys so that
foreign key checks can be fast and not require a table scan. In the
referencing table, there must be an index where the foreign key
columns are listed as the first columns in the same order. Such an
index is created on the referencing table automatically if it does not
exist.
(emphasis mine)
(Bill gave the answer; this is providing another tip.)
Get rid of id from the table and change to these two indexes:
PRIMARY KEY (`movie_id`,`genre_id`),
KEY `fk_genre_id` (`genre_id`),
That will make some of your uses of this many-to-many table run faster. It will also shrink the table size.
If need id
Since you need id for single-row deletion and updating, keep id, but use
PRIMARY KEY (id)
INDEX(`movie_id`, `genre_id`),
INDEX(`genre_id`, `movie_id`),
The PK will continue to make Delete/Update efficient; the other two indexes will make the many-to-many JOIN efficient.
THE SOLUTION IS BELOW
I have three tables like the following:
CREATE TABLE `t_arch_layer` (
`arch_layer_id` int(11) NOT NULL AUTO_INCREMENT,
`arch_layer_name` varchar(45) NOT NULL,
PRIMARY KEY (`arch_layer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
CREATE TABLE `t_tech` (
`tech_id` int(11) NOT NULL AUTO_INCREMENT,
`tech_name` varchar(45) DEFAULT NULL,
`tech_type_id` int(11) NOT NULL,
`tech_icon` text,
PRIMARY KEY (`tech_id`),
KEY `fk_t_tech_t_tech_type1_idx` (`tech_type_id`),
CONSTRAINT `fk_t_tech_t_tech_type1` FOREIGN KEY (`tech_type_id`) REFERENCES `t_tech_type` (`tech_type_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
CREATE TABLE `t_arch_layer_tech` (
`arch_layer_id` int(11) NOT NULL,
`tech_id` int(11) NOT NULL,
PRIMARY KEY (`tech_id`,`arch_layer_id`),
KEY `fk_t_layer_has_t_tech_t_tech1_idx` (`tech_id`),
KEY `fk_t_layer_has_t_tech_t_layer1_idx` (`arch_layer_id`),
CONSTRAINT `fk_t_layer_has_t_tech_t_layer1` FOREIGN KEY (`arch_layer_id`) REFERENCES `t_arch_layer` (`arch_layer_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_t_layer_has_t_tech_t_tech1` FOREIGN KEY (`tech_id`) REFERENCES `t_tech` (`tech_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Basically it's a tipical situation where one table use two foreign keys from another two different tables. This table stores the possible combinations between the layers and technologies so it can't store any combination of layer_id and tech_id which is not in both.
But there is a problem, I need to delete whenever I want some row from t_arch_layer_tech. This it's impossible due to the foreign keys, I know it.
My question is, is there something to use the foreign key as a reference to forbide insert values that there aren't into t_tech or t_arch_layer and also to be consider as "own fields" (I can't explain better) of the table in order to delete any row of the t_arch_layer_tech table? Delete t_tech and t_arch_layer tables to avoid the foreign keys and then set the limits into the t_arch_layer_tech is not a solution.
SOLUTION
When that error appears it's neccesary to check the DB relationships and read carefully the provided message. It seems useless but it helped me to understand what's happening with the t_arch_layer_tech FK. I was using them into another table BUT separately, not as a compound FK. This is the reason because I could insert some rows into t_arch_layer_tech and delete only specific pairs.
So, summarizing, if you are going to use FKs that exist together (as my pair "arch_layer_id, tech_id") create ONLY ONE FK which is a compound FK that uses the mentioned.
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, ''))));
I am trying to insert pseudo data into my db to get going, and in one particular table I have two columns which are FK's and PK's of the table; fk_product_manf_code and fk_content_id. To my understanding, these are considered composite keys in their current state.
So I add data to the table:
fk_product_manf_code fk_content_id
NOV-ABC123 1
I then want to associate another content_id to the same product_manf_code, so I perform the following:
INSERT INTO `mydb`.`package_contents`
(`fk_product_manf_code`, `fk_content_id`)
VALUES
('NOV-ABC123', 2);
However I'm greeted with the following error:
Error Code: 1062. Duplicate entry 'NOV-ABC123' for key 'fk_product_manf_code_UNIQUE'
I don't understand what's going, because I thought a composite key makes 2 columns unique? So why is it kicking up a fuss about just 1 column being unique?
Here is the table CREATE statement
CREATE TABLE `package_contents` (
`fk_product_manf_code` varchar(255) NOT NULL,
`fk_content_id` int(11) NOT NULL,
PRIMARY KEY (`fk_content_id`,`fk_product_manf_code`),
UNIQUE KEY `fk_content_id_UNIQUE` (`fk_content_id`),
UNIQUE KEY `fk_product_manf_code_UNIQUE` (`fk_product_manf_code`),
CONSTRAINT `content_id` FOREIGN KEY (`fk_content_id`) REFERENCES `contents` (`content_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `product_manf_code` FOREIGN KEY (`fk_product_manf_code`) REFERENCES `products` (`product_manf_code`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
So, you are learning why composite primary keys are a pain, especially for foreign key constraints. Not only are integer keys more efficient, but a single key is easier to work with.
I would suggest changing your table structure to be more like this:
CREATE TABLE package_contents (
package_contents_id int not null auto_increment primary key,
fk_product_manf_id int NOT NULL,
fk_content_id int(11) NOT NULL,
UNIQUE KEY (fk_content_id, fk_product_manf_id),
CONSTRAINT content_id FOREIGN KEY (fk_content_id)
REFERENCES contents(content_id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT product_manf_code FOREIGN KEY (fk_product_manf_id)
REFERENCES products(product_manf_id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Note that I changed the manufacturer code to an id as well. This should also reduce the size of the table, assuming that the "code" is longer than 4 bytes.
If you do this for all your tables, the database will be a bit more efficient, and you won't need superfluous unique constraints. The foreign key constraints should always be to primary keys (unless there is a very good reason for using a different unique key).
I have a table called foodlist, which contains foods:
CREATE TABLE `foodlist` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Example data:
id | name
1 | Apple
2 | Banana
3 | Cinnamon
Then I have a table of combination of foods you can eat together:
CREATE TABLE `foodlist-also_eat` (
`originalFood` int(10) NOT NULL,
`alsoFood` int(10) NOT NULL,
PRIMARY KEY (`originalFood`,`alsoFood`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
That means, that if you can eat apple with banana, the entry is:
originalFood | alsoFood
1 2
Now, what I want to do, is to add foreign keys in my foodlist-also_eat table.
I can add one for originalFood:
ALTER TABLE `foodlist-also_eat`
ADD FOREIGN KEY (`originalFood`) REFERENCES `foodlist` (`id`)
ON DELETE RESTRICT ON UPDATE RESTRICT;
Query OK.
However, I cannot continue adding one for alsoFood:
ALTER TABLE `foodlist-also_eat`
ADD FOREIGN KEY (`alsoFood`) REFERENCES `foodlist` (`id`)
ON DELETE RESTRICT ON UPDATE RESTRICT;
Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.
Table './dev#002dfood/foodlist#002dalso_eat' already exists
What am I doing wrong here?
As per documentation on Foreign key constraints
If the CONSTRAINT symbol clause is given, the symbol value must be unique in the database. If the clause is not given, InnoDB creates the name automatically.
Note that as of version 5.0.38, InnoDB allows two or more foreign keys on the column, they may reference diffrent tables/columns. It even allows foreign keys with the same definition, but different constraint name.
Define a unique name for each of the constraint being defined.
ALTER TABLE `foodlist-also_eat`
ADD constraint fk_of_flae
FOREIGN KEY (`originalFood`)
REFERENCES `foodlist` (`id`)
ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE `foodlist-also_eat`
ADD constraint fk_af_flae
FOREIGN KEY (`alsoFood`)
REFERENCES `foodlist` (`id`)
ON DELETE RESTRICT ON UPDATE RESTRICT;