253

I need to ALTER my existing database to add a column. Consequently I also want to update the UNIQUE field to encompass that new column. I'm trying to remove the current index but keep getting the error MySQL Cannot drop index needed in a foreign key constraint

CREATE TABLE mytable_a (
ID          TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name        VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;

CREATE TABLE mytable_b (
ID          TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name        VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;

CREATE TABLE mytable_c (
ID          TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name        VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;


CREATE TABLE `mytable` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `AID` tinyint(5) NOT NULL,
  `BID` tinyint(5) NOT NULL,
  `CID` tinyint(5) NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `AID` (`AID`,`BID`,`CID`),
  KEY `BID` (`BID`),
  KEY `CID` (`CID`),
  CONSTRAINT `mytable_ibfk_1` FOREIGN KEY (`AID`) REFERENCES `mytable_a` (`ID`) ON DELETE CASCADE,
  CONSTRAINT `mytable_ibfk_2` FOREIGN KEY (`BID`) REFERENCES `mytable_b` (`ID`) ON DELETE CASCADE,
  CONSTRAINT `mytable_ibfk_3` FOREIGN KEY (`CID`) REFERENCES `mytable_c` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB;




mysql> ALTER TABLE mytable DROP INDEX AID;
ERROR 1553 (HY000): Cannot drop index 'AID': needed in a foreign key constraint
Stewart
  • 17,616
  • 8
  • 52
  • 80
user391986
  • 29,536
  • 39
  • 126
  • 205
  • Assuming `UNIQUE KEY AID` on mytable? – Mike Purcell Dec 12 '11 at 23:21
  • The current top answers have gained a lot of up-votes the last 12 years, but they focus on getting rid of the **error**. I think it's important to understand & fix the **underlying problem**. The error itself is a good thing. It's like a safeguard. I'd recommend reading [this answer](https://stackoverflow.com/a/76591064). – at54321 Jul 07 '23 at 07:34

15 Answers15

358

You have to drop the foreign key. Foreign keys in MySQL automatically create an index on the table (There was a SO Question on the topic).

ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_1 ; 
Community
  • 1
  • 1
Brian Fisher
  • 23,519
  • 15
  • 78
  • 82
  • 27
    You might want to add it back after dropping the index: ALTER TABLE `mytable` ADD CONSTRAINT `mytable_ibfk_1` FOREIGN KEY (`AID`) REFERENCES `mytable_a` (`ID`) ON DELETE CASCADE; – laffuste Feb 11 '14 at 07:49
  • 9
    That's great, but what can I do if my `FOREIGN KEY` constraint was anonymous? – Pehat Jul 08 '16 at 14:48
  • @Pehat check my answer below https://stackoverflow.com/a/54145440/2305119 – thyzz Jan 11 '19 at 11:16
  • 3
    Note: the foreign key might not be as obvious. To find all foreign keys related to a table and column, you can use this query: https://dba.stackexchange.com/questions/102371/mysql-how-to-check-foreign-keys-related-to-a-table – charlax Jun 12 '19 at 14:54
  • I hate that this is the correct answer, but it is. (upvote). Yes, as Iaffuste says, you probably want to re-add the FK after you drop the index (or constraint). – granadaCoder Mar 24 '21 at 15:18
  • I had drop the foreign key first and then when I tried to remove that field from my table structure, I am getting " Can't DROP FOREIGN KEY `userId`; check that it exists". What is the reason behind it? – Jayna Tanawala Oct 11 '22 at 11:10
  • There is a better way to do it. I recommend reading [this](https://stackoverflow.com/a/76591064) for a deeper understanding of the pros&cons of the various options. – at54321 Jun 30 '23 at 17:32
132

Step 1

List foreign key ( NOTE that its different from index name )

SHOW CREATE TABLE  <Table Name>

The result will show you the foreign key name.

Format:

CONSTRAINT `FOREIGN_KEY_NAME` FOREIGN KEY (`FOREIGN_KEY_COLUMN`) REFERENCES `FOREIGN_KEY_TABLE` (`id`),

Step 2

Drop (Foreign/primary/key) Key

ALTER TABLE <Table Name> DROP FOREIGN KEY <Foreign key name>

Step 3

Drop the index.

Rakmo
  • 1,926
  • 3
  • 19
  • 37
Abhishek Goel
  • 18,785
  • 11
  • 87
  • 65
  • The only change that I would make to this answer would be step 1. I would replace it with the answer shown here: https://stackoverflow.com/a/201678/2730233 – Lucio Mollinedo May 08 '23 at 13:33
  • Thank a log, it save my time – Gaurav Patil May 11 '23 at 17:08
  • Awesome answer, works good. One note, the syntax for Step 3 is ALTER TABLE table_name DROP COLUMN column_name; – Avery85 May 11 '23 at 23:28
  • @LucioMollinedo The answer you showed didn't give me any result, whilst the one here did. – Isu Jun 23 '23 at 11:02
  • This is a more-detailed explanation of the solution from the accepted answer, but there are two issues with it: 1) Just dropping the foreign key constaint is dangerous (it's there for a reason); 2) There is a better approach. I recommend reading [this answer](https://stackoverflow.com/a/76591064) that explains and compares the options in detail. – at54321 Jun 30 '23 at 17:42
19

A foreign key always requires an index. Without an index enforcing the constraint would require a full table scan on the referenced table for every inserted or updated key in the referencing table. And that would have an unacceptable performance impact. This has the following 2 consequences:

  • When creating a foreign key, the database checks if an index exists. If not an index will be created. By default, it will have the same name as the constraint.
  • When there is only one index that can be used for the foreign key, it can't be dropped. If you really wan't to drop it, you either have to drop the foreign key constraint or to create another index for it first.
  • 3
    you have the theory that other answers lacked. – Dennis Feb 08 '19 at 22:20
  • 3
    So: If you have a compound unique index (multiple columns in a unique constraint) you cannot remove the unique A-B key unless you have an index for A and B. If you get this error, another table is using the index of column A or B, and you'll have to add those before you can safely remove the A-B unique. – Robin De Schepper May 06 '19 at 09:24
  • @RobinDeSchepper Good remark. And when using compound unique indexes, the order of the fields is not important for the unique index, but it might be important for a foreign key. A unique index on A,B can be used by a foreign key on A, but not by a foreign key on B. – Stefan Mondelaers Jun 07 '19 at 12:59
19

If you mean that you can do this:

CREATE TABLE mytable_d (
ID          TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name        VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;


ALTER TABLE mytable
ADD COLUMN DID tinyint(5) NOT NULL,
ADD CONSTRAINT mytable_ibfk_4 
      FOREIGN KEY (DID) 
        REFERENCES mytable_d (ID) ON DELETE CASCADE;

 > OK.

But then:

ALTER TABLE mytable
DROP KEY AID ;

gives error.


You can drop the index and create a new one in one ALTER TABLE statement:

ALTER TABLE mytable
DROP KEY AID ,
ADD UNIQUE KEY AID (AID, BID, CID, DID);
ypercubeᵀᴹ
  • 113,259
  • 19
  • 174
  • 235
13

Because you have to have an index on a foreign key field you can just create a simple index on the field 'AID'

CREATE INDEX aid_index ON mytable (AID);

and only then drop the unique index 'AID'

ALTER TABLE mytable DROP INDEX AID;
Eli DM
  • 425
  • 4
  • 8
4

I think this is easy way to drop the index.

set FOREIGN_KEY_CHECKS=0; //disable checks

ALTER TABLE mytable DROP INDEX AID;

set FOREIGN_KEY_CHECKS=1; //enable checks
Dwza
  • 6,494
  • 6
  • 41
  • 73
Ram E Sh
  • 113
  • 1
  • 1
3

drop the index and the foreign_key in the same query like below

ALTER TABLE `your_table_name` DROP FOREIGN KEY `your_index`;
ALTER TABLE `your_table_name` DROP COLUMN `your_foreign_key_id`;
Yann Boyongo
  • 171
  • 1
  • 5
3

Dropping FK is tedious and risky. Simply create the new index with new columns and new index name, such as AID2. After the new Unique Index is created, you can drop the old one with no issue. Or you can use the solution given above to incorporate the "drop index, add unique index" in the same alter table command. Both solutions will work

  • 1
    Can you elaborate on why dropping an index is risky? What potential problems could you see arising? – KillerKode Sep 27 '22 at 12:49
  • @KillerKode you might want to read [this answer](https://stackoverflow.com/a/76591064) for a detailed explanation. – at54321 Jun 30 '23 at 17:28
2

A deeper understanding on the matter could be helpful not just for finding a workaround to the error in question but also for better designing our tables and indexes. Some of the existing answers are good, but others are incomplete or clearly wrong, and that could be dangerous for hasty readers.

Important initial notes

  • The question is about a UNIQUE index, but that's of little importance here. What matters is that the index of interest is compound (i.e. it consists of 2+ columns).
  • In MySQL KEY is (in this context at least) a synonym to INDEX.

The problem

In MySQL an index is required for every foreign key constraint (FKC). Not all DBMSs have such a requirement, but in practice, if you don't have such an index, some operations, such as deleting a record in a table that is referenced from other tables might suffer from terrible performance if those referencing tables are big enough. That's why with some RDBMs like MySQL, it's been a deliberate design decision to make such indexes required.

When we create a FKC, normally, MySQL automatically creates a new index with the same column(s) as the FK (remember, primary and foreign keys could consist of 2+ columns, but in OP's example they don't). However, if there already exists an index that could be used for the FKC, MySQL will not create another index.

In OP's example, if we don't count the primary key index, we have 3 indexes: AID,BID,CID. The first one is compound, the others are not. Note that the name of the unique index might be a bit misleading, as it's the same as the name of the first column. I always recommend giving explicit names to compound indexes, to avoid potential confusion. So let's imagine the name of the UNIQUE KEY is ABC and see what indexes the table would have at the starting point:

UNIQUE KEY `ABC` (`AID`,`BID`,`CID`),
       KEY `BID` (`BID`),
       KEY `CID` (`CID`),

The key ABC can be used for the FKC on AID, because AID is the first column in it. That's why MySQL decided not to create an extra index on AID only. But dropping an index that is internally used by MySQL to satisfy the requirement of always having an index, would be a no-no. Hence the error.

The solution

We should first ask ourselves: do we want to have an explicit dedicated index for AID only, or do we want to continue using a compound index that we are having anyway. Both would be valid options, depending on our needs. If we want the best possible performance when quering on AID only, having a separate index might be slightly more efficient, but that comes with the cost of having one more index (more storage, slower updates, etc.). More often than not, the pros of using the compound index outweigh the cons, but when performance is critical, it might be useful to at least be aware that there is an option to have a smaller dedicated index.

Option 1: Having a (permanent) dedicated index for the FKC

First, we create the dedicated index for the FKC:

CREATE INDEX IDX_AID ON mytable(AID);

Note that I've used IDX_AID as a name here, but if we preferred to stick to the way MySQL names such indexes, AID could also be used if available at this point. In my example with ABC it would be, but in OP's case it wouldn't (although with RENAME INDEX that could easily be solved).

Then we re-create the unique index with the new column (let's call it NEW_COL and let's say we want it to be somewhere in the middle /remember, column order in indexes does matter/):

DROP INDEX ABC ON mytable;
CREATE UNIQUE INDEX IDX_ABNC ON mytable(AID, BID, NEW_COL, CID);

Now with SHOW CREATE TABLE mytable we should be having:

UNIQUE KEY `IDX_ABNC` (`AID`,`BID`,`NEW_COL`,`CID`),
       KEY `IDX_AID`  (`AID`),
       KEY `BID`      (`BID`),
       KEY `CID`      (`CID`),

Option 2: No dedicated index for the FKC

We essentially need a workaround, to avoid the temporary inconsistent state.

Method 1: Temporary index on the FKC (preferred)

This is similar to Option 1 but we just drop the index at the end:

CREATE INDEX IDX_TMP ON mytable(AID);
DROP INDEX IDX_ABC ON mytable;  -- here we have IDX_TMP, so no problem
CREATE UNIQUE INDEX IDX_ABNC ON mytable(AID, BID, NEW_COL, CID);
DROP INDEX IDX_TMP ON mytable;  -- not needed anymore

Method 2: Drop FKC and then recreate it

Essentially, the idea is to first drop the "guard" (i.e. the FKC) temporarily, and recreate it at the end.

ALTER TABLE mytable DROP FOREIGN KEY <FKC-name>;    
DROP INDEX IDX_ABC ON mytable;  -- no "guard" here to stop us
CREATE UNIQUE INDEX IDX_ABNC ON mytable(AID, BID, NEW_COL, CID);
ALTER TABLE mytable ADD CONSTRAINT <FKC-name> FOREIGN KEY (AID) REFERENCES mytable(AID) ON DELETE CASCADE;

You can find the <FKC-name> from SHOW CREATE TABLE mytable.

Recommendation

I think Method 1 is preferable, for the following reasons:

  • A bit simpler (no need to look up for the exact FKC name).
  • Dropping the FKC, even if for a very short time, opens the window for someone inserting an invalid value.
  • There is a potential risk to mix something up when recreating the FKC. For example, you might forget to add an important attribute (such as ON DELETE CASCADE). Or, to accidentally put a wrong one.
  • If, for some reason, we forgot or failed to execute the last step, with Method 1 no harm is done, whereas with Method 2 we've left the DB in a vulnerable state.
at54321
  • 8,726
  • 26
  • 46
0

In my case I dropped the foreign key and I still could not drop the index. That was because there was yet another table that had a foreign key to this table on the same fields. After I dropped the foreign key on the other table I could drop the indexes on this table.

jav
  • 583
  • 5
  • 15
0

If you are using PhpMyAdmin sometimes it don't show the foreign key to delete.

The error code gives us the name of the foreign key and the table where it was defined, so the code is:

ALTER TABLE your_table DROP FOREIGN KEY foreign_key_name; 
Eli
  • 27
  • 2
0

You can easily check it with DBeaver. Example: enter image description here

As you can see there are 3 FKs but only 2 FK indexes. There is no index for FK_benefCompanyNumber_beneficiaries_benefId as UK index provide uniqueness for that FK.

To drop that UK you need to:

  1. DROP FK_benefCompanyNumber_beneficiaries_benefId
  2. DROP UK
  3. CREATE FK_benefCompanyNumber_beneficiaries_benefId
Karol Murawski
  • 326
  • 2
  • 10
-1

You can show Relation view in phpMyAdmin and first delete foreign key. After this you can remove index.

dmajka
  • 109
  • 1
  • 4
  • The foreign key constraint is there for a reason, so just getting rid of it is probably a bad idea. [This](https://stackoverflow.com/a/76591064) provides a detailed explanation and a better approach. – at54321 Jun 30 '23 at 17:23
-1

The current most upvoted answer is not complete.
One needs to remove all the foreign keys whose "source" column is also present in the UNIQUE KEY declaration. So in this case, it is not enough to remove mytable_ibfk_1 for the error to go away, mytable_ibfk_2 and mytable_ibfk_3 must be deleted as well. This is the complete answer:

    ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_1;
    ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_2;
    ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_3;
Víctor Gil
  • 835
  • 8
  • 10
-1

Its late now but I found a solution which might help somebody in future. Just go to table's structure and drop foreign key from foreign keys list. Now you will be able to delete that column.