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).
Related
I cannot seem to be able to delete primary keys in a table.
All references (FKs) have been removed but it still doesn't let me delete it.
What I'm trying to do is: delete old primary keys to add a new one - but keep the old columns and data (just remove the PK attribute).
What is wrong ?
Table:
CREATE TABLE `employee` (
`User` int(10) unsigned NOT NULL,
`Company` int(10) unsigned NOT NULL,
--unrelated boolean fields
PRIMARY KEY (`User`,`Company`),
KEY `FK_Employee_Company_idx` (`Company`),
CONSTRAINT `FK_Employee_Company` FOREIGN KEY (`Company`) REFERENCES `company` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_Employee_User` FOREIGN KEY (`User`) REFERENCES `user` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Trying to delete:
alter table Employee
drop primary key;
Issue:
Error 1025: Error on rename of '.\DB_NAME#sql-3640_4' to '.\DB_NAME\employee' (errno: 150 "Foreign key constraint is incorrectly formed") SQL Statement: ALTER TABLE DB_NAME.employee DROP PRIMARY KEY
Nothing references this table anymore. I also checked via statements which select from information_schema.key_column_usage but yields no results.
Wasted the last hours on Google but can't seem to figure it out.
And if that would work, adding a new column:
alter table Employee
add column ID int unsigned not null auto_increment primary key;
The index is still needed for the existing FK constraints.
Adding the following index (first) should satisfy that requirement:
CREATE INDEX xxx ON employee (User, Company);
Test case
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'm trying to insert (from postgres via grails) about 10 millions records into a table with a primary key and 2 foreign keys. If I keep the all primary and foreign keys and the indexes automatically generated along with these keys, it'll take about 7.5 hours to complete. If I drop all the keys and indexes before the inserts, it'll take only 10 minutes to executes all the inserts. But when I used ALTER TABLE to add the keys back in, it took forever (more than 7 hours) to perform. Is there a way to improve the performance?
The concept table that this table linked to has about 1 million records.
Here's the CREATE TABLE statement:
CREATE TABLE `concept_relationship` (
`concept_id_1` int(11) NOT NULL,
`concept_id_2` int(11) NOT NULL,
`relationship_id` int(11) NOT NULL,
`valid_start_date` date NOT NULL,
`valid_end_date` date NOT NULL DEFAULT '2099-12-31',
`invalid_reason` char(1) DEFAULT NULL,
PRIMARY KEY (`concept_id_1`,`concept_id_2`,`relationship_id`),
KEY `concept_id_1` (`concept_id_1`),
KEY `concept_id_2` (`concept_id_2`),
KEY `relationship_id` (`relationship_id`),
CONSTRAINT `FK_CONCEPT_REL_child` FOREIGN KEY (`concept_id_2`) REFERENCES `concept` (`concept_id`),
CONSTRAINT `FK_CONCEPT_REL_Parent` FOREIGN KEY (`concept_id_1`) REFERENCES `concept` (`concept_id`),
CONSTRAINT `FK_CONCEPT_REL_REL_TYPE` FOREIGN KEY (`relationship_id`) REFERENCES `relationship` (`relationship_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Thanks for your help
First, the index concept_id_1 is not needed. The primary key covers this index entirely.
My suggestion is to create the table without the keys or foreign references, except for the primary key. When you insert into the table, be sure that the input data is sorted by the keys of the primary key. Then add back the other keys with explicit index creation:
create index concept_relationship_idx1 on concept_relationship(concept_id_1);
And so on.
If this doesn't work efficiently, then reconsider the primary key. The data is actually ordered by the primary key, which can be computationally intensive for inserts. Add an auto-incremented primary key. Insert the data. Then create a unique index for what is now the primary key, and indexes for the other keys.
I only get an error 150 when I run this. The other tables are InnoDB, the types are primary key, and the data types match. So I can't see what I'm doing wrong, (and I see no obvious syntax errors). Any ideas?
CREATE TABLE grouptosite (
groups_id BIGINT(20),
usertogroup_groupID BIGINT(20),
usertogroup_userID BIGINT(20),
usertosite_id INT(10),
gts_id INT(10) AUTO_INCREMENT NOT NULL,
PRIMARY KEY (gts_id),
index (gts_id),
index (groups_id),
index (usertogroup_groupID),
index (usertogroup_userID),
index (usertosite_id),
FOREIGN KEY (groups_id)
REFERENCES groups(id)
ON UPDATE CASCADE ON DELETE RESTRICT,
FOREIGN KEY (usertogroup_groupID, usertogroup_userID)
REFERENCES usertogroup(groupID, userID)
ON UPDATE CASCADE ON DELETE RESTRICT,
FOREIGN KEY (usertosite_id)
REFERENCES usertosite(id)
ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=INNODB;
Are you sure your columns are the same type? I first created the usertosite table with the id of BIGINT (following the same pattern of the other tables), but the grouptosite.usertosite_id column was INT: http://sqlfiddle.com/#!2/d821a.
As mentioned in another comment, datatypes for foreign keys need to be the same.