Không thể xóa hoặc cập nhật hàng cha mẹ: ràng buộc khóa ngoại


170

Khi làm:

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

Nó bị lỗi:

#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))

Dưới đây là bảng của tôi:

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`);

Câu trả lời:


108

Như vậy, bạn phải xóa hàng ra khỏi bảng nhà quảng cáo trước khi bạn có thể xóa hàng trong bảng công việc mà nó tham chiếu. Điều này:

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

... Thực sự trái ngược với những gì nó nên có. Như vậy, điều đó có nghĩa là bạn phải có một bản ghi trong bảng công việc trước các nhà quảng cáo. Vì vậy, bạn cần sử dụng:

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

Khi bạn sửa mối quan hệ khóa ngoài, câu lệnh xóa của bạn sẽ hoạt động.


3
Trong dòng đầu tiên: bạn không nghĩ rằng nó phải là "rằng nó tham chiếu" thay vì "tham chiếu nó"? Hoặc tôi đã hiểu nhầm thuật ngữ tài liệu tham khảo được cho là hoạt động như thế nào?
Abraham Philip

6
@AbrahamPhilip Tôi cũng nghĩ như vậy. nhà quảng cáo tham khảo công việc.
keyer

270

Cách đơn giản là vô hiệu hóa kiểm tra khóa ngoại; thực hiện các thay đổi sau đó kích hoạt lại kiểm tra khóa ngoại.

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

171
Đây không phải là một giải pháp cho vấn đề, mà là một công việc bẩn thỉu có thể không được mong muốn.
madfriend

20
Trong trường hợp của tôi: Tôi vừa chạy một tệp SQL lớn và một trong những câu lệnh cuối cùng không thành công, vì vậy tôi chỉ muốn xóa tất cả các bảng, sửa lỗi cú pháp và chạy lại, làm cho nó chính xác những gì tôi đang tìm kiếm.
ekerner

1
Nếu bạn sẽ làm điều này, tại sao không loại bỏ tất cả các ràng buộc?
Sablefoste

1
Nó rất hữu ích khi làm một cái gì đó như:REPLACE INTO tab_with_constraint ...
Maciek oziński

5
Lý do duy nhất để nâng cao câu trả lời này là nếu bạn chỉ muốn mã của mình ngừng la hét với bạn và rèn sâu hơn vào spaghetti mà không hiểu mã bạn đang viết. Lý do để có khóa ngoại ở vị trí đầu tiên là để thực thi tính toàn vẹn tham chiếu. Nếu bạn cần vô hiệu hóa chúng để tắt mã, có lẽ bạn muốn suy nghĩ lại về các khóa ngoại của mình, thay vì vô hiệu hóa chúng.
cytinus

38

Theo thiết kế hiện tại (có thể thiếu sót) của bạn, bạn phải xóa hàng ra khỏi bảng quảng cáo trước khi bạn có thể xóa hàng trong bảng công việc mà nó tham chiếu.

Ngoài ra, bạn có thể thiết lập khóa ngoại của mình sao cho việc xóa trong bảng cha sẽ khiến các hàng trong bảng con bị xóa tự động. Điều này được gọi là xóa tầng. Nó trông giống như thế này:

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

Như đã nói, như những người khác đã chỉ ra, khóa ngoại của bạn có cảm giác như nó sẽ đi theo hướng khác vì bảng quảng cáo thực sự chứa khóa chính và bảng công việc chứa khóa ngoại. Tôi sẽ viết lại như thế này:

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

Và xóa tầng sẽ không cần thiết.


18

Nếu bạn muốn thả một bảng, bạn nên thực hiện truy vấn sau trong một bước duy nhất

THIẾT LẬP FOREIGN_KEY_CHECKS = 0; DROP BẢNG tên_bảng;


13

Tôi đã thử giải pháp được đề cập bởi @Alino Manzi nhưng nó không hoạt động với tôi trên các bảng liên quan đến WordPress bằng wpdb.

sau đó tôi sửa đổi mã như dưới đây và nó hoạt động

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

Tôi nghĩ rằng khóa ngoại của bạn là ngược. Thử:

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

5

Nếu có nhiều hơn một công việc có cùng một nhà quảng cáo_id, thì khóa ngoại của bạn sẽ là:

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

Mặt khác (nếu theo cách khác trong trường hợp của bạn), nếu bạn muốn các hàng trong nhà quảng cáo sẽ tự động bị xóa nếu hàng trong công việc bị xóa, hãy thêm tùy chọn 'ON DELETE CASCADE' vào cuối khóa ngoại của bạn:

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

Kiểm tra các ràng buộc khóa ngoài


3

Bạn cần xóa nó theo thứ tự Có các phụ thuộc trong các bảng


2

Khi bạn tạo cơ sở dữ liệu hoặc tạo bảng

Bạn nên thêm dòng đó ở đầu tập lệnh tạo cơ sở dữ liệu hoặc bảng

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

Bây giờ bạn muốn xóa hồ sơ từ bảng? sau đó bạn viết như

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

Chúc may mắn!


2

Làm thế nào về giải pháp thay thế này tôi đã sử dụng: cho phép khóa ngoại là NULL và sau đó chọn BẬT XÓA THIẾT BỊ .

Cá nhân tôi thích sử dụng cả " TRÊN CẬP NHẬT CASCADE " cũng như " TRÊN XÓA BÀI TẬP " để tránh các biến chứng không cần thiết, nhưng trên thiết lập của bạn, bạn có thể muốn một cách tiếp cận khác. Ngoài ra, các giá trị khóa ngoài của NULL có thể dẫn đến các biến chứng vì bạn sẽ không biết chính xác điều gì đã xảy ra ở đó. Vì vậy, thay đổi này phải liên quan chặt chẽ đến cách mã ứng dụng của bạn hoạt động.

Hi vọng điêu nay co ich.


2

Tôi đã gặp vấn đề này trong việc di chuyển laravel
thứ tự các bảng thả trong phương thức down () không thành vấn đề

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

có thể không hoạt động, nhưng nếu bạn thay đổi thứ tự, nó hoạt động.

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

1

nếu bạn cần hỗ trợ khách hàng càng sớm càng tốt và không có quyền truy cập vào

FOREIGN_KEY_CHECKS

để toàn vẹn dữ liệu có thể bị vô hiệu hóa:

1) xóa khóa ngoại

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

2) kích hoạt thao tác xóa của bạn thông qua sql hoặc api

3) thêm khóa ngoại trở lại lược đồ

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

tuy nhiên, đây là một sửa chữa nóng, vì vậy nó có nguy cơ của riêng bạn, bởi vì lỗ hổng chính của cách tiếp cận như vậy là cần thiết sau đó để giữ tính toàn vẹn dữ liệu theo cách thủ công.


0

Bạn có thể tạo một kích hoạt để xóa các hàng được tham chiếu trước khi xóa công việc.

    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

Vấn đề chính với erorr Error Code: 1451. Cannot delete or update a parent row: a foreign key constraint failsnày là nó không cho bạn biết bảng nào chứa lỗi FK, vì vậy rất khó để giải quyết xung đột.

Nếu bạn sử dụng MySQL hoặc tương tự, tôi phát hiện ra rằng bạn có thể tạo sơ đồ ER cho cơ sở dữ liệu của mình, sau đó bạn có thể xem xét và xóa an toàn mọi xung đột gây ra lỗi.

  1. Sử dụng bàn làm việc của MySQL
  2. Nhấp vào Cơ sở dữ liệu -> Kỹ thuật đảo ngược
  3. Chọn một chính xác connection
  4. Tiếp theo cho đến khi kết thúc, hãy nhớ chọn databasetablescần kiểm tra
  5. Bây giờ bạn có sơ đồ ER, bạn có thể xem bảng nào có xung đột FK

0

Về cơ bản, lý do đằng sau các loại lỗi này cuối cùng là bạn đang cố xóa một tupple có khóa chính (bảng gốc) và khóa chính đó được sử dụng trong bảng con làm khóa ngoại. Trong trường hợp này để xóa dữ liệu bảng cha, bạn phải xóa dữ liệu bảng con (trong đó sử dụng khóa ngoại). Cảm ơn


0

Điều này cũng xảy ra với tôi và do sự phụ thuộc và tham chiếu từ các bảng khác, tôi không thể xóa mục nhập. Những gì tôi đã làm là thêm một cột xóa (loại boolean) vào bảng. Giá trị trong trường đó cho biết mục có được đánh dấu để xóa hay không. Nếu được đánh dấu để xóa, không tìm nạp / sử dụng; nếu không, sử dụng nó.


-1

Có lẽ bạn nên thử TRÊN XÓA CASCADE


34
Một cách mù quáng thêm một tầng xóa (sẽ phá hủy dữ liệu) mà không hiểu vấn đề chỉ là điều tồi tệ nhất mà người ta có thể làm.
Tom H
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.