Làm thế nào để tạo một chỉ mục duy nhất trên một cột NULL?


101

Tôi đang sử dụng SQL Server 2005. Tôi muốn giới hạn các giá trị trong một cột là duy nhất, đồng thời cho phép NULLS.

Giải pháp hiện tại của tôi liên quan đến một chỉ mục duy nhất trên một chế độ xem như vậy:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

Bất kỳ ý tưởng tốt hơn?


16
không có cơ hội sử dụng sql 2008? bạn có thể tạo chỉ mục được lọc bằng cách sử dụng 'where'
Simon_Weaver

3
Bạn không có nghĩa là duy nhất, cho phép NULL , dường như bạn có nghĩa là duy nhất, nhưng bao gồm nhiều NULL . Nếu không, NULL được lập chỉ mục giống như bất kỳ giá trị nào khác và ràng buộc tính duy nhất hoạt động như mong đợi - không theo tiêu chuẩn SQL, như @pst đã đề cập trong nhận xét bên dưới.
Suncat2000

Câu trả lời:


26

Chắc chắn rằng bạn không thể làm điều đó, vì nó vi phạm mục đích của sự độc đáo.

Tuy nhiên, người này dường như có một công việc tốt: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
Có vẻ như nội dung của liên kết bạn cung cấp đã thực sự được sao chép (một phần) mà không cần ghi công từ đây: decipherinfosys.wordpress.com/2007/11/30/…
Tom Juergens

77
Tôi không đồng ý rằng nó "vi phạm mục đích duy nhất" - NULL là một giá trị đặc biệt trong SQL (theo nhiều cách tương tự với NaN) và cần được xử lý phù hợp. Nó thực sự là một lỗi trong SQL Server để đáp ứng các thông số kỹ thuật SQL khác nhau: đây là liên kết cho yêu cầu "triển khai đúng" cho những gì nó đáng giá: connect.microsoft.com/SQLServer/feedback/details/299229/… .

5
để tham khảo vào năm 2008, bạn có thể thực hiện TẠO CHỈ SỐ DUY NHẤT foo TRÊN dbo.bar (key) WHERE key IS NOT NULL;
niico

2
Tôi cũng không đồng ý với "vi phạm mục đích của duy nhất", NULL không bằng NULL, vì vậy bạn có thể tạo chỉ mục duy nhất trên cột nullable và chèn nhiều null.
Wodzu

105

Sử dụng SQL Server 2008, bạn có thể tạo chỉ mục được lọc: http://msdn.microsoft.com/en-us/library/cc280372.aspx . (Tôi thấy Simon đã thêm điều này như một nhận xét, nhưng nghĩ rằng nó xứng đáng có câu trả lời riêng vì nhận xét này rất dễ bị bỏ sót.)

Một tùy chọn khác là trình kích hoạt để kiểm tra tính duy nhất, nhưng điều này có thể ảnh hưởng đến hiệu suất.


84
create unique index UIX on MyTable (Column1) where Column1 is not null
Jørn Schou-Rode

1
Lưu ý: hiện tại SQL Server Management Studio dường như không biết cách tạo các chỉ mục như vậy, vì vậy nếu sau này bạn sửa đổi bảng, nó sẽ bị nhầm lẫn và cố gắng loại bỏ nó, vì vậy hãy nhớ tạo lại nó
Simon_Weaver

3
Có vẻ như Microsoft đã cập nhật SSMS để hỗ trợ điều này. Tôi có SSMS 10.50.1617 và trong hộp thoại Thuộc tính chỉ mục, bạn có thể chọn trang Bộ lọc để chỉnh sửa bộ lọc. ví dụ: "([Column1] IS NOT NULL)"
Phil Haselden

5
Cho phép nhiều null trong một chỉ mục và lọc null từ một chỉ mục là những việc riêng biệt. Việc lọc một chỉ mục thực sự loại trừ các bản ghi khỏi chỉ mục, trong khi các giải pháp khác chuyển đổi giá trị null thành một giá trị duy nhất hữu ích. Hãy nhận biết sự khác biệt.
Suncat2000

Nếu bạn sử dụng thủ tục lưu trữ trên một bảng với một chỉ số lọc như vậy, chắc chắn rằng ANSI_NULLSON, nếu không bạn sẽ nhận được một lỗi khi cố gắng để chèn dữ liệu.
Arne

71

Thủ thuật cột được tính toán được biết đến rộng rãi như một "nullbuster"; ghi chú của tôi ghi có Steve Kass:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

Đây trông giống như một thủ thuật thú vị. Điều kỳ lạ là tìm kiếm nullbuster không mang lại quá nhiều thứ. Tôi đang tự hỏi liệu điều này có hữu ích cho việc tăng tốc độ tìm kiếm hay không - thay vì một cột được tính toán chỉ 1 và 0 cho giá trị rỗng hay không, nếu việc sử dụng PK cung cấp cho chỉ mục thứ gì đó để làm việc nhiều hơn? Cuối tuần này đi test trên bàn lớn xem sao.
David Storfer

@DavidStorfer, bạn không thể làm điều đó vì bạn có thể có xung đột giữa các ID của hai bảng khác nhau.
user393274,

Cải tiến: ISNULL (X, CONVERT (VARCHAR (10), pk))
Faiz

5
@Faiz: Cải tiến là trong mắt của người xem. Tôi thích giao diện của bản gốc hơn.
onedaywhen

@NunoG, đây phải là câu trả lời được chấp nhận vì nó cung cấp một giải pháp tốt phù hợp với yêu cầu của bạn, thay vì chỉ liên kết một trang web bên ngoài có thể biến mất.
Frédéric

-3

Nói một cách chính xác, một cột nullable duy nhất (hoặc tập hợp các cột) có thể là NULL (hoặc một bản ghi NULL) chỉ một lần, vì có cùng giá trị (và điều này bao gồm NULL) nhiều lần rõ ràng vi phạm ràng buộc duy nhất.

Tuy nhiên, điều đó không có nghĩa là khái niệm "cột nullable duy nhất" là hợp lệ; để thực sự triển khai nó trong bất kỳ cơ sở dữ liệu quan hệ nào, chúng ta chỉ cần lưu ý rằng loại cơ sở dữ liệu này được chuẩn hóa để hoạt động bình thường và việc chuẩn hóa thường bao gồm việc bổ sung một số bảng bổ sung (không phải thực thể) để thiết lập mối quan hệ giữa các thực thể .

Hãy làm một ví dụ cơ bản chỉ xem xét một "cột nullable duy nhất", thật dễ dàng để mở rộng nó thành nhiều cột như vậy.

Giả sử chúng ta thông tin được đại diện bởi một bảng như sau:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Chúng ta có thể làm điều đó bằng cách đặt uniqnull ra và thêm một bảng thứ hai để thiết lập mối quan hệ giữa các giá trị uniqnull và the_entity (thay vì có uniqnull "bên trong" the_entity):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

Để liên kết giá trị uniqnull với một hàng trong_entity, chúng ta cũng cần thêm một hàng trong_tương quan.

Đối với các hàng trong the_entity không có giá trị uniqnull nào được liên kết (tức là đối với những hàng mà chúng ta đặt NULL trong the_entity_incorrect), chúng ta chỉ cần thêm một hàng trong the_relation.

Lưu ý rằng các giá trị cho uniqnull sẽ là duy nhất cho tất cả các mối tương quan và cũng lưu ý rằng đối với mỗi giá trị trong the_entity có thể có nhiều nhất một giá trị trong mối quan hệ, vì các khóa chính và khóa ngoại trên đó thực thi điều này.

Sau đó, nếu giá trị 5 cho uniqnull được liên kết với id the_entity là 3, chúng ta cần:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

Và, nếu giá trị id là 10 cho the_entity không có đối chứng uniqnull, chúng tôi chỉ thực hiện:

start transaction;
insert into the_entity (id) values (10); 
commit;

Để chuẩn hóa thông tin này và lấy dữ liệu mà bảng như the_entity_incorrect sẽ lưu giữ, chúng ta cần:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

Toán tử "kết nối bên ngoài bên trái" đảm bảo tất cả các hàng từ the_entity sẽ xuất hiện trong kết quả, đặt NULL vào cột uniqnull khi không có cột phù hợp nào trong the_relation.

Hãy nhớ rằng, bất kỳ nỗ lực nào dành ra trong vài ngày (hoặc vài tuần hoặc vài tháng) để thiết kế một cơ sở dữ liệu được chuẩn hóa tốt (và các chế độ xem và thủ tục không chuẩn hóa tương ứng) sẽ giúp bạn tiết kiệm nhiều năm (hoặc nhiều thập kỷ) đau đớn và lãng phí tài nguyên.


6
Như đã nêu trên nhận xét của câu trả lời được chấp nhận với năm mươi phiếu ủng hộ, nó sẽ được MS Sql Server hỗ trợ để có nhiều cột rỗng trong một cột được lập chỉ mục là duy nhất. Đó là một thất bại trong việc triển khai các tiêu chuẩn SQL không cho phép như vậy. Null không phải là một giá trị, null không bằng null, đó là một quy tắc SQL cơ bản từ nhiều năm nay. Vì vậy, câu đầu tiên của bạn là sai và hầu hết người đọc sẽ không buồn đọc tiếp.
Frédéric
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.