I have a schema with tables like this (simplified):
-------- --------------- ------------
| key | | permission | | resource |
|------| |-------------| |----------|
| id | -----< | id | >----- | id |
| name | | key_id | | name |
-------- | resource_id | ------------
| action |
---------------
The permission table definition script is something like this:
CREATE TABLE `permission` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`key_id` INT(11) NOT NULL,
`resource_id` INT(11) NOT NULL,
`action` VARCHAR(32) NOT NULL,
PRIMARY_KEY (`id`),
CONSTRAINT `fk_permission_key` FOREIGN KEY (`key_id`) REFERENCES `key` (`id`),
CONSTRAINT `fk_permission_resource` FOREIGN KEY (`resource_id`) REFERENCES `resource` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
This all works fine. Then, I decided I needed a unique constraint on the permission table so that only a single record can exist for an action/key/resource combination, so I do this:
ALTER TABLE `permission` ADD UNIQUE KEY `uq_permission` (`key_id`, `resource_id`, `action`);
This also works fine. We are in an environment where we use migrations to manage schema changes, so I want to make sure that there's a "roll back" script. But when I issue this command:
ALTER TABLE `permission` DROP INDEX `uq_permission`;
I get this error:
1553 - Cannot drop index 'uq_permission': needed in a foreign key
constraint
After some fuddling around, I found that if I dropped the foreign key fk_permission_key, I am then able to drop the unique constraint.
Why is my unique constraint getting tangled up with a completely separate foreign key?
When you add the fk_permission_key constraint, MySQL automatically creates an index (this behaviour differs from other database engines, where you need to create such indexes explicitly):
mysql> SHOW INDEX FROM `permission`;
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| permission | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_key | 1 | key_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_resource | 1 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.00 sec)
When you create the uq_permission index, MySQL apparently drops the fk_permission_key index because it understands it can use your own index.
mysql> SHOW INDEX FROM `permission`;
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| permission | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 1 | key_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 2 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 3 | action | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_resource | 1 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
5 rows in set (0.01 sec)
However, if you now attempt to remove uq_permission MySQL complaints because the foreign key will no longer have a usable index (and this time it isn't smart enough to create the index automatically).
I don't know if automatic index creation is configurable but, in this case, the only solution I can think of is to provide the index yourself:
mysql> ALTER TABLE `permission` ADD INDEX `fk_permission_key` (`key_id`);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW INDEX FROM `permission`;
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| permission | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 1 | key_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 2 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 0 | uq_permission | 3 | action | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_resource | 1 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_key | 1 | key_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.01 sec)
mysql> ALTER TABLE `permission` DROP INDEX `uq_permission`;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW INDEX FROM `permission`;
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| permission | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_resource | 1 | resource_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| permission | 1 | fk_permission_key | 1 | key_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.00 sec)
Automatic index creation is documented:
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.
Related
I'm trying to drop some columns which I'm no longer using in my table. I've got a single column with a UNIQUE constraint. When I'm trying to drop the columns I'm getting a "Duplicate entry" found for this column.
When I search for rows with this code I'm only returned with a single result, but I figure that might be because it stops looking when it finds the first (as it thinks its unique).
I've tried deleting the row in question, but after trying to delete columns I'm returned with a new code that is "Duplicate entry".
Error when trying to delete columns:
ALTER TABLE attacktable DROP COLUMN fairfightparsed, DROP COLUMN defenderbattlestatssum, DROP COLUMN attackerbsstd, DROP COLUMN defenderdsstd, DROP COLUMN defenderlevel;
ERROR 1062 (23000): Duplicate entry 'e3cce98b6aa8085ed6a960d2afcd4dca' for key 'attacktable.attackcode'
Only one of the selected attackcode:
SELECT * FROM attacktable WHERE attackcode = "e3cce98b6aa8085ed6a960d2afcd4dca";
+----------------------------------+------------+ ...
| attackcode | attackerid | ...
+----------------------------------+------------+ ...
| e3cce98b6aa8085ed6a960d2afcd4dca | 2618403 | ...
+----------------------------------+------------+ ...
1 row in set (0,00 sec)
Description of uniqueness:
describe attacktable;
+------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+-------+
| attackcode | varchar(255) | YES | UNI | NULL | |
| attackerid | int | YES | MUL | NULL | |
....
Indexes on the table:
SHOW INDEX FROM attacktable;
+-------------+------------+------------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------------+------------+------------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| attacktable | 0 | attackcode | 1 | attackcode | A | 1022111 | NULL | NULL | YES | BTREE | | | YES | NULL |
| attacktable | 1 | attackerid | 1 | attackerid | A | 2281 | NULL | NULL | YES | BTREE | | | YES | NULL |
| attacktable | 1 | resmodchain | 1 | resmodchain | A | 92 | NULL | NULL | YES | BTREE | | | YES | NULL |
| attacktable | 1 | resmodfair | 1 | resmodfair | A | 202 | NULL | NULL | YES | BTREE | | | YES | NULL |
| attacktable | 1 | resmodwar | 1 | resmodwar | A | 1 | NULL | NULL | YES | BTREE | | | YES | NULL |
| attacktable | 1 | attackerbattlestatssum | 1 | attackerbattlestatssum | A | 76782 | NULL | NULL | YES | BTREE | | | YES | NULL |
+-------------+------------+------------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
I'm now worried there are a lot of these duplicates in my table.. Help please :)
EDIT: SO I suspect it's the error-code that is wrong, not that I have duplicates. That would be easier I guess.
DELETE FROM attacktable WHERE attackcode = "e3cce98b6aa8085ed6a960d2afcd4dca";
Query OK, 1 row affected (0,02 sec)
SELECT * FROM attacktable WHERE attackcode = "e3cce98b6aa8085ed6a960d2afcd4dca";
Empty set (0,00 sec)
Solved it after finding this thread.
In my case it was caused due to continued writing to the table while I was trying to drop columns. I locked the table, dropped the columns and unlocked the tables again.
LOCK TABLE attacktable WRITE;
ALTER TABLE DROP COLUMN ...;
UNLOCK TABLES
I am creating a table that has a unique key constraint and I performing a select query on that table. MySQL does not use the index for the unique key for querying that. Why is that happening?
CREATE TABLE foo (
column1 int NOT NULL,
column2 int NOT NULL,
UNIQUE KEY idx_column1_column2 (column1, column2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mysql> explain select * from foo where column1=1 and column2=1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> show index from foo;
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| foo | 0 | idx_column1_column2 | 1 | column1 | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| foo | 0 | idx_column1_column2 | 2 | column2 | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.01 sec)
I would expect here that the idx_column1_column2 index be used for my select query but it's not why is that happening, how can I get it to use that index automatically?
I'm getting errno 150 and can't figure out why. I've read a million other answers that say you must have an index set up on the parent column and I do so i'm not sure why i'm still getting this error.
Parent table indexes:
mysql> show indexes from plans;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| plans | 0 | PRIMARY | 1 | plan_id | A | 86 | NULL | NULL | | BTREE | | |
| plans | 0 | plan_id_UNIQUE | 1 | plan_id | A | 86 | NULL | NULL | | BTREE | | |
| plans | 1 | plan_id | 1 | plan_id | A | 86 | NULL | NULL | | BTREE | | |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.08 sec)
Child Table indexes:
mysql> show indexes from promos;
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| promos | 0 | PRIMARY | 1 | promo_id | A | 0 | NULL | NULL | | BTREE | | |
| promos | 1 | plan_id | 1 | plan_id | A | 0 | NULL | NULL | | BTREE | | |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.06 sec)
Error:
mysql> alter table promos add foreign key (plan_id) references plans (plan_id) on delete cascade on update cascade;
ERROR 1005 (HY000): Can't create table 'dev.#sql-2ce_599a' (errno: 150)
You have to have the same collation and character set for both columns.
this works: http://sqlfiddle.com/#!2/0f917
I have a table of over 9 million rows. I have a SELECT query that I'm using an index for. Here is the query:
SELECT `username`,`id`
FROM `04c1Tg0M`
WHERE `id` > 9259466
AND `tried` = 0
LIMIT 1;
That query executes very fast (0.00 sec). Here is the explain for that query:
+----+-------------+----------+-------+-----------------+---------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+-----------------+---------+---------+------+-------+-------------+
| 1 | SIMPLE | 04c1Tg0M | range | PRIMARY,triedex | PRIMARY | 4 | NULL | 10822 | Using where |
+----+-------------+----------+-------+-----------------+---------+---------+------+-------+-------------+
Now here is the same query except that I'm going to change the id to 6259466:
SELECT `username`,`id`
FROM `04c1Tg0M`
WHERE `id` > 5986551
AND `tried` = 0
LIMIT 1;
That query took 4.78 seconds to complete. This is the problem. Here is the explain for that query:
+----+-------------+----------+------+-----------------+---------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+-----------------+---------+---------+-------+---------+-------------+
| 1 | SIMPLE | 04c1Tg0M | ref | PRIMARY,triedex | triedex | 2 | const | 9275107 | Using where |
+----+-------------+----------+------+-----------------+---------+---------+-------+---------+-------------+
What is happening here and how can I fix it? Here are my indexes:
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 04c1Tg0M | 0 | PRIMARY | 1 | id | A | 9275093 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 1 | username | A | 9275093 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 2 | id | A | 9275093 | NULL | NULL | | BTREE | |
| 04c1Tg0M | 1 | pdex | 3 | tried | A | 9275093 | NULL | NULL | YES | BTREE | |
| 04c1Tg0M | 1 | triedex | 1 | tried | A | 0 | NULL | NULL | YES | BTREE | |
| 04c1Tg0M | 1 | triedex | 2 | id | A | 9275093 | NULL | NULL | | BTREE | |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
And here is my table structure:
| 04c1Tg0M | CREATE TABLE `04c1Tg0M` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`tried` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `pdex` (`username`,`id`,`tried`),
KEY `triedex` (`tried`,`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9275108 DEFAULT CHARSET=utf8 |
The first SQL returns 10822 rows, while the second one returns 9275107 rows!
The use of primary key "id" index in the second query isn't so useful because you have to do a full table scan anyway.
MySQL's cost-based optimizer thinks, in the case of the 2nd query, it's better off to use the index on 'tried'.
If you have to do a full table-scan, you're better off not using an index, as index constitutes additional disk reads.
You can use "use index" or "force index" in your query to hint to the optimizer whether to use an index.
Also update the statistics by analyzing your table periodically so the cost-based optimizer is working correctly.
Whats query can be used to get the details of Indexes of any table? I need this to find out primarykey/autoincremented value of any table..
Please help/Guide me...
You can use
show indexes from your_table;
For more informations : 12.4.5.23. SHOW INDEX Syntax
As a quick demo (on a not-quite-optimized table) :
mysql> show indexes from post;
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| post | 0 | PRIMARY | 1 | id | A | 7 | NULL | NULL | | BTREE | |
| post | 1 | id_blog_idx | 1 | id_blog | A | 2 | NULL | NULL | | BTREE | |
| post | 1 | id_user_idx | 1 | id_user | A | 7 | NULL | NULL | | BTREE | |
| post | 1 | code_syntax_idx | 1 | code_syntax | A | 7 | NULL | NULL | | BTREE | |
| post | 1 | code_status_idx | 1 | code_status | A | 2 | NULL | NULL | | BTREE | |
| post | 1 | id_category_idx | 1 | id_category | A | 7 | NULL | NULL | | BTREE | |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
6 rows in set (0,00 sec)
Note, though, that this will display indexes -- and auto_increment doesn't have much to do with indexes.
If you want to see the auto_increment of your table, you can use desc :
desc your_table;
For more informations : 12.8.1. DESCRIBE Syntax
And, for example, with the same table :
mysql> desc post;
+--------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| id_blog | int(10) unsigned | NO | MUL | NULL | |
| id_user | int(10) unsigned | NO | MUL | NULL | |
...
...
| nb_comments | smallint(6) | NO | | 0 | |
+--------------------+------------------+------+-----+---------+----------------+
17 rows in set (0,05 sec)
Also, an alternate way (which also shows you the current value of the AUTO_INCREMENT counter) is:
=> SHOW CREATE TABLE activations;
Yielding
CREATE TABLE `activations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=104974 DEFAULT CHARSET=utf8