277

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
2
  • Assuming UNIQUE KEY AID on mytable? Commented Dec 12, 2011 at 23:21
  • 1
    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. Commented Jul 7, 2023 at 7:34

16 Answers 16

392

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 ; 
Sign up to request clarification or add additional context in comments.

7 Comments

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;
That's great, but what can I do if my FOREIGN KEY constraint was anonymous?
@Pehat check my answer below stackoverflow.com/a/54145440/2305119
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: dba.stackexchange.com/questions/102371/…
There is a better way to do it. I recommend reading this for a deeper understanding of the pros&cons of the various options.
|
139

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.

4 Comments

The only change that I would make to this answer would be step 1. I would replace it with the answer shown here: stackoverflow.com/a/201678/2730233
Awesome answer, works good. One note, the syntax for Step 3 is ALTER TABLE table_name DROP COLUMN column_name;
@LucioMollinedo The answer you showed didn't give me any result, whilst the one here did.
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 that explains and compares the options in detail.
21

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 querying 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.

Comments

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);

Comments

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 Comments

you have the theory that other answers lacked.
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.
@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.
15

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;

1 Comment

your answer merged with @StefanMondelaers one are the correct answer
5

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

5 Comments

I think you exchanged enabling and disabling the checks. At the top I would expect FOREIGN_KEY_CHEK=0 and at the end FOREIGN_KEY_CHEK=1.
I edited the answere, switched the on off check and added comments to it. Would vote this serverl time up if I could :D thank you Ram.
Down voted because this doesnt work. I still cant delete the unique index.
This doesn't really work. I'm going to have to delete the foreign key first.
Of course it won't work for this case. "set FOREIGN_KEY_CHECKS=0" can help with corrupter relations only, not with foreign key itself
4

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`;

Comments

4

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

2 Comments

Can you elaborate on why dropping an index is risky? What potential problems could you see arising?
@KillerKode you might want to read this answer for a detailed explanation.
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.

Comments

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; 

Comments

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

Comments

0

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;

Comments

0

The problem is that an index can be referenced from another table try executing a script below so that you can see constraints which prevent your index from being deleted.

SELECT
  col.TABLE_NAME,
  col.CONSTRAINT_NAME,
  con.DELETE_RULE,
  con.UPDATE_RULE,
  col.REFERENCED_TABLE_NAME,
  col.COLUMN_NAME,
  col.REFERENCED_COLUMN_NAME,
  col.TABLE_SCHEMA
FROM
  INFORMATION_SCHEMA.KEY_COLUMN_USAGE col
JOIN
  INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS con USING (CONSTRAINT_NAME)
WHERE
    col.REFERENCED_TABLE_NAME = 'table_name'
    OR
    col.TABLE_NAME = 'table_name'
ORDER BY
  col.TABLE_NAME ASC;

Comments

-1

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

1 Comment

The foreign key constraint is there for a reason, so just getting rid of it is probably a bad idea. This provides a detailed explanation and a better approach.
-2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.