Các cột bảng có Khóa ngoài có thể là NULL không?


235

Tôi có một bảng có một số cột ID cho các bảng khác.

Tôi muốn một khóa ngoại để buộc toàn vẹn chỉ khi tôi đặt dữ liệu vào đó. Nếu tôi thực hiện cập nhật sau đó để điền vào cột đó, thì nó cũng nên kiểm tra ràng buộc.

(Đây có thể phụ thuộc vào máy chủ cơ sở dữ liệu, tôi đang sử dụng loại bảng MySQL & InnoDB)

Tôi tin rằng đây là một kỳ vọng hợp lý, nhưng hãy sửa tôi nếu tôi sai.


6
Tôi không biết về MySQL, nhưng MS SQL Server cho phép các khóa ngoại là không thể thực hiện được với ngữ nghĩa mà bạn muốn. Tôi mong đợi đó là hành vi tiêu chuẩn.
Jeffrey L Whitledge

1
Khóa ngoại, không thể là null theo mặc định trong myQuery, lý do rất đơn giản, nếu bạn tham chiếu một cái gì đó và bạn để nó rỗng, bạn sẽ mất tính toàn vẹn dữ liệu. khi bạn tạo tập hợp bảng, cho phép null thành KHÔNG và sau đó áp dụng ràng buộc khóa ngoài. Bạn không thể đặt null khi cập nhật, nó sẽ gửi cho bạn một lỗi, nhưng bạn có thể (bạn phải) đơn giản là không cập nhật cột này và chỉ cập nhật các trường bạn cần thay đổi.
JoelBonetR

Câu trả lời:


245

Có, bạn chỉ có thể thực thi ràng buộc khi giá trị không phải là NULL. Điều này có thể dễ dàng kiểm tra với ví dụ sau:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

Chèn đầu tiên sẽ vượt qua vì chúng ta chèn NULL vào parent_id. Chèn thứ hai không thành công do ràng buộc khóa ngoài, vì chúng tôi đã cố gắng chèn một giá trị không tồn tại trong parentbảng.


16
Bảng cha cũng có thể được khai báo với id INT KHÔNG NULL.
Sẽ

@CJDennis Nếu bạn thực hiện để chỉ một hàng có thể có ID null, nó có thể được sử dụng làm giá trị dự phòng cho các hàng khác. (Mặc dù DB có thể hoạt động tốt hơn nếu bạn chỉ sử dụng nhiều cột hơn.) Ràng buộc mặc định có vẻ như là một vấn đề nếu sau đó bạn muốn biết liệu giá trị ban đầu được đặt là "mặc định" (bằng cách sử dụng null) hoặc được đặt thành giá trị xảy ra giống như "mặc định". Bằng cách có một hàng có id rỗng, bạn có thể chỉ ra rõ ràng rằng hàng này không được sử dụng như một hàng bình thường và có thể sử dụng hàng này như một cách cung cấp một loại giá trị mặc định động cho các hàng khác.
Ouroborus

1
tôi nghĩ rằng parent_id INT NULLphần này (bằng lời nói) bằngparent_id int default null

Lưu ý phụ cho người dùng java, nếu bạn sử dụng ibatis hoặc ORM khác và người dùng nguyên thủy intthay vì Integertrong các thành viên lớp của bạn, mặc định sẽ không bao giờ là null, nhưng sẽ là 0 và bạn sẽ thất bại.
Jim Ford

32

Tôi thấy rằng khi chèn, các giá trị cột null phải được khai báo cụ thể là NULL, nếu không tôi sẽ gặp lỗi vi phạm ràng buộc (trái ngược với chuỗi rỗng).


8
Bạn có thể không đặt giá trị mặc định của NULL trên cột để cho phép điều này không?
Kevin Coulombe

Có, trong hầu hết các ngôn ngữ, NULL khác với một chuỗi trống. Có lẽ tinh tế khi bắt đầu, nhưng quan trọng để nhớ.
Gary

Xin chào Backslider, bạn nói "(trái ngược với chuỗi rỗng)", nhưng tôi không nghĩ bạn có nghĩa là bạn sẽ CHỌN một giá trị của chuỗi trống, nhưng đúng hơn là bạn không chỉ định giá trị cho Giá trị tại tất cả? tức là bạn thậm chí không đề cập đến cột trong INSERT INTO {table} {list_of_columns}? Bởi vì điều đó đúng với tôi; bỏ qua đề cập đến cột gây ra lỗi, nhưng bao gồm và thiết lập rõ ràng để sửa lỗi NULL. Nếu tôi đúng, tôi nghĩ rằng nhận xét của @ Gary không áp dụng (vì bạn không có nghĩa là một chuỗi trống), nhưng @Kevin Coulombe có thể hữu ích ...
The Red Pea

Có, đề xuất của @ KevinCoulombe hoạt động, tôi đã mô tả cách đạt được điều này với các tập lệnh Di chuyển của Entity Framework Core, tại đây
The Red Pea

Điều quan trọng để chỉ ra rằng lý do rõ ràng khi cập nhật một bản ghi có chứa khóa ngoại NULL chỉ áp dụng cho các loại chuỗi (varchar, v.v.), vì nếu không, một chuỗi trống có thể được chuyển qua mặc định. Đây là trường hợp với MySQL và dẫn đến lỗi toàn vẹn khi cập nhật.
CodeMantle

4

Vâng, điều đó sẽ làm việc như bạn mong đợi. Thật không may, tôi dường như gặp khó khăn khi tìm thấy một tuyên bố rõ ràng về điều này trong hướng dẫn sử dụng MySQL .

Khóa ngoại có nghĩa là giá trị phải tồn tại trong bảng khác. NULL đề cập đến sự vắng mặt của giá trị, vì vậy khi bạn đặt một cột thành NULL, sẽ không có ý nghĩa gì khi cố gắng thực thi các ràng buộc về điều đó.


Theo thiết kế, Khóa ngoài phải tham chiếu một số khóa (Chính) không phải là NULL, nhưng trong giai đoạn phát triển khi chúng ta cần chèn nhiều dữ liệu vào bảng con, chúng ta không biết nó sẽ đề cập đến ai (bảng cha) . Đó là lý do tại sao chúng tôi có giá trị NULL được phép. Trong sản xuất có NULL sẽ là một dòng thiết kế, có thể nói đại khái.
krishna tối ưu

2

Các công việc trên nhưng điều này không. Lưu ý CASCADE TRÊN XÓA

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)

4
Bạn có ý nghĩa gì bởi 'những điều trên'? Lưu ý rằng nếu bạn đang đề cập đến câu trả lời khác, thứ tự có thể thay đổi.
d219

2

Có, giá trị có thể là NULL, nhưng bạn phải rõ ràng. Tôi đã trải qua tình huống tương tự trước đây và thật dễ dàng để quên TẠI SAO điều này xảy ra, và vì vậy phải mất một chút để nhớ những gì cần phải làm.

Nếu dữ liệu được gửi được truyền hoặc giải thích dưới dạng một chuỗi trống, nó sẽ thất bại. Tuy nhiên, bằng cách đặt rõ ràng giá trị thành NULL khi XÁC NHẬN hoặc CẬP NHẬT, bạn sẽ ổn.

Nhưng đây là niềm vui của lập trình, phải không? Tạo ra vấn đề của chúng ta và sau đó sửa chúng! Chúc mừng!


1

Một cách khác xung quanh điều này sẽ là chèn một phần tử DEFAULT vào bảng khác. Ví dụ: mọi tham chiếu đến uuid = 00000000-0000-0000-0000-000000000000 trên bảng khác sẽ cho biết không có hành động. Bạn cũng cần đặt tất cả các giá trị cho id đó là "trung tính", ví dụ 0, chuỗi rỗng, null để không ảnh hưởng đến logic mã của bạn.


2
Đây không phải là điều tương tự. Giá trị mặc định hoặc "trung tính" không giống với NULL, sự vắng mặt của một giá trị. Không cần thảo luận về giá trị của một giá trị mặc định so với NULL, cụm từ của bạn là một hỗn hợp. "Một cách khác xung quanh điều này sẽ là chèn một phần tử null vào bảng khác" sẽ nói một cái gì đó giống như "Một cách khác xung quanh điều này sẽ là chèn một phần tử DEFAULT vào bảng khác"
blindguy

0

Tôi cũng bị mắc kẹt về vấn đề này. Nhưng tôi đã giải quyết đơn giản bằng cách xác định khóa ngoại là unsigned integer. Tìm ví dụ dưới đây-

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;
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.