Khóa ngoại có thể là NULL và / hoặc trùng lặp không?


325

Hãy làm rõ hai điều cho tôi:

  1. Khóa ngoại có thể là NULL không?
  2. Khóa ngoại có thể trùng lặp không?

Công bằng như tôi biết, NULLkhông nên sử dụng khóa ngoại, nhưng trong một số ứng dụng của tôi, tôi có thể nhập vào NULLcả Oracle và SQL Server và tôi không biết tại sao.


1
@Adrian: Tốt nhất kiến ​​thức khóa ngoại của tôi không thể là null nhưng nó đang bị null trong máy chủ và nhà tiên tri sql. bạn có thể giải thích lý do tại sao?
ùn tắc

@Jams - đọc liên kết trong câu trả lời của tôi.
JNK

11
điều này không thể xóa được vì câu trả lời và câu hỏi rất hữu ích. Hãy chỉnh sửa câu hỏi để cải thiện nó.
Jeff Atwood

Vui lòng tách ra câu hỏi về trùng lặp. Chỉ có một về NULL được trả lời dưới đây.
Revierpost

Câu trả lời:


529

Câu trả lời ngắn: Có, nó có thể là NULL hoặc trùng lặp.

Tôi muốn giải thích tại sao khóa ngoại có thể cần phải là null hoặc có thể cần phải là duy nhất hoặc không phải là duy nhất. Trước tiên hãy nhớ khóa ngoại đơn giản yêu cầu giá trị trong trường đó phải tồn tại trước tiên trong một bảng khác (bảng cha). Đó là tất cả một FK theo định nghĩa. Null theo định nghĩa không phải là một giá trị. Null có nghĩa là chúng ta chưa biết giá trị là gì.

Hãy để tôi cho bạn một ví dụ thực tế cuộc sống. Giả sử bạn có một cơ sở dữ liệu lưu trữ các đề xuất bán hàng. Giả sử thêm rằng mỗi đề xuất chỉ có một người bán hàng được chỉ định và một khách hàng. Vì vậy, bảng đề xuất của bạn sẽ có hai khóa ngoại, một khóa có ID khách hàng và một khóa có ID đại diện bán hàng. Tuy nhiên, tại thời điểm bản ghi được tạo, không phải lúc nào đại diện bán hàng cũng được chỉ định (vì chưa có ai tự do làm việc trên đó), vì vậy ID khách hàng được điền nhưng ID đại diện bán hàng có thể không có giá trị. Nói cách khác, thông thường bạn cần có khả năng có FK null khi bạn có thể không biết giá trị của nó tại thời điểm dữ liệu được nhập, nhưng bạn biết các giá trị khác trong bảng cần được nhập. Để cho phép null trong FK nói chung, tất cả những gì bạn phải làm là cho phép null trên trường có FK. Giá trị null tách biệt với ý tưởng về việc nó là FK.

Cho dù nó là duy nhất hay không duy nhất liên quan đến việc bảng có mối quan hệ một hay nhiều với bảng cha. Bây giờ nếu bạn có mối quan hệ một đối một, có thể bạn có thể có tất cả dữ liệu trong một bảng, nhưng nếu bảng quá rộng hoặc nếu dữ liệu thuộc một chủ đề khác (nhân viên - ví dụ bảo hiểm @tbone đã đưa ra ví dụ), sau đó bạn muốn các bảng riêng biệt với một FK. Sau đó, bạn muốn tạo FK này hoặc là PK (đảm bảo tính duy nhất) hoặc đặt một ràng buộc duy nhất cho nó.

Hầu hết các FK dành cho một mối quan hệ một đến nhiều và đó là những gì bạn nhận được từ một FK mà không cần thêm một ràng buộc nào nữa trên trường. Vì vậy, bạn có một bảng đơn hàng và bảng chi tiết đơn hàng chẳng hạn. Nếu khách hàng đặt hàng mười mặt hàng cùng một lúc, anh ta có một đơn đặt hàng và mười hồ sơ chi tiết đơn hàng có cùng đơn đặt hàng với FK.


13
Vì vậy, điều đó dự định sẽ tốt hơn là có một người bán hàng giả tên là "Chưa được chỉ định"?
Thomas Weller

8
Một lời bình luận. Nulls để lại nhiều chỗ cho các lỗi trong truy vấn bởi những người không biết SQL (mis) xử lý 3VL như thế nào. Nếu một nhân viên bán hàng thực sự không cần thiết cho một bảng r nhất định, chỉ cần bạn không bao gồm hồ sơ đó. Một bảng riêng biệt có thể là "ProposedAssignTo" hoặc một số bảng như vậy, với các ràng buộc thích hợp. Sau đó, một người viết truy vấn có thể tham gia vào bảng đó và cung cấp logic riêng cho bất cứ điều gì chúng tôi muốn làm khi đề xuất không có nhân viên bán hàng. NULL không chỉ có nghĩa là "chúng tôi không biết" - nó có thể được sử dụng cho nhiều thứ (đó là lý do tại sao nó hầu như luôn là một ý tưởng tồi)
N West

26
@nWest, tôi không cho phép những người không đủ năng lực truy vấn cơ sở dữ liệu của tôi và bất kỳ nhà phát triển nào không biết cách xử lý null là không đủ năng lực. Đôi khi dữ liệu không được biết tại thời điểm nhập dữ liệu ban đầu cho một trường cụ thể nhưng các trường khác là cần thiết tại thời điểm đó.
HLGEM

28
@ThomasWeller Tham khảo một nhân viên bán hàng giả ("Chưa được chỉ định") làm cho vấn đề trở nên tồi tệ hơn. Tôi giả sử bảng nhân viên bán hàng của bạn có nhiều cột ...? Số bảo hiểm xã hội của ông Unassign là gì? Bộ phận nào được giao cho? Ông chủ của anh ta là ai? Tôi hy vọng bạn hiểu ý tôi: khi bạn tạo một nhân viên bán hàng "Chưa được phân bổ", bạn sẽ nhanh chóng phát hiện ra rằng bạn đã giao dịch NULLtrong một bảng cho nhiều NULLs trong một bảng khác nhau.
Gili

1
@ThomasWeller Bạn cũng sẽ gặp vấn đề nếu / khi bạn cần bản địa hóa giao diện của mình.
tobiv


45

Từ miệng ngựa:

Khóa ngoại cho phép các giá trị khóa là tất cả NULL, ngay cả khi không có khóa CHÍNH HÃNG hoặc UNIITE phù hợp

Không có ràng buộc về khóa ngoại

Khi không có ràng buộc nào khác được xác định trên khóa ngoại, bất kỳ số lượng hàng nào trong bảng con có thể tham chiếu cùng một giá trị khóa cha. Mô hình này cho phép null trong khóa ngoại. ...

KHÔNG NULL ràng buộc về khóa ngoại

Khi null không được phép trong khóa ngoại, mỗi hàng trong bảng con phải tham chiếu rõ ràng một giá trị trong khóa cha vì null không được phép trong khóa ngoại.

Bất kỳ số lượng hàng nào trong bảng con có thể tham chiếu cùng một giá trị khóa cha, vì vậy mô hình này thiết lập mối quan hệ một-nhiều giữa khóa cha và khóa ngoài. Tuy nhiên, mỗi hàng trong bảng con phải có tham chiếu đến giá trị khóa cha; không có giá trị (null) trong khóa ngoại không được phép. Ví dụ tương tự trong phần trước có thể được sử dụng để minh họa một mối quan hệ như vậy. Tuy nhiên, trong trường hợp này, nhân viên phải có một tài liệu tham khảo đến một bộ phận cụ thể.

Ràng buộc nhất định về khóa ngoại

Khi một ràng buộc UNIITE được xác định trên khóa ngoại, chỉ một hàng trong bảng con có thể tham chiếu một giá trị khóa cha đã cho. Mô hình này cho phép null trong khóa ngoại.

Mô hình này thiết lập mối quan hệ một đối một giữa khóa gốc và khóa ngoài cho phép các giá trị không xác định (null) trong khóa ngoại. Ví dụ: giả sử rằng bảng nhân viên có một cột có tên MEMBERNO, đề cập đến số thành viên của nhân viên trong gói bảo hiểm của công ty. Ngoài ra, một bảng có tên BẢO HIỂM có khóa chính có tên MEMBERNO và các cột khác của bảng giữ thông tin tương ứng liên quan đến chính sách bảo hiểm của nhân viên. MEMBERNO trong bảng nhân viên phải là cả khóa ngoại và khóa duy nhất:

  • Để thực thi các quy tắc toàn vẹn tham chiếu giữa các bảng EMP_TAB và BẢO HIỂM (ràng buộc FOREIGN KEY)

  • Để đảm bảo rằng mỗi nhân viên có một số thành viên duy nhất (ràng buộc khóa UNIQUE)

Các ràng buộc KHÔNG GIỚI HẠN và KHÔNG NULL đối với Khóa ngoài

Khi cả hai ràng buộc UNIQUE và NOT NULL được xác định trên khóa ngoại, chỉ một hàng trong bảng con có thể tham chiếu giá trị khóa cha đã cho và vì các giá trị NULL không được phép trong khóa ngoại, mỗi hàng trong bảng con phải tham chiếu rõ ràng một giá trị trong khóa cha.

Xem cái này:

Liên kết Oracle 11g


16

Có khóa ngoại có thể là null như đã nói ở trên bởi các lập trình viên cao cấp ... Tôi sẽ thêm một kịch bản khác trong đó Khóa ngoại sẽ được yêu cầu là null .... giả sử chúng ta có các bảng nhận xét, Ảnh và Video trong một ứng dụng cho phép nhận xét về ảnh và video. Trong bảng nhận xét, chúng ta có thể có hai Khóa ngoại hình ảnhId và VideoId cùng với Nhận xét khóa chính. Vì vậy, khi bạn nhận xét về một video, chỉ VideoId sẽ được yêu cầu và imageId sẽ không có giá trị ... và nếu bạn chỉ nhận xét về một hình ảnh thì PictureId sẽ được yêu cầu và VideoId sẽ không có giá trị ...


1
Tôi nghĩ có một cách tốt hơn để giải quyết vấn đề này. Thay vì tạo các cột mới, bạn có thể có hai cột là "id" và "type", sẽ chứa id và tên của bảng khóa ngoại. Ví dụ: id = 1, type = Picture sẽ biểu thị liên kết đến bảng Ảnh với id 1. Ưu điểm của việc sử dụng giải pháp này là, bạn sẽ không phải tạo các cột mới khi các bình luận được thêm vào các bảng bổ sung. Nhược điểm sẽ không có ràng buộc khóa ngoại ở mức db, thay vào đó, ràng buộc sẽ phải là cấp ứng dụng.
Đặc vụ47DarkSoul

4
@Agent: Chúng tôi đã có "giải pháp" này trong sử dụng sản xuất. Đừng làm điều đó, nó thật kinh khủng. Làm cho các truy vấn trở thành mớ hỗn độn này, "nếu đó là loại 1, hãy tham gia vào bảng này, nếu không thì hãy tham gia vào bảng này". Đó là một cơn ác mộng đối với chúng tôi. Chúng tôi cuối cùng đã làm những gì câu trả lời này nói và tạo một cột mới cho mỗi loại tham gia. Tạo cột là giá rẻ. Khá nhiều khuyết điểm duy nhất là rất nhiều cột làm cho cóc khó sử dụng, nhưng đó chỉ là một lỗ hổng của cóc.
dùng128216

1
@FighterJet Rails cung cấp một khung ORM tuyệt vời xử lý ngay cả các truy vấn phức tạp với giải pháp này.
Đặc vụ47DarkSoul

2
@Agent: Có thể nó có thể ... nhưng nếu bạn có thể làm cho nó đơn giản, tại sao làm cho nó phức tạp? Và có lẽ "ác mộng" là từ sai để sử dụng: nó rất bất tiện. Chúng tôi đã không gặp phải vấn đề toàn vẹn dữ liệu (nhiều).
dùng128216

7

nó phụ thuộc vào vai trò này foreign keytrong mối quan hệ của bạn.

  1. nếu đây foreign keycũng là một key attributemối quan hệ của bạn, thì nó không thể là NULL
  2. nếu đây foreign keylà một thuộc tính bình thường trong mối quan hệ của bạn, thì nó có thể là NULL.

3

Đây là một ví dụ sử dụng cú pháp của Oracle:
Trước tiên, hãy tạo một bảng QUỐC GIA

CREATE TABLE TBL_COUNTRY ( COUNTRY_ID VARCHAR2 (50) NOT NULL ) ;
ALTER TABLE TBL_COUNTRY ADD CONSTRAINT COUNTRY_PK PRIMARY KEY ( COUNTRY_ID ) ;

Tạo bảng TỈNH

CREATE TABLE TBL_PROVINCE(
PROVINCE_ID VARCHAR2 (50) NOT NULL ,
COUNTRY_ID  VARCHAR2 (50)
);
ALTER TABLE TBL_PROVINCE ADD CONSTRAINT PROVINCE_PK PRIMARY KEY ( PROVINCE_ID ) ;
ALTER TABLE TBL_PROVINCE ADD CONSTRAINT PROVINCE_COUNTRY_FK FOREIGN KEY ( COUNTRY_ID ) REFERENCES TBL_COUNTRY ( COUNTRY_ID ) ;

Điều này chạy hoàn toàn tốt trong Oracle. Lưu ý khóa ngoại COUNTRY_ID trong bảng thứ hai không có "KHÔNG NULL".

Bây giờ để chèn một hàng vào bảng PROVINCE, chỉ cần xác định PROVINCE_ID là đủ. Tuy nhiên, nếu bạn cũng chọn chỉ định COUNTRY_ID, thì nó phải tồn tại trong bảng COUNTRY.


1

Theo mặc định, không có ràng buộc nào đối với khóa ngoại, khóa ngoại có thể là null và trùng lặp.

trong khi tạo bảng / thay đổi bảng, nếu bạn thêm bất kỳ ràng buộc nào về tính duy nhất hoặc không null thì chỉ có điều nó sẽ không cho phép các giá trị null / trùng lặp.


0

Nói một cách đơn giản, mối quan hệ "Không xác định" giữa các Thực thể là một phần của Mô hình ER và có sẵn trong Microsoft Visio khi thiết kế Biểu đồ ER. Điều này là bắt buộc để thực thi cardinality giữa các Thực thể loại "không hoặc nhiều hơn không" hoặc "không hoặc một". Lưu ý "không" này về số lượng thay vì "một" trong "một đến nhiều".

Bây giờ, ví dụ về mối quan hệ không xác định trong đó cardinality có thể là "zero" (không xác định) là khi chúng ta nói một bản ghi / đối tượng trong một thực thể - A "có thể" hoặc "có thể" không có giá trị như một tham chiếu đến bản ghi / s trong một thực thể khác-B.

Vì, có khả năng một bản ghi của thực thể-A tự nhận dạng chính nó với các bản ghi của Thực thể-B khác, do đó cần có một cột trong Thực thể-B để có giá trị nhận dạng của bản ghi của Thực thể-B. Cột này có thể là "Không" nếu không có bản ghi nào trong Thực thể-A xác định bản ghi / s (hoặc, đối tượng / s) trong Thực thể-B.

Trong Mô hình hướng đối tượng (thế giới thực), có những tình huống khi một đối tượng của Lớp B không nhất thiết phải phụ thuộc (kết hợp mạnh mẽ) vào đối tượng của lớp A vì sự tồn tại của nó, có nghĩa là Lớp B được ghép lỏng lẻo với Lớp- A sao cho Class-A có thể "Chứa" (Chứa) một đối tượng của Class-A, trái ngược với khái niệm đối tượng của Class-B phải có (Thành phần) một đối tượng của Class-A, đối với (đối tượng của class- B) sáng tạo.

Từ quan điểm Truy vấn SQL, bạn có thể truy vấn tất cả các bản ghi trong thực thể-B "không null" cho khóa ngoài dành riêng cho Thực thể-B. Điều này sẽ mang lại tất cả các bản ghi có giá trị tương ứng nhất định cho các hàng trong Thực thể-A thay vào đó, tất cả các bản ghi có giá trị Null sẽ là các bản ghi không có bất kỳ bản ghi nào trong Thực thể-A trong Thực thể-B.


-1

Tôi nghĩ rằng tốt hơn là xem xét các cardinality có thể có trong các bảng. Chúng ta có thể có cardinality tối thiểu có thể bằng không. Khi là tùy chọn, sự tham gia tối thiểu của các bộ dữ liệu từ bảng có liên quan có thể bằng 0, Bây giờ bạn phải đối mặt với sự cần thiết của các giá trị khóa ngoài được phép null.

Nhưng câu trả lời là tất cả phụ thuộc vào Doanh nghiệp.


-3

Ý tưởng về khóa ngoại dựa trên khái niệm tham chiếu một giá trị đã tồn tại trong bảng chính. Đó là lý do tại sao nó được gọi là khóa ngoại trong bảng khác. Khái niệm này được gọi là toàn vẹn tham chiếu. Nếu khóa ngoại được khai báo là trường null, nó sẽ vi phạm chính logic của tính toàn vẹn tham chiếu. Nó sẽ đề cập đến cái gì? Nó chỉ có thể đề cập đến một cái gì đó có mặt trong bảng chính. Do đó, tôi nghĩ sẽ sai khi khai báo trường khóa ngoài là null.


Nó có thể tham chiếu "không có gì" hoặc bạn chưa biết giá trị của nó NULL, nhưng tính toàn vẹn tham chiếu nói là nếu nó tham chiếu "cái gì đó" thì nó phải ở đó.
yaxe

-7

Tôi nghĩ rằng khóa ngoại của một bảng cũng là khóa chính cho một số bảng khác. Vì vậy, nó sẽ không cho phép null. Vì vậy, không có câu hỏi nào về việc có giá trị null trong khóa ngoại.

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.