MySQL - indexing innodb foreign keys - mysql

Is there an improvement in performance in indexing foreign keys in InnoDB? As far as I have read, InnoDB automatically creates an index for the foreign key.
Here is the query given to me for creating the table.
DROP TABLE IF EXISTS `assignments`;
CREATE TABLE `assignments`
(
`id` INTEGER NOT NULL AUTO_INCREMENT,
`user` INTEGER NOT NULL,
`job` INTEGER NOT NULL,
`created_at` DATETIME,
`updated_at` DATETIME,
PRIMARY KEY (`id`),
INDEX `job_fk1` (`user`),
INDEX `job_fk2` (`job`),
CONSTRAINT `job_fk1`
FOREIGN KEY (`user`)
REFERENCES `users` (`id`),
CONSTRAINT `job_fk2`
FOREIGN KEY (`job`)
REFERENCES `jobs` (`id`)
) ENGINE=InnoDB;
In there, he created foreign keys named job_fk1 and job_fk2. He used the names of these foreign keys as the name of the index.

Is there an improvement in performance in indexing foreign keys in InnoDB?
Answer: No. Performance will be degraded due to duplicate keys.
You do not need
INDEX `job_fk1` (`user`),
INDEX `job_fk2` (`job`),
Those will be automatically created by InnoDB internally. But you need to have index on users (id) and jobs (id) for faster operations on assignments table
http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
"InnoDB 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. (This is in contrast to some older versions, in which indexes had to be created explicitly or the creation of foreign key constraints would fail.) index_name, if given, is used as described previously."

You are correct that MySQL will create an index on a column, if it doesn't already exist, when creating a foreign key constraint. However, feel free to create an index on the column and remove the auto-generated one if you want.
You also might want additional multi-column indexes to aid queries like this make-believe one:
SELECT id, user, job
FROM assignments
WHERE job = 5
ORDER BY user
The multi-column index (job, user) would satisfy both the search and the sort, and since secondary indexes include the primary key, it would also act as a covering index in this case.

Related

MySQL table foreign key indexes on primary index columns

I've created a table for accounts/users with a primary key (UsersID, AccountsID) like below. Should I add the index for the Users table?
create table AccountsUsers
(
AccountsID int unsigned not null,
UsersID int unsigned not null,
Roles bigint unsigned null,
primary key (UsersID, AccountsID),
constraint AccountsUsers_Accounts_ID_fk
foreign key (AccountsID) references Accounts (ID)
on update cascade on delete cascade,
constraint AccountsUsers_Users_ID_fk
foreign key (UsersID) references Users (ID)
on update cascade on delete cascade
)
engine=InnoDB
;
create index AccountsUsers_Accounts_ID_fk
on AccountsUsers (AccountsID)
;
MySQL will create the necessary indexes for the foreign key automatically, if necessary.
In the case of your foreign key on UsersId, it can use the left column of your primary key. It doesn't need to create a new index for that foreign key.
In the case of your foreign key on AccountsId, MySQL will create a new index automatically. It can't use the fact that AccountsId is part of your primary key, because it isn't the left-most column.
After you do the CREATE TABLE, run SHOW CREATE TABLE AccountsUsers and you should see the new index it created for AccountsId.
From the documentation
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. This index might be silently dropped later, if you create
another index that can be used to enforce the foreign key constraint.
index_name, if given, is used as described previously.
In other words, if you don't already have the required indexes on the columns of your referencing table (AccountsUsers), MySQL will create them for you.
If the columns in the referenced tables (Accounts and Users) are not indexed you will get an error. Your's look like they will be Primary Keys on their respective tables, so you should be fine.

MySQL INSERT INTO tables with foreign key speed is very slow

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.

Failure in using alter table to add partition

I have a table having structure as below:
CREATE TABLE `child_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` int,
`ref_id` int,
PRIMARY KEY (`id`),
KEY `ref_id` (`ref_id`),
CONSTRAINT `FK4E9BF08E940F8C98` FOREIGN KEY (`ref_id`) REFERENCES `parent_table` (`id`) ON DELETE CASCADE
)
When running statement to add partition, it fails and show the error:
ERROR 1217: Cannot delete or update a parent row: a foreign key constraint fails
SQL Statement:
ALTER TABLE `learning`.`child_table` PARTITION BY HASH(ref_id) PARTITIONS 10
So I remove the foreign constraint with parent_table, then run again. It still fails and show the same error.
Did I do anything wrong?
I know this is an old question, but for people that fall here from looking for this problem, since its the first Google result:
MySQL does not support foreign keys on partitioned tables.
From the manual
Foreign keys not supported for partitioned InnoDB tables. Partitioned tables using the InnoDB storage engine do not support foreign keys. More specifically, this means that the following two statements are true:
No definition of an InnoDB table employing user-defined partitioning may contain foreign key references; no InnoDB table whose definition contains foreign key references may be partitioned.
No InnoDB table definition may contain a foreign key reference to a user-partitioned table; no InnoDB table with user-defined partitioning may contain columns referenced by foreign keys.
The error is referring to a foreign key on another table that references child_table. You need to find and remove the foreign key from that table, not necessarily child_table. You could also try running SET foreign_key_checks = 0 first.

mysql and indexes with more than one column

How to use indexes with more than one column
The original index has an index on block_id, but is it necesarry when it's already in the unique index with two column?
Indexes with more than one column
(a,b,c)
you can search for a, b and c
you can search for a and b
you can search for a
you can not search for a and c
Does this apply to unique indexes too?
table
id
block_id
account_id
name
indexes origin
PRIMARY KEY (`id`)
UNIQUE KEY `block_id` (`block_id`,`account_id`)
KEY `block_id` (`block_id`),
KEY `account_id` (`account_id`),
indexes alternative
PRIMARY KEY (`id`)
UNIQUE KEY `block_id` (`block_id`,`account_id`)
KEY `account_id` (`account_id`),
The rules you describe above have to my knowledge always held whether an index is unique or not. You might run explain on the query you have in mind and observe when the index is used and when it is not used under various circumstances.

Creating foreign keys on already indexed columns with MySQL Workbench

I am creating a database model with Workbench and create the following table:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) )
ENGINE = InnoDB
It has a primary key, and a unique key on my second column.
When I create foreign key constraints on them, Workbench automatically adds two indexes:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) ,
INDEX `FKOne` (`idtable1` ASC) , //here
INDEX `FKTwo` (`uniquecolumn` ASC) , //(I don't want this!)
CONSTRAINT `FKOne`
FOREIGN KEY (`idtable1` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `FKTwo`
FOREIGN KEY (`uniquecolumn` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
(The above is the forward-engineered script after adding the foreign keys to my model)
I have four indexes now.
This is what the MySQL Reference Manual says:
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.
So I understand there is no need to create indexes FKOne and FKTwo, since there are already a Primary Key and a Unique index, on the same columns, in the same order. Yet MySQL Workbench doesn't allow me to delete indexes FKOne and FKTwo. And I think I should be able to do this:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) ,
CONSTRAINT `FKOne`
FOREIGN KEY (`idtable1` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `FKTwo`
FOREIGN KEY (`uniquecolumn` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
Am I right? Would this code work? Is there some way to do it with Workbench? (Apart from deleting those two lines at the last moment before forward-engineering).
Or maybe MySQL is smart enough to refrain from creating totally redundant indexes and I don't have to worry about it...?
(I'm assuming this is when defining a model.)
See Bug 53277, where I mention the following obscure workaround:
You start with a foreign key and its corresponding generated index that you want to get rid of. Make sure the key is (at least temporarily) on a single non-unique column. In the Indexes tab, change the Type to UNIQUE. Then go to the Columns tab, where UQ is now checked, and uncheck it. The unwanted index is eliminated!