Jak tymczasowo wyłączyć ograniczenie klucza obcego w MySQL?


651

Czy można tymczasowo wyłączyć ograniczenia w MySQL?

Mam dwa modele Django, każdy z kluczem ForeignKey do drugiego. Usunięcie instancji modelu zwraca błąd z powodu ograniczenia 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()

Czy mimo to można tymczasowo wyłączyć ograniczenia i je usunąć?


3
Albo nie rozumiem, co chcesz robić, albo to, co próbujesz zrobić, jest bardzo, bardzo, bardzo brzydkie . Nawet jeśli możesz to zrobić, prawdopodobnie nie powinieneś.
Dariusz

3
Upuszczenie i ponowne zastosowanie FK powoduje zmianę bazy danych. Próbujesz przeciwstawić się takim ograniczeniom, które pozwalają systemowi dostrzec sens, nie ma znaczenia, że ​​FK może być czymś tymczasowym, a gdyby wiedział, wpadłby w panikę.
Grant Thomas

1
Dziwne, co próbujesz zrobić. Ale jakiej bazy danych używasz?
andrefsp

4
co jeśli zamiast wyłączyć ograniczenie, na stałe je zmodyfikowałeś ON DELETE SET NULL? Osiągnęłoby to podobną rzecz i nie musiałbyś włączać i wyłączać sprawdzania klucza.
dnagirl

1
@dnagirl: rzeczywiście byłoby lepiej. Jak mogę to zrobić?
lipiec

Odpowiedzi:


1466

Spróbuj DISABLE KEYSlub

SET FOREIGN_KEY_CHECKS=0;

Upewnij się

SET FOREIGN_KEY_CHECKS=1;

po.


14
czy jest to coś, co jest ustawione dla mysql jako całości czy tylko dla tej sesji?
tipu

28
Wierzę, że to jest na sesję.
Andrew Campbell,

13
serverfault.com/questions/291100/… , Należy również pamiętać, że nie można disable keys dla Innodb
Pacerier

1
Czy mogę po prostu wyłączyć FOREIGN_KEY_CHECKS dla pojedynczego stołu?
jDub9

@Pacerier Po przeczytaniu tego wydaje się, że możesz, ale tylko na jedną sesję.
Brett

150

Aby globalnie wyłączyć ograniczenie klucza obcego, wykonaj następujące czynności:

SET GLOBAL FOREIGN_KEY_CHECKS=0;

i pamiętaj, aby ustawić go z powrotem, gdy skończysz

SET GLOBAL FOREIGN_KEY_CHECKS=1;

OSTRZEŻENIE: Powinieneś to zrobić tylko podczas konserwacji w trybie pojedynczego użytkownika. Może to spowodować niespójność danych. Na przykład będzie to bardzo pomocne, gdy przesyłasz dużą ilość danych przy użyciu wyjścia mysqldump.


1
to musiałem wiedzieć, więc nie jest to świetna praktyka, ale odpowiedź tych facetów powinna być lepsza ...
ftrotter

1
To zadziałało dla mnie po wypróbowaniu „najlepszej odpowiedzi” nie działało dla mnie. Być może można by dodać wyjaśnienie różnicy.
hexnet

7
@hexnet Różnica polega na tym, że po SET FOREIGN_KEY_CHECKSprostu zmienia wartość bieżącego połączenia , a SET GLOBAL ..zmienia wartość wszystkich połączeń , w tym przyszłych połączeń. Jeśli zrobisz to tylko SET FOREIGN..w jednym oknie, spróbuj zastosować instrukcję w innym oknie (przez inne połączenie), wartość się tam nie zmieniła. Z GLOBAL, ta sama zmienna ma tę samą wartość dla obu połączeń.
MatsLindh

Jedyne, co może mi pomóc podczas odtwarzania większego zrzutu (6+ GB) <3
Max

To mi nie działa. Kiedy próbuję, widzę:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
Mike B

53

Zwykle wyłączam ograniczenia klucza obcego tylko wtedy, gdy chcę obciąć tabelę, a ponieważ wciąż wracam do tej odpowiedzi, to dla mnie w przyszłości:

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

25

Zamiast wyłączać ograniczenie, trwale zmodyfikuj je, aby WŁĄCZYĆ USUŃ NULL. Dzięki temu osiągniesz podobną rzecz i nie będziesz musiał włączać i wyłączać sprawdzania klucza. Tak jak:

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

Przeczytaj to ( http://dev.mysql.com/doc/refman/5.5/en/alter-table.html ) i to ( http://dev.mysql.com/doc/refman/5.5/en /create-table-foreign-keys.html ).


7
Strzeż się, że zmiana tabeli może zająć dużo czasu, lepiej ustawić globalny serwer na FOREIGN_KEY_CHECKS0 i odłożyć go z powrotem po wykonaniu brudnej roboty. Poza tym może zablokować pisanie twoich tabel.
Aki

Czy to nie złamie referencji przy zmianie typu kolumny zdalnej? (Wygląda na to, że mój klient zmienia nazwę zmodyfikowanej tabeli tymczasowej na pierwotną nazwę tabeli.)
Cees Timmerman

15

Aby wyłączyć ograniczenie klucza obcego globalnie:

SET GLOBAL FOREIGN_KEY_CHECKS = 0;

i dla aktywnego ograniczenia klucza obcego

SET GLOBAL FOREIGN_KEY_CHECKS = 1;

10

Bardzo proste rozwiązanie z phpmyadmin:

  • W tabeli przejdź do SQLkarty
  • Po edycji polecenia SQL, które chcesz uruchomić, obok pola wyboru o GOnazwie „ Włącz sprawdzanie klucza obcego” znajduje się pole wyboru .
  • Odznacz to pole wyboru i uruchom SQL . Zostanie on automatycznie ponownie sprawdzony po uruchomieniu.

3
Dzięki! Rzeczywiście rozwiązanie SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;nie działało dla mnie w PHPMyAdmin, ponieważ zapomniałem odznaczyć pole wyboru „Włącz sprawdzanie klucza obcego”. W PHPMyAdmin możesz pominąć te polecenia SET i po prostu odznaczyć pole wyboru.
Jan

5

Dla mnie to po prostu SET FOREIGN_KEY_CHECKS=0;za mało. Nadal miałem com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException.

Musiałem dodać ALTER TABLE myTable DISABLE KEYS;.

Więc:

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

Do Twojej wiadomości, mySQL 5.7 generuje ostrzeżenie, silnik InnoDB nie ma tej opcji po uruchomieniu polecenia DISABLE KEYS.
jDub9

to działało, bez tabeli zmian to też nie działało dla mnie
David Kabii

3

Jeśli pole klucza ma wartość zerową, możesz również ustawić wartość null przed próbą jej usunięcia:

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

W phpMyAdmin możesz wybrać wiele wierszy, a następnie kliknąć akcję usuwania. Wejdziesz do ekranu z listą usuniętych zapytań, możesz odznaczyć zaznaczenie klucza obcego i kliknąć Tak, aby je wykonać.

Umożliwi to usunięcie wierszy, nawet jeśli istnieje ograniczenie ograniczenia ON DELETE.


-2

Ustawienie ograniczenia klucza obcego na 0 nie jest dobrym pomysłem, ponieważ jeśli tak zrobisz, baza danych nie upewni się, że nie narusza integralności referencyjnej. Może to prowadzić do niedokładnych, wprowadzających w błąd lub niekompletnych danych.

Tworzysz klucz obcy z jakiegoś powodu: ponieważ wszystkie wartości w kolumnie potomnej będą takie same jak wartość w kolumnie macierzystej. Jeśli nie ma ograniczeń klucza obcego, wiersz potomny może mieć wartość, która nie znajduje się w wierszu nadrzędnym, co może prowadzić do niedokładnych danych.

Załóżmy na przykład, że masz witrynę internetową, na której studenci mogą się zalogować, a każdy uczeń musi zarejestrować się jako konto. Masz jedną tabelę dla identyfikatorów użytkowników, z identyfikatorem użytkownika jako kluczem podstawowym; i kolejna tabela dla kont studentów, z identyfikatorem studenta jako kolumną. Ponieważ każdy uczeń musi mieć identyfikator użytkownika, sensowne byłoby, aby identyfikator studenta z tabeli kont studentów był kluczem obcym, który odwołuje się do identyfikatora użytkownika klucza podstawowego w tabeli identyfikatorów użytkowników. Jeśli nie ma sprawdzania klucza obcego, uczeń może mieć identyfikator studenta i identyfikator użytkownika, co oznacza, że ​​uczeń może uzyskać konto bez bycia użytkownikiem, co jest błędne.

Wyobraź sobie, że dzieje się to z dużą ilością danych. Dlatego potrzebujesz sprawdzenia klucza obcego.

Najlepiej dowiedzieć się, co powoduje błąd. Najprawdopodobniej próbujesz usunąć z wiersza nadrzędnego bez usuwania z wiersza podrzędnego. Spróbuj usunąć z wiersza podrzędnego przed usunięciem z wiersza nadrzędnego.


To prawda, że ​​zawsze istnieje kompromis.
Pacerier

21
Nikt nie mówi, aby tak to zawsze działać. Wyłączasz ograniczenia, ładujesz zbiorczo niektóre dane i włączasz z powrotem. Nic wielkiego, ludzie robią to cały czas.
bwawok

jest to konieczne w przypadku importu hurtowego, przynajmniej w celu zapewnienia wydajności jest bardzo powszechne. Czasami potrzebujesz tylko przywrócić dane, a następnie możesz sprawdzić.
Firas Abd Alrahman

3
To nie jest odpowiedź na pytanie.
Koray Tugay,

Uwaga, jego pytanie brzmi: jak to zrobić tymczasowo. Jest to wymagane podczas wykonywania niektórych czynności konserwacyjnych i importowania danych. Zastrzeżeniem jest oczywiście to, że skrypty importu stają się odpowiedzialne za integralność danych. Następnie, gdy indeksy i ograniczenia zostaną ponownie włączone, db poinformuje cię, czy coś jest zepsute.
mcstar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.