2009-12-15 31 views
94

Khi thực hiện:Không thể xóa hoặc cập nhật hàng mẹ: một ràng buộc khoá ngoại thất bại

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

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

Trả lời

65

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ự ngược lại với những gì cần thiết. Vì nó có nghĩa là bạn phải có một bản ghi trong bảng việc làm trước các nhà quảng cáo. Vì vậy, bạn cần phải 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, báo cáo xóa của bạn sẽ hoạt động.

+2

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

+5

@AbrahamPhilip Tôi đã suy nghĩ cùng một điều. nhà quảng cáo tham chiếu đến công việc. – keyser

24

Theo bạn thiết kế hiện tại (có thể thiếu sót), bạn phải xóa hàng ra khỏi bảng nhà quảng cáo trước 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 bạn sao cho xóa trong bảng cha làm cho 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ư sau:

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

Có nói rằng, như những người khác đã chỉ ra, chính nước ngoài của bạn cảm thấy như nó nên đi theo con đường khác xung quanh kể từ khi 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 nó như sau:

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

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

+1

Asaph, sửa tôi nếu tôi sai, nhưng sẽ không có nhiều công việc cho mỗi nhà quảng cáo? –

+0

@Rashmi: Bạn chính xác –

+0

@Rashmi Pandit: Tôi đã thêm thảo luận thêm vào câu trả lời của tôi để giải quyết vấn đề đó. – Asaph

3

Nếu có nhiều hơn một công việc có advertiser_id cùng, sau đó khóa ngoại của bạn nên là:

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

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

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

Kiểm tra của bạn ra Foreign Key constraints

5

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

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

Có lẽ bạn nên thử ON DELETE CASCADE

+26

Một cách mù quáng khi thêm một lần xóa tầng (sẽ hủy dữ liệu) mà không hiểu vấn đề là điều tồi tệ nhất mà người ta có thể làm. –

1

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

Bạn nên thêm dòng đó vào kịch bản đầu tạo cơ sở dữ liệu hoặc bảng

SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1; 

Bây giờ bạn muốn xóa bản ghi khỏi bảng?sau đó bạn viết là

SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1; 
DELETE FROM `jobs` WHERE `job_id` =1 LIMIT 1 

Chúc may mắn!

150

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 rồi bậ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 
+92

Đâ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 xung quanh có thể không được mong muốn. – madfriend

+11

Trong trường hợp của tôi: Tôi chỉ chạy một tệp SQL lớn và một trong các 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 chính xác những gì tôi đang tìm kiếm. – ekerner

+1

Nếu bạn định làm điều này, tại sao không chỉ loại bỏ tất cả các ràng buộc? – Sablefoste

0

Bạn cần phải xóa nó bằng cách trật tự Có sự phụ thuộc trong các bảng

0

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 

nên tính 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 hoạt động xóa bạn thruogh sql hoặc api

3) thêm khóa ngoại trở lại schema

ALTER TABLE `advertisers` 
    ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`); 
tuy nhiên

, nó là một hot-sửa chữa, vì vậy nó là một mình rủi ro, bởi vì lỗ hổng chính của cách tiếp cận này là cần thiết sau đó để giữ cho toàn vẹn dữ liệu theo cách thủ công.

6

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

SET FOREIGN_KEY_CHECKS = 0; DROP TABLE table_name;

1

Làm thế nào về sự thay thế này, tôi đã sử dụng: cho phép người nước ngoài chính là NULL và sau đó chọn ON DELETE SET NULL.

Cá nhân tôi thích sử dụng cả "ON UPDATE CASCADE" cũng như "ON SET NULL DELETE" để tránh những biến chứng không cần thiết, nhưng trên của bạn thiết lập bạn có thể muốn có một cách tiếp cận khác nhau. 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 do bạn không biết chính xác những 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.

Hy vọng điều này sẽ hữu ích.

2

Tôi có vấn đề này trong laravel di cư quá
thứ tự của bảng giảm xuống() phương pháp không thành vấn đề

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

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

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

Bạn có thể tạo trình 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 ; 
Các vấn đề liên quan