2009-03-23 18 views
8

Đây là một thử nghiệm nhỏ mà tôi đã chạy trong cơ sở dữ liệu Oracle (10g). Ngoài sự thuận tiện của Oracle (Oracle), tôi không thể tìm ra lý do tại sao một số insertions được chấp nhận và những người khác bị từ chối.Làm cách nào để có thể hạn chế nhiều cột để ngăn trùng lặp, nhưng bỏ qua các giá trị rỗng?

create table sandbox(a number(10,0), b number(10,0)); 
create unique index sandbox_idx on sandbox(a,b); 

insert into sandbox values (1,1); -- accepted 
insert into sandbox values (1,2); -- accepted 
insert into sandbox values (1,1); -- rejected 

insert into sandbox values (1,null); -- accepted 
insert into sandbox values (2,null); -- accepted 
insert into sandbox values (1,null); -- rejected 

insert into sandbox values (null,1); -- accepted 
insert into sandbox values (null,2); -- accepted 
insert into sandbox values (null,1); -- rejected 

insert into sandbox values (null,null); -- accepted 
insert into sandbox values (null,null); -- accepted 

Giả sử rằng nó làm cho tinh thần để thỉnh thoảng có một số hàng với một số cột giá trị không rõ, tôi có thể nghĩ đến hai trường hợp sử dụng có thể liên quan đến các bản sao ngăn chặn:
1. Tôi muốn từ chối bản sao, nhưng chấp nhận khi bất kỳ hạn chế giá trị của cột không xác định.
2. Tôi muốn từ chối các bản sao, ngay cả trong trường hợp giá trị của một cột bị hạn chế không xác định.

Rõ ràng Oracle thực hiện một cái gì đó khác nhau mặc dù:
3. Từ chối bản sao, nhưng chấp nhận (chỉ) khi tất cả giá trị cột hạn chế chưa được biết.

Tôi có thể nghĩ cách để sử dụng triển khai của Oracle để sử dụng trường hợp (2) - ví dụ, có giá trị đặc biệt cho "không xác định" và làm cho các cột không thể rỗng. Nhưng tôi không thể tìm ra cách để sử dụng trường hợp (1).

Nói cách khác, làm cách nào để làm cho Oracle hoạt động như thế này?

create table sandbox(a number(10,0), b number(10,0)); 
create unique index sandbox_idx on sandbox(a,b); 

insert into sandbox values (1,1); -- accepted 
insert into sandbox values (1,2); -- accepted 
insert into sandbox values (1,1); -- rejected 

insert into sandbox values (1,null); -- accepted 
insert into sandbox values (2,null); -- accepted 
insert into sandbox values (1,null); -- accepted 

insert into sandbox values (null,1); -- accepted 
insert into sandbox values (null,2); -- accepted 
insert into sandbox values (null,1); -- accepted 

insert into sandbox values (null,null); -- accepted 
insert into sandbox values (null,null); -- accepted 
+0

Ví dụ hoàn hảo về câu hỏi hay (cộng với câu hỏi tôi cần trả lời!) – orbfish

Trả lời

7
create unique index sandbox_idx on sandbox 
(case when a is null or b is null then null else a end, 
    case when a is null or b is null then null else b end); 

Chỉ mục chức năng! Về cơ bản tôi chỉ cần đảm bảo tất cả các bộ dữ liệu tôi muốn bỏ qua (tức là - chấp nhận) được dịch sang tất cả các giá trị rỗng. Xấu xí, nhưng không phải mông xấu xí. Hoạt động như mong muốn.

đặn nó ra với sự giúp đỡ của một giải pháp cho một câu hỏi khác: How to constrain a database table so only one row can have a particular value in a column?

Vì vậy, đến đó và cung cấp cho Tony Andrews chỉ quá.:)

+0

Tôi không thấy điều này xấu xí chút nào. IMHO sạch hơn nhiều so với câu trả lời được chấp nhận, có thể gắn 2 cột lại với nhau, có thể không phải cùng một kiểu dữ liệu, để tạo một số khóa Frankenstein duy nhất (không phải là tôi sẽ không sử dụng nó nếu bạn không hiển thị đúng cú pháp cho nhiều cột). – orbfish

1

Tôi đoán bạn có thể sau đó.

Chỉ cần cho các hồ sơ, mặc dù tôi rời khỏi đoạn của tôi để giải thích tại sao Oracle cư xử như vậy nếu bạn có một chỉ số duy nhất đơn giản trên hai cột:

Oracle sẽ không bao giờ chấp nhận hai (1, null) cặp nếu các cột được lập chỉ mục duy nhất.

Một cặp 1 và một giá trị rỗng, được coi là cặp "có thể lập chỉ mục". Một cặp hai null không thể được lập chỉ mục, đó là lý do tại sao nó cho phép bạn chèn nhiều null, null cặp như bạn muốn.

(1, null) được lập chỉ mục vì 1 có thể được lập chỉ mục. Lần tới bạn cố gắng chèn (1, null) một lần nữa, 1 được chọn bởi chỉ mục và ràng buộc duy nhất bị vi phạm.

(null, null) không được lập chỉ mục vì không có giá trị nào được lập chỉ mục. Đó là lý do tại sao nó không vi phạm ràng buộc duy nhất.

+0

Đây chỉ là một lý do để triển khai các chỉ mục dựa trên chức năng trong Oracle. Nó cho phép doanh nghiệp điều chỉnh chỉ mục theo quy tắc kinh doanh của riêng họ. – DCookie

+0

Có, tôi đã sửa chữa :-) – Petros

7

Hãy thử một chỉ số dựa trên chức năng:

tạo độc đáo sandbox_idx chỉ mục trên sandbox (CASE KHI một IS NULL THEN NULL KHI b IS NULL THEN ELSE NULL một || '' || b END);

Có nhiều cách khác để làm da mèo này, nhưng đây là một trong số chúng.

2

Tôi không phải là một anh chàng Oracle, nhưng đây là một ý tưởng nên hoạt động, nếu bạn có thể bao gồm một cột được tính toán trong một chỉ mục trong Oracle.

Thêm cột bổ sung vào bảng của bạn (và chỉ số UNIQUE) được tính như sau: NULL nếu cả a và b là không NULL, và đó là khóa chính của bảng nếu không. Tôi gọi cột bổ sung này là "nullbuster" vì những lý do rõ ràng.

alter table sandbox add nullbuster as 
    case when a is null or b is null then pk else null end; 
create unique index sandbox_idx on sandbox(a,b,pk); 

Tôi đưa ví dụ này một số lần khoảng năm 2002 hoặc lâu hơn trong microsoft.public.sqlserver.programming nhóm Usenet. Bạn có thể tìm thấy các cuộc thảo luận nếu bạn tìm kiếm groups.google.com cho từ "nullbuster". Thực tế là bạn đang sử dụng Oracle không quan trọng lắm.

P.S. Trong SQL Server, giải pháp này được khá nhiều thay thế bởi chỉ số lọc:

create unique index sandbox_idx on sandbox(a,b) 
(where a is not null and b is not null); 

Các chủ đề bạn tham khảo gợi ý rằng Oracle không cung cấp cho bạn tùy chọn này. Liệu nó cũng không có khả năng của một cái nhìn được lập chỉ mục, đó là một lựa chọn khác?

create view sandbox_for_unique as 
select a, b from sandbox 
where a is not null and b is not null; 

create index sandbox_for_unique_idx on sandbox_for_unique(a,b); 
+0

Câu trả lời hay mặc dù hơi quá baroque cho ứng dụng của tôi. Để trả lời các câu hỏi của bạn, Oracle không có các chỉ mục được lọc nhưng "chế độ xem được lập chỉ mục" của bạn dường như được bao quát bởi các khung nhìn vật chất hóa trong Oracle, có thể được lập chỉ mục và thường là tính toàn vẹn tham chiếu. – orbfish