Không thể thay đổi cột được sử dụng trong ràng buộc khóa ngoại


111

Tôi gặp lỗi này khi cố gắng thay đổi bảng của mình.

Error Code: 1833. Cannot change column 'person_id': used in a foreign key constraint 'fk_fav_food_person_id' of table 'table.favorite_food'

Đây là BÁO CÁO TẠO BẢNG của tôi Đã chạy thành công.

CREATE TABLE favorite_food(
    person_id SMALLINT UNSIGNED,
    food VARCHAR(20),
    CONSTRAINT pk_favorite_food PRIMARY KEY(person_id,food),
    CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

Sau đó, tôi đã cố gắng thực hiện câu lệnh này và gặp lỗi ở trên.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

4
Ví dụ trên là từ cuốn sách "Học SQL, xuất bản lần thứ 2". Tôi mong tác giả, Alan Beaulieu sửa chữa.
Dmitry

Câu trả lời:


126

Loại và định nghĩa của trường khóa ngoại và tham chiếu phải bằng nhau. Điều này có nghĩa là khóa ngoại của bạn không cho phép thay đổi loại trường của bạn.

Một giải pháp sẽ là:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

ALTER TABLE favorite_food
    DROP FOREIGN KEY fk_fav_food_person_id,
    MODIFY person_id SMALLINT UNSIGNED;

Bây giờ bạn có thể thay đổi bạn person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

tạo lại khóa ngoại

ALTER TABLE favorite_food
    ADD CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
          REFERENCES person (person_id);

UNLOCK TABLES;

CHỈNH SỬA: Đã thêm khóa ở trên, cảm ơn các ý kiến

Bạn phải không cho phép ghi vào cơ sở dữ liệu trong khi thực hiện việc này, nếu không, bạn có nguy cơ gặp sự cố về tính toàn vẹn dữ liệu.

Tôi đã thêm một khóa ghi ở trên

Tất cả các truy vấn viết trong bất kỳ phiên nào khác với phiên của bạn ( INSERT, UPDATE, DELETE) sẽ đợi đến khi hết giờ hoặc UNLOCK TABLES; được thực hiện

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

CHỈNH SỬA 2: OP đã yêu cầu giải thích chi tiết hơn về dòng "Loại và định nghĩa của trường khóa ngoại và tham chiếu phải bằng nhau. Điều này có nghĩa là khóa ngoại của bạn không cho phép thay đổi loại trường của bạn."

Từ Hướng dẫn tham khảo MySQL 5.5: Các ràng buộc về chìa khóa ngoại lai

Các cột tương ứng trong khóa ngoại và khóa được tham chiếu phải có kiểu dữ liệu nội bộ tương tự bên trong InnoDB để chúng có thể được so sánh mà không cần chuyển đổi kiểu. Kích thước và dấu của các kiểu số nguyên phải giống nhau. Độ dài của các loại chuỗi không được giống nhau. Đối với cột chuỗi không nhị phân (ký tự), bộ ký tự và đối chiếu phải giống nhau.


1
Đừng quên sử dụng một giao dịch cho việc này. Nếu không, cơ sở dữ liệu của bạn có thể bị hỏng.
Francois Bourgeois

5
Điểm tốt, rất tiếc là MySQL không hỗ trợ các giao dịch xung quanh các câu lệnh DDL. Giao dịch mở được cam kết trước khi một truy vấn DDL được thực hiện see dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim

2
Câu lệnh đúng để tạo lại khóa ngoại sẽ là: ALTER TALE favourite_food THÊM HỢP ĐỒNG fk_fav_food_woman_id FOREIGN KEY (person_id) THAM KHẢO người (id);
Felizardo

1
Tại sao bạn sửa đổi person_idngay sau khi bỏ khóa ngoại? Có vẻ như bạn đã không thay đổi bất cứ điều gì vì nó đã là một SMALLINT UNSIGNED.
Dennis Subachev

1
Chúng tôi không biết nó là gì vì anh ấy chỉ đăng cấu trúc bảng tham chiếu. Innodb có int là loại nội bộ, smallint, v.v. chỉ là các phím tắt
Michel Feldheim,

198

Bạn có thể tắt kiểm tra khóa ngoại:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Hãy đảm bảo KHÔNG sử dụng cái này trong quá trình sản xuất và có bản sao lưu.


1
Có vẻ là giải pháp rất không an toàn. Nó có thể dẫn đến mất toàn vẹn dữ liệu không?
hrust

@Synaps - có, nó có thể xảy ra nếu bạn đang xóa / cập nhật / chèn. mất dữ liệu sẽ không xảy ra nếu bạn chỉ sửa đổi một bảng hoặc gieo hạt db của bạn, mặt khác bạn nên xác nhận bằng tay dữ liệu của bạn (kể từ khi bạn loại bỏ những hạn chế)
Dementic

2
Giải pháp này rất tuyệt và bạn có thể sử dụng nó trong sản xuất nếu bạn viết khóa quảng cáo trước khi sửa đổi dữ liệu của mình và sau đó mở khóa khi bạn hoàn tất. Sử dụng tệp sql để thực hiện các thay đổi của bạn trong khoảng thời gian ngắn nhất có thể sẽ tốt hơn.
Francisco Zarabozo

Giải pháp tốt để điều chỉnh nhanh
Genaut

1
Bạn không cần khóa như SET FOREIGN_KEY_CHECKSphạm vi phiên (các phiên khác sẽ vẫn áp dụng ràng buộc FK). Nó hoàn hảo cho việc thêm / gỡ bỏ AUTO_INCREMENT(mà không thay đổi kiểu dữ liệu cột thực tế), nhưng nó sẽ không làm việc nếu bạn cố gắng để thay đổi kiểu dữ liệu cột cho "thật" (nói, từ SMALLINT để INT) như bạn sẽ nhận được một hợp pháp 150 FK constraint incorrectly formedkhi mysql cố gắng thay thế bảng cũ bằng bảng mới. Trong trường hợp đó, hãy sử dụng câu trả lời được chấp nhận.
Xenos

-3

Khi bạn đặt khóa (chính hoặc khóa ngoài), bạn đang thiết lập các ràng buộc về cách chúng có thể được sử dụng, điều này sẽ hạn chế những gì bạn có thể làm với chúng. Nếu bạn thực sự muốn thay đổi cột, bạn có thể tạo lại bảng mà không bị ràng buộc, mặc dù tôi khuyên bạn không nên làm như vậy. Nói chung, nếu bạn gặp phải tình huống bạn muốn làm điều gì đó, nhưng nó bị chặn bởi một hạn chế, tốt nhất hãy giải quyết bằng cách thay đổi những gì bạn muốn làm thay vì bị ràng buộc.


12
Đây là ĐÓ một, câu trả lời vô ích vô ích!
ajmedway

3
@ajmedway Sau đó, bạn có thể viết một câu trả lời hữu ích mà không đổ lỗi cho những người dùng khác
Tôi là Hầu hết ngốc Person

2
@IamtheMostStupidPerson Nó tốt hơn nhiều so với việc chỉ từ chối mà không có bình luận. Ít nhất thì người bình luận cũng có thể đoán được tại sao lại phản đối. Những câu trả lời chung chung như "tốt hơn là an toàn hơn xin lỗi" không hữu ích.
Csaba Toth
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.