Não é possível alterar a coluna usada em uma restrição de chave estrangeira

111

Recebi este erro quando estava tentando alterar minha mesa.

Error Code: 1833. Cannot change column 'person_id': used in a foreign key constraint 'fk_fav_food_person_id' of table 'table.favorite_food'

Aqui está o meu CREATE TABLE STATEMENT, que foi executado com sucesso.

CREATE TABLE favorite_food(
    person_id SMALLINT UNSIGNED,
    food VARCHAR(20),
    CONSTRAINT pk_favorite_food PRIMARY KEY(person_id,food),
    CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

Então tentei executar esta instrução e recebi o erro acima.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
fonte
4
O exemplo acima é do livro "Learning SQL, 2nd edition". Espero que o autor, Alan Beaulieu, faça correções.
Dmitry

Respostas:

126

O tipo e a definição do campo de chave estrangeira e a referência devem ser iguais. Isso significa que sua chave estrangeira não permite a alteração do tipo de seu campo.

Uma solução seria esta:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

ALTER TABLE favorite_food
    DROP FOREIGN KEY fk_fav_food_person_id,
    MODIFY person_id SMALLINT UNSIGNED;

Agora você pode mudar sua person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

recriar chave estrangeira

ALTER TABLE favorite_food
    ADD CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
          REFERENCES person (person_id);

UNLOCK TABLES;

EDITAR: bloqueios adicionados acima, graças aos comentários

Você deve proibir a gravação no banco de dados enquanto faz isso, caso contrário, corre o risco de problemas de integridade de dados.

Eu adicionei um bloqueio de gravação acima

Todas as consultas de escrita em qualquer outra sessão que não a sua ( INSERT, UPDATE, DELETE) irão esperar até o tempo limite ou UNLOCK TABLES; É executado

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

EDIT 2: OP pediu uma explicação mais detalhada da linha "O tipo e definição de campo de chave estrangeira e referência devem ser iguais. Isso significa que sua chave estrangeira não permite alterar o tipo de seu campo."

Do MySQL 5.5 Reference Manual: FOREIGN KEY Constraints

As colunas correspondentes na chave estrangeira e na chave referenciada devem ter tipos de dados internos semelhantes dentro do InnoDB para que possam ser comparadas sem uma conversão de tipo. O tamanho e o sinal dos tipos inteiros devem ser os mesmos. O comprimento dos tipos de string não precisa ser o mesmo. Para colunas de string não binárias (caracteres), o conjunto de caracteres e o agrupamento devem ser os mesmos.

Michel Feldheim
fonte
1
Não se esqueça de usar uma transação para isso. Caso contrário, seu banco de dados pode ser corrompido.
François Bourgeois
5
Bom ponto, infelizmente o MySQL não suporta transações em torno de instruções DDL. As transações abertas são confirmadas antes que uma consulta DDL seja executada, consulte dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim
2
A instrução correta para recriar uma chave estrangeira seria: ALTER TALE favorite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERÊNCIAS pessoa (id);
Felizardo
1
Por que você modifica person_idlogo após descartar a chave estrangeira? Parece que você não mudou nada, pois já é um SMALLINT UNSIGNED.
Dennis Subachev
1
Não sabemos o que era, pois ele postou apenas a estrutura da tabela de referência. Innodb tem int como tipo interno, smallint etc são apenas atalhos
Michel Feldheim
198

Você pode desativar as verificações de chave estrangeira:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Certifique-se de NÃO usar isso na produção e tenha um backup.

Demencial
fonte
1
Parece ser uma solução muito insegura. Isso pode levar à perda de integridade dos dados?
hora
@Synaps - sim, poderia se você estiver excluindo / atualizando / inserindo. a perda de dados não ocorrerá se você estiver apenas modificando uma tabela ou propagando seu banco de dados, por outro lado, você deve validar manualmente seus dados (desde que você remove as restrições)
Dementic
2
Esta solução é ótima e você pode usá-la na produção se você gravar bloqueios antes de modificar seus dados e, em seguida, desbloquear quando terminar. Usar um arquivo sql para fazer suas alterações no menor tempo possível seria ainda melhor.
Francisco Zarabozo
Boa solução para ajustes rápidos
Genaut
1
Você não precisa de um bloqueio, pois SET FOREIGN_KEY_CHECKSé o escopo da sessão (outras sessões ainda terão a restrição FK aplicada). É perfeito para adicionar / remover AUTO_INCREMENT(o que não altera o tipo de dados da coluna real), mas não funcionará se você tentar alterar o tipo de dados da coluna para "real" (digamos, de SMALLINT para INT), pois você obterá um legítimo 150 FK constraint incorrectly formedquando mysql tenta substituir a tabela antiga pela nova. Nesse caso, use a resposta aceita.
Xenos
-3

Ao definir chaves (primárias ou estrangeiras), você está definindo restrições sobre como elas podem ser usadas, o que, por sua vez, limita o que você pode fazer com elas. Se você realmente deseja alterar a coluna, pode recriar a tabela sem as restrições, embora eu recomende não fazê-lo. De modo geral, se você tem uma situação em que deseja fazer algo, mas está bloqueado por uma restrição, é melhor resolvido alterando o que você deseja fazer em vez da restrição.

RonaldBarzell
fonte
12
Esta é uma resposta TAL inútil e inútil!
ajmedway
3
@ajmedway Então você pode escrever uma resposta útil sem culpar os outros usuários
Eu sou a pessoa mais estúpida
2
@IamtheMostStupidPerson É muito melhor do que apenas votar sem fazer comentários. Pelo menos o comentarista pode adivinhar por que os votos negativos. Respostas genéricas como "melhor prevenir do que remediar" não são úteis.
Csaba Toth