Fałszywe ograniczenie klucza obcego nie powiodło się

110

Otrzymuję ten komunikat o błędzie:

BŁĄD 1217 (23000) w wierszu 40: nie można usunąć ani zaktualizować wiersza nadrzędnego: nie powiodło się ograniczenie klucza obcego

... kiedy próbuję upuścić stół:

DROP TABLE IF EXISTS `area`;

... zdefiniowane w ten sposób:

CREATE TABLE `area` (
  `area_id` char(3) COLLATE utf8_spanish_ci NOT NULL,
  `nombre_area` varchar(30) COLLATE utf8_spanish_ci NOT NULL,
  `descripcion_area` varchar(100) COLLATE utf8_spanish_ci NOT NULL,
  PRIMARY KEY (`area_id`),
  UNIQUE KEY `nombre_area_UNIQUE` (`nombre_area`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci;

Zabawne jest to, że porzuciłem już wszystkie inne tabele w schemacie, które mają klucze obce area. W rzeczywistości baza danych jest pusta, z wyjątkiem areatabeli.

Jak może mieć wiersze potomne, jeśli w bazie danych nie ma żadnego innego obiektu? O ile wiem, InnoDB nie zezwala na klucze obce w innych schematach, prawda?

(Mogę nawet uruchomić RENAME TABLE area TO something_elsepolecenie: -?)

Álvaro González
źródło
Czy to możliwe, że tabela jest częścią relacji referencyjnej integralności w innym schemacie?
Raj More
Mam kilka innych kopii aplikacji, więc zawsze jest to możliwe. Jednak składnia, której używam, jest w zasadzie CONSTRAINT fk_servicio_area1 FOREIGN KEY (area_id) REFERENCES area (area_id), tj. Brak nazwy schematu w odwołaniu do tabeli: -?
Álvaro González

Odpowiedzi:

101

Dwie możliwości:

  1. Istnieje tabela w innym schemacie („baza danych” w terminologii mysql), która ma odniesienie FK
  2. Słownik danych wewnętrznych innodb nie jest zsynchronizowany ze słownikiem mysql.

Możesz zobaczyć, która to była tabela (a przynajmniej jedna z nich), wykonując polecenie „POKAŻ STATUS INNODB SILNIKA” po niepowodzeniu upuszczania.

Jeśli okaże się, że jest to drugi przypadek, zrzuciłbym i przywróciłbym cały serwer, jeśli możesz.

MySQL 5.1 i nowsze podają nazwę tabeli z FK w komunikacie o błędzie.

MarkR
źródło
1
Nie mogę już odtworzyć problemu. Prawdopodobną przyczyną jest brak synchronizacji w słowniku. Przetestuję to dzień i zobaczę, jakie SHOW ENGINE INNODB STATUSraporty.
Álvaro González
3
Dziękuję za tę odpowiedź! Miałem tabelę wiele do wielu, która nadal odwoływała się do tabeli, której nie mogliśmy usunąć, więc najpierw musiałem usunąć tę tabelę.
Christian Oudard
5
SHOW ENGINE INNODB STATUS wyświetla ostatni błąd klucza obcego w pozycji „LATEST FOREIGN KEY ERROR”. Ma sygnaturę czasową.
bbrame
może istnieć tabela nadal zawierająca klucz odniesienia do tabeli tematu. tak było w moim przypadku.
RT
Zaoszczędzono dużo czasu. Usunięto db pod „LATEST FOREIGN KEY ERROR”
Sand1512
121

Na żądanie, teraz jako odpowiedź ...

Podczas korzystania z MySQL Query Browser lub phpMyAdmin wydaje się, że dla każdego zapytania otwierane jest nowe połączenie ( bugs.mysql.com/bug.php?id=8280 ), co sprawia, że ​​konieczne jest zapisanie wszystkich instrukcji drop w jednym zapytaniu, np.

SET FOREIGN_KEY_CHECKS=0; 
DROP TABLE my_first_table_to_drop; 
DROP TABLE my_second_table_to_drop; 
SET FOREIGN_KEY_CHECKS=1; 

Gdzie SET FOREIGN_KEY_CHECKS=1służy jako dodatkowy środek bezpieczeństwa ...

Karlis Rode
źródło
2
Dla tych, którzy tworzą zrzut przy użyciu phpMyAdmina, dostępna jest opcja „Wyłącz sprawdzanie klucza obcego”, która automatycznie zostanie dodana SET FOREIGN_KEY_CHECKS=0;na początku zrzutu.
Mike
Wygląda na to, że phpMyAdmin zaimplementował tę cudowną funkcję, teraz czekam, aż mysqlWorkbench zrobi to samo! :)
Karlis Rode
@CodeMed FYI, zaakceptowałem odpowiedź MarkR, ponieważ zawiera ona wyjaśnienie problemu, które ma sens - chociaż przyznaję, że nie mogłem tego zweryfikować, ponieważ nie napotkałem tego samego problemu przez następne 6 lat, ani razu. Ta i wcześniejsze odpowiedzi stanowią obejście (świetne do tego celu), ale tak naprawdę nie odnoszą się do samego pytania, a ponieważ możesz zaakceptować tylko jedną odpowiedź, którą musiałem wybrać.
Álvaro González
1
Ostrzeżenie: to nie jest rozwiązanie, a jedynie obejście leniwego człowieka. Po użyciu tego (z rekordami w niektórych innych tabelach wskazujących na usuniętą tabelę), zauważysz wiszące klucze obce, co fatalnie narusza spójność ( C w ACID ) bazy danych, a aplikacje zaczną rzucać wyjątki w każdym miejscu. Zostałeś ostrzeżony.
bekce,
Chociaż jestem pewien, że ostrzeżenie Bekce należy zrozumieć i wziąć pod uwagę, to rozwiązanie zadziałało dla mnie w sytuacji, w której byłem pewien, że porzucałem również wszystkie tabele, które wskazywały na tabele z kłopotliwymi ograniczeniami klucza obcego.
user1147171
47

Wyłącz sprawdzanie klucza obcego

SET FOREIGN_KEY_CHECKS=0
Flakron Bytyqi
źródło
62
Wydaje się, SET FOREIGN_KEY_CHECKS=0że jest to poprawne polecenie i naprawia komunikat o błędzie. Czy masz pojęcie, dlaczego jest to wymagane? Czy klucze obce są buforowane nawet po usunięciu tabel?
Álvaro González
1
Cóż, prawdę mówiąc, nie mam pojęcia, dlaczego pojawia się taki problem, ale upewnij się, że wyłączasz sprawdzanie kluczy za każdym razem, gdy wprowadzasz duże zmiany lub aktualizacje. Zdarzyło mi się to kilka razy, pozostawiając mnie bez snu na wiele dni.
Flakron Bytyqi
55
Upewnij się, że SET FOREIGN_KEY_CHECKS=1;po zakończeniu!
pedro_sland
5
Podczas korzystania z przeglądarki MySQL Query Browser lub phpMyAdmin wydaje się, że dla każdego zapytania otwierane jest nowe połączenie ( bugs.mysql.com/bug.php?id=8280 ), co sprawia, że ​​konieczne jest zapisanie wszystkich instrukcji drop w jednym zapytaniu, np. SET FOREIGN_KEY_CHECKS=0; DROP TABLE my_first_table_to_drop; DROP TABLE my_second_table_to_drop; SET FOREIGN_KEY_CHECKS=1; Gdzie SET FOREIGN_KEY_CHECKS = 1 służy jako dodatkowy środek bezpieczeństwa ...
Karlis Rode,
1
@KarlisRode, Bravo za komentarz dotyczący phpMyAdmin. Gdybyś to ujął jako odpowiedź, dałbym jej +1.
Sablefoste
28

z tego bloga :

Możesz tymczasowo wyłączyć sprawdzanie kluczy obcych:

SET FOREIGN_KEY_CHECKS=0;

Po prostu pamiętaj, aby je przywrócić, gdy skończysz się bawić:

SET FOREIGN_KEY_CHECKS=1;
JackD
źródło
Dobra odpowiedź, ponieważ rozwijałem się lokalnie :)
Adelin
Jest to poprawne obejście (mogę potwierdzić, że działa), ale link do wpisu w blogu tak naprawdę nie mówi o scenariuszu w tym pytaniu (baza danych, która jest już pusta, ale dla jednej tabeli).
Álvaro González
6

miejmy nadzieję, że to działa

SET Foreign_key_checks = 0; DROP TABLE table name; SET Foreign_key_checks = 1;

M_ Fa
źródło
Tak, to działa, jak już wielokrotnie wspominano ;-)
Álvaro González
1

Na Railsach można wykonać następujące czynności używając rails console:

connection = ActiveRecord::Base.connection
connection.execute("SET FOREIGN_KEY_CHECKS=0;")
yeyo
źródło
0

Być może wcześniej wystąpił błąd podczas pracy z tą tabelą. Możesz zmienić nazwę tabeli i spróbować ponownie ją usunąć.

ALTER TABLE `area` RENAME TO `area2`;
DROP TABLE IF EXISTS `area2`;
Vadim Pluzhinsky
źródło
0

Znalazłem proste rozwiązanie, wyeksportuj bazę danych, edytuj to, co chcesz edytować w edytorze tekstu, a następnie zaimportuj. Gotowe

Abdulrahman K.
źródło
4
To ciekawe rozwiązanie, które prawdopodobnie nie powinno się wydarzyć. Zamiast tego wszystko, co należy zmienić, powinno być zrobione za pośrednictwem DBMS. Edycja zrzutu bazy danych w edytorze tekstu wydaje się być dojrzałą drogą do problemów.
Brandon Anzaldi
1
Naprawdę nie rozumiem, co cię kręci. Zrzucenie bazy danych, usunięcie CREATE TABLEkodu i ponowne wczytanie zrzutu ... nie spowoduje usunięcia tabeli przez MySQL. A jeśli masz na myśli przywrócenie zrzutu w nowej bazie danych ... Jeśli chcesz wyczyścić wszystkie tabele, tak jak ja, nowo utworzona baza danych będzie już pusta. Jeśli chcesz zachować niektóre tabele, SET FOREIGN_KEY_CHECKS=0obejście wymienione wszędzie tutaj działa dobrze i jest prostsze; i prawdopodobnie i tak nie musisz edytować zrzutu, ponieważ nowa kopia danych prawdopodobnie nie będzie miała niezsynchronizowanego słownika danych.
Álvaro González
-1

Nie można usunąć ani zaktualizować wiersza nadrzędnego: ograniczenie klucza obcego nie powiodło się ( table1. user_role, OGRANICZANIE FK143BF46A8dsfsfds@#5A6BD60KLUCZA OBCEGO ( user_id) REFERENCES user(id ))

Co zrobiłem w dwóch prostych krokach. najpierw usuwam wiersz podrzędny w tabeli podrzędnej, np

mysql> usuń z tabeli 2, gdzie role_id = 2 && user_id = 20;

Zapytanie OK, dotyczy 1 wiersza (0,10 s)

a drugi krok to usunięcie rodzica

usuń z tabeli1, gdzie id = 20;

Zapytanie OK, dotyczy 1 wiersza (0,12 s)

W ten sposób rozwiązuję problem, który oznacza Usuń dziecko, a następnie Usuń rodzica

Mam nadzieję, że masz to. :)

Aadil Masavir
źródło
Przeczytaj ponownie pytanie. Nie możesz usunąć tabeli, która nie istnieje.
Álvaro González
w tym scenariuszu możemy usunąć ograniczenie klucza obcego, a następnie spróbować usunąć tabelę. możemy upuścić klucz obcy w ten sposób ALTER TABLE <TABLE_NAME> DROP CONSTRAINT <FOREIGN_KEY_NAME>
Aadil Masavir