Ràng buộc khóa ngoài: Khi nào nên sử dụng TRÊN CẬP NHẬT và BẬT XÓA


195

Tôi đang thiết kế lược đồ cơ sở dữ liệu của mình bằng MySQL Workbench, điều này khá tuyệt vì bạn có thể thực hiện các sơ đồ và nó chuyển đổi chúng: P

Dù sao, tôi đã quyết định sử dụng InnoDB vì đó là hỗ trợ Khóa ngoài. Một điều tôi nhận thấy là nó cho phép bạn thiết lập tùy chọn On Update và trên Xóa cho các khóa ngoại. Ai đó có thể giải thích nơi "Hạn chế", "Cascade" và đặt null có thể được sử dụng trong một ví dụ đơn giản không?

Ví dụ: giả sử tôi có một user bảng bao gồm a userID. Và giả sử tôi có một bảng thông báo messagelà nhiều-nhiều-trong đó có 2 khóa ngoại (tham chiếu cùng một khóa chính, userIDtrong userbảng). Việc cài đặt các tùy chọn On Update và On Delete có hữu ích trong trường hợp này không? Nếu vậy, tôi chọn cái nào? Nếu đây không phải là một ví dụ tốt, bạn có thể vui lòng đưa ra một ví dụ hay để minh họa làm thế nào những thứ này có thể hữu ích không?

Cảm ơn

Câu trả lời:


485

Đừng ngần ngại đặt các ràng buộc trên cơ sở dữ liệu. Bạn chắc chắn sẽ có một cơ sở dữ liệu nhất quán và đó là một trong những lý do tốt để sử dụng cơ sở dữ liệu. Đặc biệt nếu bạn có một số ứng dụng yêu cầu nó (hoặc chỉ một ứng dụng nhưng với chế độ trực tiếp và chế độ hàng loạt sử dụng các nguồn khác nhau).

Với MySQL, bạn không có các ràng buộc nâng cao như bạn sẽ có trong postgreSQL nhưng ít nhất các ràng buộc khóa ngoại khá tiên tiến.

Chúng ta sẽ lấy một ví dụ, một bảng công ty có bảng người dùng chứa những người từ công ty này

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

Hãy xem mệnh đề ON UPDATE :

  • TRÊN CẬP NHẬT GIỚI HẠN : mặc định : nếu bạn cố cập nhật company_id trong bảng CÔNG TY, công cụ sẽ từ chối thao tác nếu ít nhất một NGƯỜI DÙNG liên kết với công ty này.
  • CẬP NHẬT KHÔNG CÓ HÀNH ĐỘNG : giống như GIỚI HẠN.
  • TRÊN CẬP NHẬT CASCADE : thông thường tốt nhất : nếu bạn cập nhật company_id trong một hàng CÔNG TY, công cụ sẽ cập nhật nó phù hợp trên tất cả các hàng NGƯỜI DÙNG tham khảo CÔNG TY này (nhưng không có kích hoạt nào được kích hoạt trên bảng USER, cảnh báo). Động cơ sẽ theo dõi các thay đổi cho bạn, thật tốt.
  • TRÊN CẬP NHẬT NULL : nếu bạn cập nhật company_id trong một hàng bảng CÔNG TY, công cụ sẽ đặt USER_ company_id liên quan thành NULL (nên có sẵn trong trường USER company_id). Tôi không thể thấy bất kỳ điều thú vị nào để làm với điều đó trên một bản cập nhật, nhưng tôi có thể sai.

Và bây giờ về phía BẬT XÓA :

  • TRÊN XÓA HẠN CHẾ : mặc định : nếu bạn cố xóa Id công ty_id trong bảng CÔNG TY, công cụ sẽ từ chối thao tác nếu một NGƯỜI DÙNG ít nhất liên kết trên công ty này, có thể cứu mạng bạn.
  • TRÊN XÓA KHÔNG CÓ HÀNH ĐỘNG : giống như GIỚI HẠN
  • TRÊN XÓA CASCADE : nguy hiểm : nếu bạn xóa một hàng công ty trong bảng CÔNG TY, công cụ cũng sẽ xóa các NGƯỜI DÙNG có liên quan. Điều này nguy hiểm nhưng có thể được sử dụng để thực hiện dọn dẹp tự động trên các bảng thứ cấp (vì vậy nó có thể là thứ bạn muốn, nhưng chắc chắn không phải là ví dụ về CÔNG TY <-> USER)
  • TRÊN XÓA THIẾT LẬP NULL : handful : nếu bạn xóa một hàng CÔNG TY, các NGƯỜI DÙNG có liên quan sẽ tự động có mối quan hệ với NULL. Nếu Null là giá trị của bạn đối với người dùng không có công ty thì đây có thể là một hành vi tốt, ví dụ có thể bạn cần giữ người dùng trong ứng dụng của mình, với tư cách là tác giả của một số nội dung, nhưng loại bỏ công ty không phải là vấn đề đối với bạn.

thông thường mặc định của tôi là: TRÊN XÓA HẠN CHẾ TRÊN CẬP NHẬT CASCADE . với một số ON DELETE CASCADEbảng theo dõi (nhật ký - không phải tất cả nhật ký--, những thứ tương tự) và ON DELETE SET NULLkhi bảng chính là 'thuộc tính đơn giản' cho bảng chứa khóa ngoại, như bảng JOB cho bảng USER.

Biên tập

Lâu lắm rồi tôi mới viết như vậy. Bây giờ tôi nghĩ rằng tôi nên thêm một cảnh báo quan trọng. MySQL có một giới hạn tài liệu lớn với các tầng. Cascades không kích hoạt kích hoạt . Vì vậy, nếu bạn quá tự tin vào công cụ đó để sử dụng các kích hoạt, bạn nên tránh các ràng buộc theo tầng.

Các trình kích hoạt MySQL chỉ kích hoạt các thay đổi được thực hiện đối với các bảng bằng các câu lệnh SQL. Chúng không kích hoạt các thay đổi về chế độ xem cũng như các thay đổi đối với các bảng được tạo bởi các API không truyền các câu lệnh SQL đến Máy chủ MySQL

==> Xem bên dưới chỉnh sửa cuối cùng, mọi thứ đang di chuyển trên miền này

Kích hoạt không được kích hoạt bởi các hành động quan trọng nước ngoài.

Và tôi không nghĩ rằng điều này sẽ được sửa chữa một ngày. Các ràng buộc khóa ngoài được quản lý bởi bộ lưu trữ InnoDb và Triggers được quản lý bởi công cụ SQL SQL. Cả hai đều tách ra. Innodb là bộ lưu trữ duy nhất có quản lý ràng buộc, có thể họ sẽ thêm các kích hoạt trực tiếp vào công cụ lưu trữ một ngày nào đó, có thể không.

Nhưng tôi có ý kiến ​​của riêng tôi về yếu tố nào bạn nên chọn giữa việc triển khai trình kích hoạt kém và hỗ trợ ràng buộc khóa ngoại rất hữu ích. Và một khi bạn sẽ quen với tính nhất quán của cơ sở dữ liệu, bạn sẽ thích PostgreSQL.

12/2017-Cập nhật Chỉnh sửa này về MySQL:

như tuyên bố của @IstiaqueAhmed trong các bình luận, tình hình đã thay đổi về chủ đề này. Vì vậy, hãy theo liên kết và kiểm tra tình hình cập nhật thực sự (có thể thay đổi một lần nữa trong tương lai).


8
ON DELETE CASCADE : dangerous- uống với một nhúm muối.
onedaywhen

3
Bạn sẽ cần phải cẩn thận xếp tầng, nó có thể khóa hệ thống của bạn nếu cần thay đổi nhiều hồ sơ. Xóa Cascde đặc biệt nên được xem xét cẩn thận trước khi sử dụng, thường thì bạn thực sự muốn xóa không xảy ra nếu có hồ sơ con. Tôi sẽ không muốn khách hàng xóa để xóa sạch dữ liệu tài chính cho các oreders mà anh ta có trước đây. Đôi khi, tốt hơn là đảm bảo rằng việc quét không được bật và cung cấp một cách để các bản ghi amrk không hoạt động.
HLGEM

1
Về mặt logic kinh doanh, có một trường hợp có thể thú vị SET NULLtrong ON UPDATE: cập nhật một công ty thể hiện sự tách rời của Công ty> Quan hệ người dùng. Ví dụ: nếu một công ty thay đổi loại hình kinh doanh, những người dùng trước đó có thể không còn liên quan đến doanh nghiệp đó nữa, do đó NULLcó thể thích hợp hơn cho chỉ số này.
CPHPython

1
@regilero, có vẻ như nội dung trong liên kết đầu tiên của bạn ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) đến trang web mysql đã thay đổi. Nó nói This includes changes to base tables that underlie updatable viewsthay vì những gì bạn đã dán tức làThey do not activate for changes in views
Istiaque Ahmed

6
"Tôi sẽ không muốn khách hàng xóa để xóa sạch dữ liệu tài chính cho các đơn hàng mà anh ta đã có trước đây." Trong tình huống như vậy, có lẽ bạn vẫn cần dữ liệu của khách hàng. Thiết kế của bạn có lẽ nên đánh dấu khách hàng là không hoạt động, không xóa hàng của họ khỏi cơ sở dữ liệu. Trong thực tế, đó là kinh nghiệm chuyên môn của tôi rằng bạn thực sự rất hiếm khi muốn xóa bất cứ thứ gì , thích đánh dấu không hoạt động theo mặc định. Trong trường hợp xóa vĩnh viễn ok, CASCADE DELETEnói chung cũng ổn, thậm chí được ưa thích. Tôi không coi nó là đặc biệt nguy hiểm.
GrandOpener

3

Ngoài ra câu trả lời @MarkR - một điều cần lưu ý là nhiều khung công tác PHP với ORM sẽ không nhận ra hoặc sử dụng thiết lập DB nâng cao (khóa ngoại, xóa tầng, ràng buộc duy nhất) và điều này có thể dẫn đến hành vi không mong muốn.

Ví dụ: nếu bạn xóa một bản ghi bằng ORM và bạn DELETE CASCADEsẽ xóa các bản ghi trong các bảng có liên quan, nỗ lực xóa các bản ghi liên quan này (thường là tự động) sẽ dẫn đến lỗi.


11
Đó sẽ là lý do để không sử dụng ORM cụ thể đó. Bất kỳ công cụ nào kém về hỗ trợ cơ sở dữ liệu đều không đáng tin cậy. Khóa ngoại và xóa tầng hoặc cập nhật là những khái niệm cơ bản không phải là khái niệm nâng cao và không có cơ sở dữ liệu thực tế nào được thiết kế mà không có ràng buộc khóa ngoài!
HLGEM

Vấn đề là họ ném lỗi. Có thể HẠN CHẾ XÓA nhưng có động cơ không phát sinh lỗi nhưng vẫn duy trì ngữ nghĩa? Tôi muốn chương trình của tôi vẫn tiếp tục trong khi đồng thời bảo vệ các dữ liệu khác khỏi bị xóa.
TheRealChx101

2

Bạn sẽ cần phải xem xét điều này trong bối cảnh của ứng dụng. Nói chung, bạn nên thiết kế một ứng dụng, không phải cơ sở dữ liệu (cơ sở dữ liệu đơn giản là một phần của ứng dụng).

Xem xét cách ứng dụng của bạn sẽ đáp ứng với các trường hợp khác nhau.

Hành động mặc định là hạn chế (tức là không cho phép) hoạt động, đó thường là những gì bạn muốn vì nó ngăn ngừa các lỗi lập trình ngu ngốc. Tuy nhiên, trên DELETE CASCADE cũng có thể hữu ích. Nó thực sự phụ thuộc vào ứng dụng của bạn và cách bạn định xóa các đối tượng cụ thể.

Cá nhân, tôi sử dụng InnoDB vì nó không làm mất dữ liệu của bạn (cf MyISAM, cũng vậy), thay vì vì nó có các ràng buộc FK.

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.