Como desativar temporariamente uma restrição de chave estrangeira no MySQL?


651

É possível desativar temporariamente as restrições no MySQL?

Eu tenho dois modelos de Django, cada um com uma chave estrangeira para o outro. A exclusão de instâncias de um modelo retorna um erro devido à restrição ForeignKey:

cursor.execute("DELETE FROM myapp_item WHERE n = %s", n)
transaction.commit_unless_managed()  #a foreign key constraint fails here

cursor.execute("DELETE FROM myapp_style WHERE n = %s", n)
transaction.commit_unless_managed()

É possível desativar temporariamente as restrições e excluir de qualquer maneira?


3
Ou eu não entendo o que você quer fazer, ou o que você está tentando fazer é muito, muito, muito feio . Mesmo que você consiga, provavelmente não deveria.
precisa

3
Soltar e reaplicar um FK está mudando seu banco de dados. Você está tentando desafiar as mesmas restrições que permitem ao sistema ter algum sentido, não importa que um FK possa ser uma coisa temporária e, se soubesse, entraria em pânico.
Grant Thomas

1
É estranho o que você está tentando fazer. Mas qual banco de dados você está usando?
andrefsp

4
e se, em vez de desativar sua restrição, você a modificasse permanentemente ON DELETE SET NULL? Isso faria uma coisa semelhante e você não precisaria ativar e desativar a verificação de chaves.
dnagirl

1
@ dnagirl: isso seria melhor, de fato. Como eu posso fazer isso?
julho

Respostas:


1466

Experimente DISABLE KEYSou

SET FOREIGN_KEY_CHECKS=0;

tenha certeza de

SET FOREIGN_KEY_CHECKS=1;

depois de.


14
isso é algo definido para o mysql como um todo ou apenas essa sessão?
tipu 31/10/2013

28
Eu acredito que é por sessão.
Andrew Campbell

13
serverfault.com/questions/291100/… , Observe também que você não pode disable keys para o Innodb
Pacerier

1
Posso apenas desativar FOREIGN_KEY_CHECKS para uma única tabela?
JDub9

@Pacerier Ao ler isso, parece que você pode, mas apenas para uma única sessão.
Brett

150

Para desativar globalmente a restrição de chave estrangeira, faça o seguinte:

SET GLOBAL FOREIGN_KEY_CHECKS=0;

e lembre-se de devolvê-lo quando terminar

SET GLOBAL FOREIGN_KEY_CHECKS=1;

AVISO: você deve fazer isso apenas quando estiver fazendo manutenção no modo de usuário único. Como pode resultar em inconsistência dos dados. Por exemplo, será muito útil quando você estiver carregando uma grande quantidade de dados usando uma saída mysqldump.


1
isto é o que eu precisava saber, por isso não é grande prática, mas esta resposta caras devem ser pontuação superior ...
ftrotter

1
Isso funcionou para mim depois de tentar a 'melhor resposta' não funcionou para mim. Talvez uma explicação da diferença possa ser adicionada.
Hexnet

7
@hexnet A diferença é que apenas SET FOREIGN_KEY_CHECKSaltera o valor da conexão atual , enquanto SET GLOBAL ..altera o valor de todas as conexões , incluindo conexões futuras. Se você apenas fizer isso SET FOREIGN..em uma janela, tente aplicar a instrução em uma janela diferente (em uma conexão diferente), o valor não foi alterado lá. Com GLOBAL, a mesma variável tem o mesmo valor para ambas as conexões.
precisa saber é o seguinte

A única coisa que poderia me ajudar ao reproduzir um dump maior (6 ou mais GB) <3
Max

Isso não funciona para mim. Quando tento, vejo:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
Mike B

53

Normalmente, desabilito as restrições de chave estrangeira apenas quando quero truncar uma tabela e, como continuo voltando a esta resposta, isso é para mim do futuro:

SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE table;
SET FOREIGN_KEY_CHECKS=1;

25

Em vez de desativar sua restrição, modifique-a permanentemente para ON DELETE SET NULL. Isso fará uma coisa semelhante e você não precisaria ativar e desativar a verificação de chaves. Igual a:

ALTER TABLE tablename1 DROP FOREIGN KEY fk_name1; //get rid of current constraints
ALTER TABLE tablename2 DROP FOREIGN KEY fk_name2;

ALTER TABLE tablename1 
  ADD FOREIGN KEY (table2_id) 
        REFERENCES table2(id)
        ON DELETE SET NULL  //add back constraint

ALTER TABLE tablename2 
  ADD FOREIGN KEY (table1_id) 
        REFERENCES table1(id)
        ON DELETE SET NULL //add back other constraint

Leia isto ( http://dev.mysql.com/doc/refman/5.5/en/alter-table.html ) e este ( http://dev.mysql.com/doc/refman/5.5/en /create-table-foreign-keys.html ).


7
Cuidado: alterar a tabela pode levar um longo tempo; é melhor definir o servidor global FOREIGN_KEY_CHECKScomo 0 e colocá-lo de volta assim que o trabalho sujo for concluído. Além disso, pode travar para escrever suas tabelas.
Aki

Isso não quebrará a referência ao alterar o tipo de coluna remota? (Parece que meu cliente renomeia uma tabela temporária modificado para o nome da tabela original.)
Cees Timmerman

15

Para desativar a restrição de chave estrangeira globalmente:

SET GLOBAL FOREIGN_KEY_CHECKS = 0;

e para restrição de chave estrangeira ativa

SET GLOBAL FOREIGN_KEY_CHECKS = 1;

10

Uma solução muito simples com o phpmyadmin:

  • Na sua mesa, vá para a SQLaba
  • Depois de editar o comando SQL que você deseja executar, há uma caixa de seleção ao lado GO, denominada ' Ativar verificações de chave estrangeira' .
  • Desmarque esta caixa de seleção e execute seu SQL . Ele será verificado novamente novamente após a execução.

3
Obrigado! Na verdade, a solução SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;não funcionou para mim no PHPMyAdmin porque esqueci de desmarcar a caixa de seleção 'Ativar verificações de chave estrangeira'. No PHPMyAdmin, você pode pular esses comandos SET e apenas desmarcar a caixa de seleção.
Jan

5

Para mim simplesmente SET FOREIGN_KEY_CHECKS=0;não foi suficiente. Eu ainda estava tendo um com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException.

Eu tive que adicionar ALTER TABLE myTable DISABLE KEYS;.

Assim:

SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE myTable DISABLE KEYS;
DELETE FROM myTable;
ALTER TABLE myTable ENABLE KEYS;
SET FOREIGN_KEY_CHECKS=1;

Para sua informação, o mySQL 5.7 lança um aviso, o mecanismo InnoDB não possui essa opção ao executar o comando DISABLE KEYS.
JDub9

este fez o trabalho, sem a tabela de alter também não funcionou para mim
David Kabii

3

Se o campo-chave for anulável, você também poderá definir o valor como nulo antes de tentar excluí-lo:

cursor.execute("UPDATE myapp_item SET myapp_style_id = NULL WHERE n = %s", n)
transaction.commit_unless_managed() 

cursor.execute("UPDATE myapp_style SET myapp_item_id = NULL WHERE n = %s", n)
transaction.commit_unless_managed()

cursor.execute("DELETE FROM myapp_item WHERE n = %s", n)
transaction.commit_unless_managed()

cursor.execute("DELETE FROM myapp_style WHERE n = %s", n)
transaction.commit_unless_managed()

2

No phpMyAdmin, você pode selecionar várias linhas e clicar na ação excluir. Você entrará em uma tela que lista as consultas de exclusão, pode desmarcar a verificação de chave estrangeira e clicar em Sim para executá-las.

Isso permitirá que você exclua linhas, mesmo se houver uma restrição de restrição ON DELETE.


-2

Não é uma boa ideia definir uma restrição de chave estrangeira como 0, porque se você fizer isso, seu banco de dados não garantirá que não esteja violando a integridade referencial. Isso pode levar a dados imprecisos, enganosos ou incompletos.

Você cria uma chave estrangeira por um motivo: porque todos os valores na coluna filho devem ser iguais a um valor na coluna pai. Se não houver restrições de chave estrangeira, uma linha filha pode ter um valor que não esteja na linha pai, o que levaria a dados imprecisos.

Por exemplo, digamos que você tenha um site para os alunos fazerem login e todos os alunos devem se registrar em uma conta como usuário. Você tem uma tabela para os IDs do usuário, com o ID do usuário como chave primária; e outra tabela para contas de alunos, com o ID do aluno como uma coluna. Como todo aluno deve ter um ID de usuário, faria sentido transformar o ID do aluno da tabela de contas do aluno em uma chave estrangeira que faça referência ao ID do usuário da chave primária na tabela de IDs do usuário. Se não houver verificação de chave estrangeira, um aluno pode acabar tendo um ID de estudante e nenhum ID de usuário, o que significa que um aluno pode obter uma conta sem ser um usuário, o que está errado.

Imagine se isso acontecer com uma grande quantidade de dados. É por isso que você precisa da verificação de chave estrangeira.

É melhor descobrir o que está causando o erro. Provavelmente, você está tentando excluir de uma linha pai sem excluir de uma linha filho. Tente excluir da linha filho antes de excluir da linha pai.


É verdade que sempre há uma troca.
Pacerier 23/02

21
Ninguém está dizendo para executá-lo assim para sempre. Você desativa as restrições, carrega em massa alguns dados e os liga novamente. Não é grande coisa, as pessoas fazem isso o tempo todo.
bwawok

é necessário para importações em massa, pelo menos para o desempenho, é muito comum. às vezes, você só precisa restaurar os dados e fazer suas verificações.
Firas Abd Alrahman

3
Esta não é uma resposta para a pergunta.
Koray Tugay

Note, sua pergunta é como fazer isso temporariamente. Isso é necessário ao fazer determinadas importações de manutenção e dados. A ressalva, é claro, é que seus scripts de importação se tornam responsáveis ​​pela integridade dos dados. Depois, quando os índices e restrições forem ativados novamente, o banco de dados informará se algo está quebrado.
mcstar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.