Як тимчасово відключити обмеження іноземного ключа в MySQL?


651

Чи можливо тимчасово відключити обмеження в MySQL?

У мене є дві моделі Django, кожна з яких є ForeignKey до іншої. Видалення екземплярів моделі повертає помилку через обмеження 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()

Чи можливо тимчасово відключити обмеження та видалити їх все одно?


3
Або я не отримую те, що ти хочеш зробити, або те, що ти намагаєшся робити, дуже, дуже, дуже некрасиво . Навіть якщо ви можете це зробити, ви, мабуть, не повинні.
Даріуш

3
Видалення і повторне FK є зміна вашого дб. Ви намагаєтеся не допустити самих обмежень, які дозволяють системі бачити певний сенс, вона не має відношення до того, що ФК може бути тимчасовою справою, і якби він знав, це панікуватиме.
Грант Томас

1
Дивно, що ти намагаєшся зробити. Але яку базу даних ви використовуєте?
andrefsp

4
що робити, якщо замість того, щоб вимкнути обмеження, ви остаточно його змінили ON DELETE SET NULL? Це дозволить зробити подібне, і вам не доведеться включати та вимикати перевірку ключів.
dnagirl

1
@dnagirl: справді було б краще. Як я можу це зробити?
липень

Відповіді:


1466

Спробуйте DISABLE KEYSабо

SET FOREIGN_KEY_CHECKS=0;

переконайтесь, що

SET FOREIGN_KEY_CHECKS=1;

після.


14
це щось, що встановлено для mysql в цілому або просто той сеанс?
tipu

28
Я вважаю, що це за сеанс.
Ендрю Кемпбелл

13
serverfault.com/questions/291100/… , Також зауважте, що ви не можете disable keys потрапити до Innodb
Pacerier

1
Чи можу я просто відключити FOREIGN_KEY_CHECKS для однієї таблиці?
jDub9

@Pacerier Зчитавши це, здається, ви можете, але лише за один сеанс.
Бретт

150

Щоб вимкнути обмеження на зовнішній ключ у всьому світі, виконайте наступне:

SET GLOBAL FOREIGN_KEY_CHECKS=0;

і не забудьте повернути його, коли закінчите

SET GLOBAL FOREIGN_KEY_CHECKS=1;

ПОПЕРЕДЖЕННЯ. Це потрібно робити лише тоді, коли ви виконуєте обслуговування одного режиму користувача. Оскільки це може призвести до невідповідності даних. Наприклад, це буде дуже корисно, коли ви завантажуєте велику кількість даних за допомогою виводу mysqldump.


1
це те, що мені потрібно було знати, тому це не велика практика, але ця відповідь, хлопці, повинна
набирати

1
Це спрацювало для мене після того, як спробу "найкращої відповіді" для мене не вийшло. Можливо, пояснення різниці можна було б додати.
hexnet

7
@hexnet Різниця полягає в тому, що просто SET FOREIGN_KEY_CHECKSзмінюється значення для поточного з'єднання , в той час як SET GLOBAL ..змінюється значення для всіх з'єднань , включаючи майбутні з'єднання. Якщо ви просто робите SET FOREIGN..в одному вікні, то спробуйте застосувати оператор у іншому вікні (через інше з'єднання), значення там не змінилося. З GLOBAL, однакова змінна має однакове значення для обох з'єднань.
MatsLindh

Єдине, що могло б мені допомогти при відтворенні більшого дампа (6+ ГБ) <3
Макс

Це не працює для мене. Коли я намагаюся, я бачу:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
Майк Б

53

Я, як правило, відключаю обмеження зовнішніх ключів лише тоді, коли хочу врізати таблицю, і оскільки я постійно повертаюся до цієї відповіді, це для мене на майбутнє:

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

25

Замість того, щоб вимкнути обмеження, назавжди змініть його на ВИМОЖЛИВО ВІДНОВИТИ НАСТУП. Це дозволить зробити подібне, і вам не доведеться включати та вимикати перевірку ключів. Так:

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

Прочитайте це ( http://dev.mysql.com/doc/refman/5.5/uk/alter-table.html ) і це ( http://dev.mysql.com/doc/refman/5.5/uk /create-table-foreign-keys.html ).


7
Остерігайтеся зміни таблиці може зайняти багато часу, краще встановити глобальний сервер FOREIGN_KEY_CHECKSна 0 і повернути його, коли буде зроблена брудна робота. Крім того, він може заблокувати написання ваших таблиць.
Акі

Хіба це не порушить посилання під час зміни типу віддалених стовпців? (Здається, мій клієнт перейменовує змінену таблицю темп на оригінальну назву таблиці.)
Cees Timmerman

15

Щоб вимкнути обмеження зовнішнього ключа в усьому світі:

SET GLOBAL FOREIGN_KEY_CHECKS = 0;

і для активного обмеження зовнішніх ключів

SET GLOBAL FOREIGN_KEY_CHECKS = 1;

10

Дуже просте рішення з phpmyadmin:

  • У таблиці перейдіть на SQLвкладку
  • Після редагування команди SQL, яку ви хочете запустити, поруч є прапорець під GOназвою " Увімкнути перевірку зовнішніх ключів" .
  • Зніміть цей прапорець і запустіть свій SQL . Він буде автоматично повторно перевірений після виконання.

3
Дякую! Насправді рішення SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;не працювало для мене в PHPMyAdmin, оскільки я забув зняти прапорець "Увімкнути прапорець із зовнішнім ключем". У PHPMyAdmin ви можете пропустити ці команди SET і просто зняти прапорець.
Jan

5

Мені просто SET FOREIGN_KEY_CHECKS=0;не вистачало. Я все ще мав com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException.

Довелося додати ALTER TABLE myTable DISABLE KEYS;.

Тому:

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

FYI, mySQL 5.7 кидає попередження, у двигуна InnoDB немає цієї опції при виконанні команди DISABLE KEYS.
jDub9

це спрацювало, без таблиці
альтерів

3

Якщо ключове поле є нульовим, то ви також можете встановити значення null, перш ніж намагатися його видалити:

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

У phpMyAdmin ви можете вибрати кілька рядків і натиснути дію видалення. Ви ввійдете на екран, у якому перераховані запити на видалення, зніміть прапорець із пунктом "Зовнішній ключ" та натисніть "Так", щоб виконати їх.

Це дозволить вам видалити рядки, навіть якщо є обмеження ON DELETE.


-2

Недоцільно встановити обмеження для іноземного ключа на 0, тому що якщо ви це зробите, ваша база даних не гарантує, що вона не порушує референтну цілісність. Це може призвести до неточних, оманливих або неповних даних.

Ви робите зовнішній ключ з причини: тому що всі значення в дочірньому стовпці повинні бути однаковими зі значенням у батьківському стовпчику. Якщо немає зовнішніх ключових обмежень, дочірній рядок може мати значення, яке не знаходиться в батьківському рядку, що призведе до неточних даних.

Наприклад, скажімо, у вас є веб-сайт для входу студентів, і кожен студент повинен зареєструватися для облікового запису як користувач. У вас є одна таблиця для ідентифікаторів користувачів, ідентифікатор користувача як основний ключ; та інша таблиця для облікових записів студентів, із ідентифікатором студента як стовпцем. Оскільки кожен студент повинен мати ідентифікатор користувача, було б доцільно зробити ідентифікатор студента зі таблиці облікових записів студентів зовнішнім ключем, який посилається на ідентифікатор основного ключа в таблиці ідентифікаторів користувача. Якщо немає іноземних ключів, студент може мати ідентифікатор студента та ідентифікатор користувача, а це означає, що студент може отримати обліковий запис, не будучи користувачем, що неправильно.

Уявіть, якщо це трапляється з великою кількістю даних. Ось чому вам потрібна перевірка закордонного ключа.

Найкраще з’ясувати, що викликає помилку. Швидше за все, ви намагаєтесь видалити з батьківського рядка, не видаляючи дочірнього рядка. Спробуйте видалити з дочірнього рядка перед видаленням з батьківського рядка.


Щоправда, завжди є компроміс.
Pacerier

21
Ніхто не каже, щоб запустити його так назавжди. Ви вимикаєте обмеження, масово завантажуєте деякі дані та повертаєте їх назад. Нічого страшного, люди роблять це постійно.
bwawok

це необхідно для оптового імпорту, принаймні для його виконання дуже часто. також іноді вам просто потрібні дані для відновлення, тоді ви можете зробити свої перевірки.
Фірас Абд Алрахман

3
Це не відповідь на запитання.
Корай Тугай

Зауважте, його питання полягає в тому, як це зробити тимчасово. Це потрібно під час певного обслуговування та імпорту даних. Безперечно, що ваші сценарії імпорту стають відповідальними за цілісність даних. Потім, пізніше, коли індекси та обмеження будуть знову ввімкнено, db скаже вам, якщо щось порушено.
mcstar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.