Tại sao một ràng buộc ĐỘC ĐÁO chỉ cho phép một NULL?


36

Về mặt kỹ thuật, NULL = NULL là Sai, theo logic đó, không có NULL nào bằng với bất kỳ NULL nào và tất cả các NULL đều khác biệt. Không phải điều này có nghĩa là tất cả các NULL là duy nhất và một chỉ mục duy nhất sẽ cho phép bất kỳ số lượng NULL nào?


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White nói GoFundMonica

Câu trả lời:


52

Tại sao nó hoạt động theo cách này? Bởi vì từ khi nào, ai đó đã đưa ra quyết định thiết kế mà không biết hoặc không quan tâm đến những gì tiêu chuẩn nói (rốt cuộc, chúng ta có tất cả các loại hành vi kỳ lạ với NULLs, và có thể cưỡng chế các hành vi khác nhau theo ý muốn). Quyết định rằng quyết rằng, trong này trường hợp, NULL = NULL.

Đó không phải là một quyết định rất thông minh. Những gì họ nên làm là có hành vi mặc định tuân thủ tiêu chuẩn ANSI và nếu họ thực sự muốn hành vi kỳ dị này, hãy cho phép nó thông qua tùy chọn DDL như WITH CONSIDER_NULLS_EQUALhoặc WITH ALLOW_ONLY_ONE_NULL.

Tất nhiên, nhận thức muộn là 20/20.

Và chúng tôi có một cách giải quyết, bây giờ, dù thế nào, ngay cả khi nó không phải là sạch nhất hoặc trực quan nhất.

Bạn có thể có được hành vi ANSI thích hợp trong SQL Server 2008 trở lên bằng cách tạo một chỉ mục được lọc, duy nhất.

CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;

Điều này cho phép nhiều hơn một NULLgiá trị vì những hàng đó hoàn toàn không được kiểm tra trùng lặp. Là một phần thưởng bổ sung, cuối cùng, đây sẽ là một chỉ mục nhỏ hơn một chỉ số bao gồm toàn bộ bảng nếu nhiều NULLs được cho phép (đặc biệt khi đó không phải là cột duy nhất trong chỉ mục, nó có INCLUDEcác cột, v.v.). Tuy nhiên, bạn có thể muốn biết về một số hạn chế khác của các chỉ mục được lọc:


8

Chính xác. Việc thực hiện một ràng buộc hoặc chỉ mục duy nhất trong máy chủ sql cho phép một và chỉ một NULL. Cũng đúng rằng về mặt kỹ thuật này không phù hợp với định nghĩa của NULL nhưng đó là một trong những điều họ đã làm để làm cho nó hữu ích hơn mặc dù nó không "đúng" về mặt kỹ thuật. Lưu ý KHÓA CHÍNH (cũng là một chỉ mục duy nhất) không cho phép NULL (tất nhiên).


1
Tính kỹ thuật này (SQL-Server) cũng không phù hợp với tiêu chuẩn SQL. Có một mục Connect 7 năm về vấn đề này.
ypercubeᵀᴹ

@ypercube Đúng. Đó là lý do tại sao tôi nói đó chỉ là việc thực hiện và không thực sự phù hợp với định nghĩa của NULL. Tôi đã không nghĩ về chỉ số duy nhất được lọc (mặc dù tôi đã sử dụng nó cho những thứ khác.)
Kenneth Fisher

3

Đầu tiên - ngừng sử dụng cụm từ "Giá trị không", nó sẽ khiến bạn lạc lối. Thay vào đó, hãy sử dụng cụm từ "null mark" - một điểm đánh dấu trong một cột chỉ ra rằng giá trị thực trong cột này bị thiếu hoặc không thể áp dụng (nhưng lưu ý rằng điểm đánh dấu không cho biết lựa chọn nào trong số đó thực sự là trường hợp¹).

Bây giờ, hãy tưởng tượng như sau (nơi cơ sở dữ liệu không có kiến ​​thức đầy đủ về tình huống được mô hình hóa).

Situation          Database

ID   Code          ID   Code
--   -----         --   -----
1    A             1    A
2    B             2    (null)
3    C             3    C
4    B             4    (null)

Quy tắc toàn vẹn mà chúng tôi đang lập mô hình là "Mã phải là duy nhất". Tình huống trong thế giới thực vi phạm điều này, vì vậy cơ sở dữ liệu không nên cho phép cả hai mục 2 và 4 ở trong bảng cùng một lúc.

Cách tiếp cận an toàn nhất và linh hoạt nhất sẽ là không cho phép các điểm đánh dấu null trong trường Mã, do đó không có khả năng dữ liệu không nhất quán. Cách tiếp cận linh hoạt nhất sẽ là cho phép nhiều điểm đánh dấu null và lo lắng về tính duy nhất khi các giá trị được nhập.

Các lập trình viên Sybase đã đi theo cách tiếp cận có phần an toàn, không linh hoạt khi chỉ cho phép một điểm đánh dấu null trong bảng - điều mà các nhà bình luận đã phàn nàn kể từ đó. Microsoft đã tiếp tục hành vi này, tôi đoán để tương thích ngược.


Tôi chắc chắn rằng tôi đã đọc ở đâu đó rằng Codd đã cân nhắc việc thực hiện hai điểm đánh dấu null - một là không xác định, một là không thể áp dụng - nhưng đã từ chối nó, nhưng tôi không thể tìm thấy tài liệu tham khảo. Tôi có nhớ chính xác không?

PS Câu nói yêu thích của tôi về null: Louis Davidson, "Thiết kế cơ sở dữ liệu SQL Server 2000 chuyên nghiệp", Nhà xuất bản Digitx, 2001, trang 52. "Luộc xuống một câu duy nhất: NULL là xấu xa."


1
Cho phép một người duy nhất nullkhông đạt được mục tiêu này. Bởi vì giá trị còn thiếu có thể giống với giá trị ở một trong các hàng khác.
Martin Smith

1
Những gì @MartinSmith nói. Điều gì nếu bạn có một ràng buộc kiểm tra CHECK (Value IN ('A','B','C','D'))? Sau đó, cả triển khai SQL-Server và tiêu chuẩn SQL đều cho phép bảng có 5 hàng (một hàng cho mỗi giá trị cộng với 1 với NULL.) Sau đó, có thể cho rằng, trong khi cơ sở dữ liệu phù hợp với các ràng buộc của nó, nó không phù hợp với ý định của nhà thiết kế bảng có tối đa 4 hàng. Không có giá trị nào mà NULL có thể được thay đổi thành sẽ không vi phạm ràng buộc, trừ khi một hoặc nhiều hàng bị xóa.
ypercubeᵀᴹ

1
Thực tế là tiêu chuẩn sẽ cho phép 6 thậm chí 106 hàng thay vì 5 không thay đổi mà cả hai đều thất bại theo cách nào đó trong kịch bản này.
ypercubeᵀᴹ

@Martin Smith, nó có thể, nhưng một lần nữa, nó có thể không - máy chủ cơ sở dữ liệu không thể nói để nó không gặp rủi ro và đi theo con đường an toàn. Đó là những gì các lập trình viên Sybase (tôi đoán) đã quyết định, gây ra sự khó chịu kể từ đó (ít nhất là từ Inside SQL Server 6.5, cuốn sách lâu đời nhất trên kệ sách của tôi, nơi Ron Soukup đưa ra nhiều nhận xét giống như câu trả lời của Aaron) . Tôi đoán nó có thể tồi tệ hơn - họ có thể đã bắt buộc không có điểm đánh dấu null. :-)
Greenstone Walker

2
@GreenstoneWalker - Nó không đi theo con đường "an toàn". Nó giả định rằng giá trị còn thiếu sẽ không xung đột. CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;sẽ đưa ra một lỗi. Theo lý thuyết của bạn về các động lực thiết kế, nó đã phải ngăn chặn việc chèn vào NULLtrong trường hợp đầu tiên - bởi vì kiến ​​thức không đầy đủ có nghĩa là không có gì đảm bảo rằng giá trị là khác nhau.
Martin Smith

2

Điều này có thể không chính xác về mặt kỹ thuật, nhưng về mặt triết học, nó giúp tôi ngủ vào ban đêm ...

Giống như nhiều người khác đã nói hoặc ám chỉ, nếu bạn nghĩ về NULL là không xác định, thì bạn không thể xác định liệu một giá trị NULL trên thực tế có bằng với giá trị NULL khác hay không. Nghĩ về nó theo cách này, biểu thức NULL == NULL nên đánh giá thành NULL, nghĩa là không xác định.

Một ràng buộc duy nhất sẽ cần một giá trị dứt khoát để so sánh các giá trị cột. Nói cách khác, khi so sánh một giá trị cột đơn với bất kỳ giá trị cột nào khác bằng toán tử đẳng thức, nó phải ước tính thành false để hợp lệ. Unknown không thực sự sai mặc dù nó thường được coi là giả. Hai giá trị NULL có thể bằng nhau hoặc không ... đơn giản là nó không thể được xác định rõ ràng.

Nó giúp nghĩ về một ràng buộc duy nhất là hạn chế các giá trị có thể được xác định là khác biệt với nhau. Ý tôi là điều này là nếu bạn chạy một CHỌN trông giống như thế này:

SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"

Hầu hết mọi người sẽ mong đợi một kết quả, cho rằng có một ràng buộc duy nhất. Nếu bạn cho phép nhiều giá trị NULL trong CộtWithUniqueConstraint, thì không thể chọn một hàng riêng biệt từ bảng bằng cách sử dụng NULL làm giá trị so sánh.

Do đó, tôi tin rằng bất kể nó có được thực hiện chính xác hay không liên quan đến định nghĩa của NULL, thì chắc chắn sẽ thực tế hơn rất nhiều trong hầu hết các tình huống so với việc cho phép nhiều giá trị NULL.


Chọn của bạn sẽ cho 1 kết quả, khi có một ràng buộc duy nhất (trong bất kỳ triển khai nào, không chỉ SQL-Server). Quan điểm của bạn là gì?
ypercubeᵀᴹ

-3

Một trong những mục đích chính của một UNIQUEràng buộc là ngăn chặn các bản ghi trùng lặp. Nếu một người cần có một bảng trong đó có thể có nhiều bản ghi trong đó một giá trị là "không xác định", nhưng không có hai bản ghi nào được phép có cùng giá trị "đã biết", thì các giá trị không xác định phải được gán các định danh duy nhất nhân tạo trước khi chúng thêm vào bảng

Có một vài trường hợp hiếm hoi trong đó một cột có UNIQUEràng buộc và chứa một giá trị null duy nhất; ví dụ: nếu một bảng chứa ánh xạ giữa các giá trị cột và mô tả văn bản được bản địa hóa, một hàng cho NULLphép xác định mô tả sẽ xuất hiện khi cột đó trong một số bảng khác NULL. Các hành vi NULLcho phép cho trường hợp sử dụng đó.

Mặt khác, tôi thấy không có cơ sở cho cơ sở dữ liệu có UNIQUEràng buộc trên bất kỳ cột nào để cho phép tồn tại nhiều bản ghi giống hệt nhau, nhưng tôi thấy không có cách nào để ngăn chặn điều đó trong khi cho phép nhiều bản ghi có giá trị khóa không thể phân biệt được. Tuyên bố rằng NULLkhông bằng chính nó sẽ không làm cho NULLcác giá trị có thể phân biệt được với nhau.


3
Nhận dạng duy nhất nhân tạo là một trò đùa, xin lỗi. Làm thế nào bạn sẽ làm điều đó cho một số VIN? Nếu bạn không biết nó là gì, tại sao phải làm gì đó? Chỉ để chiếm thêm không gian đĩa? Có vẻ như vô nghĩa khi giải quyết một số vấn đề khác (như không muốn viết ứng dụng theo cách mà nó xử lý duyên dáng các NULL). Nếu bạn thực sự cần biết lý do tại sao một cái gì đó là NULL (tồn tại nhưng không biết so với biết nó không tồn tại so với không biết hoặc quan tâm nếu nó tồn tại, chẳng hạn), sau đó thêm một số loại cột trạng thái. Mã thông báo chỉ dẫn đến mã nhỏ giọt vụng về để đối phó với chúng.
Aaron Bertrand

Rất nhiều phụ thuộc vào mục đích của các ràng buộc duy nhất. Nếu một trường sẽ được sử dụng làm định danh, thì nó không nên là null. Trong các trường hợp (như với số VIN) trong đó các quy tắc kinh doanh sẽ đề xuất rằng khi một mặt hàng xuất hiện hai lần, một trong số chúng phải sai, nhưng một số mặt hàng có thể "không biết", một ràng buộc duy nhất không giống như cách tiếp cận phù hợp. Nếu một người có một chiếc xe có số VIN đã biết và nó xung đột với một chiếc khác trong cơ sở dữ liệu, người ta có thể biết rằng ít nhất một trong số số đó là sai, nhưng sẽ tốt hơn nếu cơ sở dữ liệu báo cáo giá trị tin cho cả hai bản ghi hơn là đoán cái đó đúng
supercat

@AaronBertrand: Có một số trường hợp trường duy nhất có thể không có giá trị null cần phải là khóa thay thế không thể được thiết lập trước khi điền vào trường (ví dụ: "ID người phối ngẫu"), nhưng trong các tình huống như rằng một ràng buộc "duy nhất" sẽ không đủ; điều cần thiết là nếu X.Spouse không phải là null, X.Spouse.Spouse = X. Ngẫu nhiên, một cái gì đó như "vợ / chồng" cũng có thể được xử lý bằng cách nói rằng hồ sơ cho một người chưa kết hôn không nên có "NULL" như một người phối ngẫu, mà thay vào đó là ID riêng của mình, trong trường hợp đó, quy tắc X.spouse.spouse = X có thể áp dụng cho tất cả mọi người.
supercat
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.