Неможливо видалити або оновити батьківський рядок: помилка іноземного ключа виходить з ладу


170

При виконанні:

DELETE FROM `jobs` WHERE `job_id` =1 LIMIT 1 

Це помилки:

#1451 - Cannot delete or update a parent row: a foreign key constraint fails 
(paymesomething.advertisers, CONSTRAINT advertisers_ibfk_1 FOREIGN KEY 
(advertiser_id) REFERENCES jobs (advertiser_id))

Ось мої таблиці:

CREATE TABLE IF NOT EXISTS `advertisers` (
  `advertiser_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `password` char(32) NOT NULL,
  `email` varchar(128) NOT NULL,
  `address` varchar(255) NOT NULL,
  `phone` varchar(255) NOT NULL,
  `fax` varchar(255) NOT NULL,
  `session_token` char(30) NOT NULL,
  PRIMARY KEY (`advertiser_id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;


INSERT INTO `advertisers` (`advertiser_id`, `name`, `password`, `email`, `address`, `phone`, `fax`, `session_token`) VALUES
(1, 'TEST COMPANY', '', '', '', '', '', '');

CREATE TABLE IF NOT EXISTS `jobs` (
  `job_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `advertiser_id` int(11) unsigned NOT NULL,
  `name` varchar(255) NOT NULL,
  `shortdesc` varchar(255) NOT NULL,
  `longdesc` text NOT NULL,
  `address` varchar(255) NOT NULL,
  `time_added` int(11) NOT NULL,
  `active` tinyint(1) NOT NULL,
  `moderated` tinyint(1) NOT NULL,
  PRIMARY KEY (`job_id`),
  KEY `advertiser_id` (`advertiser_id`,`active`,`moderated`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;


INSERT INTO `jobs` (`job_id`, `advertiser_id`, `name`, `shortdesc`, `longdesc`, `address`, `active`, `moderated`) VALUES
(1, 1, 'TEST', 'TESTTEST', 'TESTTESTES', '', 0, 0);

ALTER TABLE `advertisers`
  ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`);

Відповіді:


108

Так, ви повинні видалити рядок із таблиці рекламодавців, перш ніж ви зможете видалити рядок із таблиці завдань, на яку він посилається. Це:

ALTER TABLE `advertisers`
  ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) 
      REFERENCES `jobs` (`advertiser_id`);

... насправді є протилежним тому, що має бути. Це означає, що вам доведеться мати запис у таблиці завдань перед рекламодавцями. Тому вам потрібно використовувати:

ALTER TABLE `jobs`
  ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) 
      REFERENCES `advertisers` (`advertiser_id`);

Після виправлення відносин із зовнішнім ключем ваше повідомлення про видалення запрацює.


3
У першому рядку: ти не вважаєш, що це повинно бути «що воно посилається», а не «що посилається на нього»? Або я неправильно зрозумів, як повинна працювати термінологія посилань?
Авраам Філіп

6
@AbrahamPhilip Я думав те саме. рекламодавці посилаються на вакансії.
клавішник

270

Найпростішим способом було б відключити перевірку зовнішнього ключа; внесіть зміни, а потім повторно увімкніть перевірку зовнішнього ключа.

SET FOREIGN_KEY_CHECKS=0; -- to disable them
SET FOREIGN_KEY_CHECKS=1; -- to re-enable them

171
Це не вирішення проблеми, а скоріше брудна робота, яка може бути не бажаною.
коханий

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

1
Якщо ви збиралися це зробити, то чому б просто не усунути всі обмеження?
Sablefoste

1
Це корисно, роблячи щось на кшталт:REPLACE INTO tab_with_constraint ...
Maciek Łoziński

5
Єдина причина, щоб підкріпити цю відповідь, це якщо ви просто хочете, щоб ваш код перестав кричати на вас і занурюватися глибше в спагетті, не розуміючи код, який ви пишете. Підставою для того, щоб в першу чергу мати іноземні ключі, - це забезпечити референтну цілісність. Якщо вам потрібно відключити їх, щоб зробити код закритим, ви, ймовірно, хочете переглянути свої закордонні ключі, а не вимикати їх.
цитинус

38

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

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

ALTER TABLE `advertisers`
ADD CONSTRAINT `advertisers_ibfk_1`
FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`)
ON DELETE CASCADE;

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

ALTER TABLE `jobs`
ADD FOREIGN KEY (`advertiser_id`) REFERENCES `advertisers` (`advertiser_id`);

І каскадне видалення не знадобиться.


18

Якщо ви хочете опустити таблицю, слід виконати наступний запит за один крок

SET FOREIGN_KEY_CHECKS = 0; DROP TABLE_ ім'я таблиці;


13

Я спробував рішення, згадане @Alino Manzi, але воно не працювало для мене на таблицях, пов’язаних з WordPress, використовуючи wpdb.

потім я змінив код, як показано нижче, і він працював

SET FOREIGN_KEY_CHECKS=OFF; //disabling foreign key

//run the queries which are giving foreign key errors

SET FOREIGN_KEY_CHECKS=ON; // enabling foreign key

6

Я думаю, що ваш зовнішній ключ назад. Спробуйте:

ALTER TABLE 'jobs'
ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `advertisers` (`advertiser_id`)

5

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

ALTER TABLE `jobs`
ADD CONSTRAINT `advertisers_ibfk_1` 
FOREIGN KEY (`advertiser_id`) 
REFERENCES `advertisers` (`advertiser_id`);

В іншому випадку (якщо у вашому випадку це навпаки), якщо ви хочете, щоб рядки в рекламодавці автоматично видалялися, якщо рядок із завданням видалено, додайте опцію "НАДОБРИТИ КАСКАД" до кінця закордонного ключа:

ALTER TABLE `advertisers`
ADD CONSTRAINT `advertisers_ibfk_1` 
FOREIGN KEY (`advertiser_id`) 
REFERENCES `jobs` (`advertiser_id`)
ON DELETE CASCADE;

Ознайомтеся з обмеженнями зовнішніх ключів



2

Коли ви створюєте базу даних або створюєте таблиці

Ви повинні додати цей рядок у верхньому сценарії створення бази даних або таблиці

SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1;

Тепер ви хочете видалити записи з таблиці? то ви пишете як

SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1;
DELETE FROM `jobs` WHERE `job_id` =1 LIMIT 1

Удачі!


2

Як щодо цієї альтернативи, яку я використовував: дозвольте зовнішньому ключу бути NULL, а потім виберіть ON DELETE SET NULL .

Особисто я вважаю за краще використовувати " ON UPDATE CASCADE ", а також " ON DELETE SET NULL ", щоб уникнути зайвих ускладнень, але у вашому налаштуванні ви можете захотіти іншого підходу. Крім того, NULL'ing іноземних ключових значень може призвести до ускладнень, оскільки ви не знатимете, що саме там сталося. Тож ця зміна має бути тісно пов'язана з тим, як працює ваш додаток.

Сподіваюся, це допомагає.


2

Я мав цю проблему в міграції Laravel занадто
ПОРЯДКОВИЙ краплинні таблиць в пуху () метод має значення

Schema::dropIfExists('groups');
Schema::dropIfExists('contact');

може не працювати, але якщо ви зміните замовлення, воно працює.

Schema::dropIfExists('contact');
Schema::dropIfExists('groups');

1

якщо вам потрібно якомога швидше підтримати клієнта, і не маєте доступу до нього

FOREIGN_KEY_CHECKS

щоб цілісність даних була відключена:

1) видалити зовнішній ключ

ALTER TABLE `advertisers` 
DROP FOREIGN KEY `advertisers_ibfk_1`;

2) активуйте операцію видалення через thruogh sql або api

3) додайте зовнішній ключ назад до схеми

ALTER TABLE `advertisers`
  ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`);

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


0

Ви можете створити тригер для видалення згаданих рядків перед видаленням завдання.

    DELIMITER $$
    CREATE TRIGGER before_jobs_delete 
        BEFORE DELETE ON jobs
        FOR EACH ROW 
    BEGIN
        delete from advertisers where advertiser_id=OLD.advertiser_id;
    END$$
    DELIMITER ;

0

Основна проблема цього ерора Error Code: 1451. Cannot delete or update a parent row: a foreign key constraint failsполягає в тому, що він не дає вам знати, в якій таблиці міститься збій FK, тому важко вирішити конфлікт.

Якщо ви використовуєте MySQL або подібне, я з’ясував, що ви можете створити ER-діаграму для своєї бази даних, то ви можете переглянути та безпечно усунути будь-які конфлікти, що викликають помилку.

  1. Використовуйте робочу версію MySQL
  2. Клацніть База даних -> Зворотна інженерія
  3. Виберіть правильний connection
  4. Далі до кінця не забудьте вибрати database& tablesщо потрібно вивчити
  5. Тепер у вас є діаграма ER, ви можете бачити, у якій таблиці конфлікт FK

0

В основному, причиною помилок такого типу є врешті-решт, ви намагаєтесь видалити tupple, у якого є первинний ключ (коренева таблиця), і цей первинний ключ використовується в дочірній таблиці як зовнішній ключ. У цьому сценарії для того, щоб видалити дані батьківської таблиці, потрібно видалити дані дочірньої таблиці (в яких використовується зовнішній ключ). Дякую


0

Це сталося і зі мною, і через залежність та посилання з інших таблиць я не зміг видалити запис. Що я зробив, додав стовпчик видалення (типу булевого типу) до таблиці. Значення в цьому полі показало, позначений чи пункт для видалення чи ні. Якщо позначено для видалення, не виймайте / не використовуйте; в іншому випадку використовуйте його.


-1

Можливо, вам варто спробувати НАДОБРИТИ КАСКАД


34
Сліпо додавання каскадного видалення (яке знищить дані) без розуміння проблеми - це лише найгірше, що можна було зробити.
Том Н
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.