Làm cách nào để tạm thời vô hiệu hóa một ràng buộc khóa ngoại trong MySQL?


651

Có thể tạm thời vô hiệu hóa các ràng buộc trong MySQL?

Tôi có hai mẫu Django, mỗi mẫu có ForeignKey cho mẫu kia. Xóa các phiên bản của một mô hình trả về một lỗi do ràng buộc 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()

Có thể tạm thời vô hiệu hóa các ràng buộc và xóa?


3
Hoặc tôi không nhận được những gì bạn muốn làm, hoặc những gì bạn đang cố gắng làm là rất, rất, rất xấu xí . Ngay cả khi bạn có thể làm điều đó, bạn có thể không nên.
Dariusz

3
Việc bỏ và áp dụng lại FK đang thay đổi db của bạn. Bạn đang cố gắng thách thức chính những ràng buộc cho phép hệ thống thấy được ý nghĩa nào đó, không có liên quan rằng FK có thể là một điều tạm thời, và nếu nó biết, nó sẽ hoảng loạn.
Grant Thomas

1
Thật kỳ lạ những gì bạn đang cố gắng làm. Nhưng cơ sở dữ liệu nào bạn đang sử dụng?
andrefsp

4
Điều gì xảy ra nếu, thay vì vô hiệu hóa ràng buộc của bạn, bạn đã sửa đổi nó vĩnh viễn thành ON DELETE SET NULL? Điều đó sẽ thực hiện một điều tương tự và bạn sẽ không phải bật và tắt khóa kiểm tra.
dnagirl

1
@dnagirl: điều đó sẽ tốt hơn, thực sự. Làm thế nào tôi có thể làm điều đó?
Tháng Bảy

Câu trả lời:


1466

Hãy thử DISABLE KEYShoặc

SET FOREIGN_KEY_CHECKS=0;

đảm bảo

SET FOREIGN_KEY_CHECKS=1;

sau.


14
Đây có phải là một cái gì đó được thiết lập cho toàn bộ mysql hay chỉ là phiên đó?
tipu

28
Tôi tin rằng nó là mỗi phiên.
Andrew Campbell


1
Tôi có thể vô hiệu hóa FOREIGN_KEY_CHECKS cho một bảng không?
jDub9

@Pacerier Từ việc đọc đó, có vẻ như bạn có thể, nhưng chỉ trong một phiên duy nhất.
Brett

150

Để tắt ràng buộc khóa ngoại trên toàn cầu, hãy làm như sau:

SET GLOBAL FOREIGN_KEY_CHECKS=0;

và nhớ đặt lại khi bạn hoàn thành

SET GLOBAL FOREIGN_KEY_CHECKS=1;

CẢNH BÁO: Bạn chỉ nên làm điều này khi bạn đang thực hiện bảo trì chế độ người dùng. Vì nó có thể dẫn đến sự không thống nhất dữ liệu. Ví dụ, nó sẽ rất hữu ích khi bạn tải lên một lượng lớn dữ liệu bằng cách sử dụng đầu ra mysqldump.


1
đây là những gì tôi cần biết, vì vậy nó không phải là thực hành tuyệt vời, nhưng câu trả lời của những người này nên được ghi điểm cao hơn ...
ftrotter

1
Điều này làm việc cho tôi sau khi thử 'câu trả lời tốt nhất' không hiệu quả với tôi. Có lẽ một lời giải thích về sự khác biệt có thể được thêm vào.
hexnet

7
@hexnet Sự khác biệt là SET FOREIGN_KEY_CHECKSchỉ thay đổi giá trị cho kết nối hiện tại , trong khi SET GLOBAL ..thay đổi giá trị cho tất cả các kết nối , bao gồm cả các kết nối trong tương lai. Nếu bạn chỉ thực hiện SET FOREIGN..trong một cửa sổ, sau đó thử áp dụng câu lệnh trong một cửa sổ khác (qua một kết nối khác), giá trị không thay đổi ở đó. Với GLOBAL, cùng một biến có cùng giá trị cho cả hai kết nối.
MatsLindh

Điều duy nhất có thể giúp tôi khi phát lại một bãi chứa lớn hơn (6+ GB) <3
Tối đa

Điều này không làm việc cho tôi. Khi tôi thử, tôi thấy:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
Mike B

53

Tôi thường chỉ vô hiệu hóa các ràng buộc khóa ngoại khi tôi muốn cắt bớt một bảng và vì tôi tiếp tục quay lại câu trả lời này, đây là cho tôi trong tương lai:

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

25

Thay vì vô hiệu hóa ràng buộc của bạn, hãy sửa đổi vĩnh viễn thành BẬT XÓA BÀI TẬP. Điều đó sẽ hoàn thành một điều tương tự và bạn sẽ không phải bật và tắt khóa kiểm tra. Thích như vậy:

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

Hãy đọc cái này ( http://dev.mysql.com/doc/refman/5.5/en/alter-table.html ) và cái này ( http://dev.mysql.com/doc/refman/5.5/en /create-table-forign-keys.html ).


7
Cẩn thận thay đổi bảng có thể mất nhiều thời gian, tốt hơn là đặt máy chủ toàn cầu về FOREIGN_KEY_CHECKS0 và đặt lại sau khi công việc bẩn được thực hiện. Bên cạnh đó nó có thể khóa để viết bảng của bạn.
Aki

Sẽ không phá vỡ tham chiếu khi thay đổi loại cột từ xa? (Có vẻ như khách hàng của tôi đổi tên bảng tạm thời đã sửa đổi thành tên bảng ban đầu.)
Cees Timmerman

15

Để tắt ràng buộc khóa ngoại trên toàn cầu:

SET GLOBAL FOREIGN_KEY_CHECKS = 0;

và cho các ràng buộc khóa ngoại chủ động

SET GLOBAL FOREIGN_KEY_CHECKS = 1;

10

Một giải pháp rất đơn giản với phpmyadmin:

  • Trong bàn của bạn, đi đến SQL tab
  • Sau khi bạn chỉnh sửa lệnh SQL mà bạn muốn chạy, có một hộp kiểm bên cạnh GO, được đặt tên là ' Kích hoạt kiểm tra khóa ngoại' .
  • Bỏ chọn hộp kiểm này và chạy SQL của bạn . Nó sẽ được tự động kiểm tra lại sau khi thực hiện.

3
Cảm ơn! Quả thực giải pháp SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;không hiệu quả với tôi trong PHPMyAdmin vì tôi quên bỏ chọn hộp kiểm 'Kích hoạt kiểm tra khóa ngoại'. Trong PHPMyAdmin, bạn có thể bỏ qua các lệnh SET này và chỉ cần bỏ chọn hộp kiểm.
Ngày

5

Đối với tôi chỉ SET FOREIGN_KEY_CHECKS=0;là không đủ. Tôi vẫn còn có một com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException.

Tôi đã phải thêm ALTER TABLE myTable DISABLE KEYS;.

Vì thế:

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

FYI, myQuery 5.7 đưa ra cảnh báo, công cụ InnoDB không có tùy chọn này khi chạy lệnh DISABLE KEYS.
jDub9

điều này đã làm việc, không có bảng thay đổi, nó cũng không hoạt động với tôi
David Kabii

3

Nếu trường khóa là null, thì bạn cũng có thể đặt giá trị thành null trước khi thử xóa nó:

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

Trong phpMyAdmin, bạn có thể chọn nhiều hàng sau đó nhấp vào hành động xóa. Bạn sẽ vào một màn hình liệt kê các truy vấn xóa, bạn có thể bỏ chọn kiểm tra Khóa ngoại và nhấp vào Có để thực hiện chúng.

Điều này sẽ cho phép bạn xóa các hàng ngay cả khi có một ràng buộc hạn chế BẬT XÓA.


-2

Không nên đặt ràng buộc khóa ngoại thành 0, vì nếu bạn làm vậy, cơ sở dữ liệu của bạn sẽ không đảm bảo rằng nó không vi phạm tính toàn vẹn tham chiếu. Điều này có thể dẫn đến dữ liệu không chính xác, sai lệch hoặc không đầy đủ.

Bạn tạo một khóa ngoại vì một lý do: bởi vì tất cả các giá trị trong cột con sẽ giống như một giá trị trong cột cha. Nếu không có ràng buộc khóa ngoại, một hàng con có thể có một giá trị không nằm trong hàng cha, điều này sẽ dẫn đến dữ liệu không chính xác.

Chẳng hạn, giả sử bạn có một trang web để sinh viên đăng nhập và mọi sinh viên phải đăng ký tài khoản với tư cách là người dùng. Bạn có một bảng cho id người dùng, với id người dùng làm khóa chính; và một bảng khác cho các tài khoản sinh viên, với id sinh viên là một cột. Vì mỗi sinh viên phải có id người dùng, sẽ rất hợp lý khi biến id sinh viên từ bảng tài khoản sinh viên thành khóa ngoại tham chiếu id người dùng khóa chính trong bảng id người dùng. Nếu không có kiểm tra khóa ngoại, một sinh viên cuối cùng có thể có id sinh viên và không có id người dùng, điều đó có nghĩa là sinh viên có thể có tài khoản mà không phải là người dùng, điều đó là sai.

Hãy tưởng tượng nếu nó xảy ra với một lượng lớn dữ liệu. Đó là lý do tại sao bạn cần kiểm tra khóa ngoại.

Tốt nhất là tìm ra nguyên nhân gây ra lỗi. Rất có thể, bạn đang cố xóa khỏi hàng cha mà không xóa khỏi hàng con. Hãy thử xóa từ hàng con trước khi xóa khỏi hàng cha.


Đúng vậy, luôn có sự đánh đổi.
Pacerier 23/2/2015

21
Không ai nói chạy nó như thế này mãi mãi. Bạn tắt các ràng buộc, tải số lượng lớn một số dữ liệu và bật lại. Không có vấn đề lớn, mọi người làm điều đó mọi lúc.
bwawok

nó là cần thiết cho nhập khẩu số lượng lớn, ít nhất là cho hiệu suất, nó là rất phổ biến. đôi khi bạn chỉ cần khôi phục dữ liệu, sau đó bạn có thể thực hiện kiểm tra.
Firas Abd Alrahman

3
Đây không phải là một câu trả lời cho câu hỏi.
Koray Tugay

Lưu ý, câu hỏi của anh ấy là làm thế nào để làm điều này tạm thời. Điều này là cần thiết khi thực hiện bảo trì và nhập dữ liệu nhất định. Tất nhiên, điều đáng nói là các tập lệnh nhập của bạn trở nên chịu trách nhiệm về tính toàn vẹn dữ liệu. Sau đó, sau đó khi các chỉ mục và các ràng buộc được bật lại, db sẽ cho bạn biết nếu có gì đó bị hỏng.
mcstar
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.