2010-07-30 30 views
6

Làm cách nào để tạo khóa tổng hợp trên nhiều cột, một trong số đó có thể có một số giá trị nhưng không có giá trị rỗng (hoặc một số giá trị không đổi)?Cách tạo khóa tổng hợp trên nhiều cột

Ví dụ:

PK Loc_ID  Date    Time  Cancelled 
1   1   01/01/2010  10:00AM  YES 
2   1   01/01/2010  10:00AM  YES 
3   1   01/01/2010  10:00AM  null 
4   1   01/01/2010  10:00AM  null - Not Acceptable 

Đầu vào kỷ lục thứ tư nên nâng cao một lỗi vi phạm chủ chốt composite.

+3

dụ của bạn dữ liệu không có cơ sở để từ chối hàng với PK = 4 khi nó cũng không từ chối một với PK = 2. –

+0

@ Jonathan Leffler - Đánh tôi với nó. Có cùng một câu hỏi. – Thomas

+0

Có, PK = 2 là OK. Hủy bỏ có thể có bất kỳ giá trị nhưng không null lặp lại hai lần cho cùng một locid, ngày tháng và thời gian. –

Trả lời

6

Vì vậy, những gì bạn làm gì để thực thi quy tắc chỉ không thể hủy hồ sơ cho bất kỳ hoán vị đã cho LOC_ID, DATE, TIME? Chúng ta có thể làm điều này với một chỉ số duy nhất dựa trên chức năng.

Đây là những gì chúng tôi muốn tránh:

SQL> select * from t34 
    2/

     PK  LOC_ID SOMEDATE SOMETIM CAN 
---------- ---------- ---------- ------- --- 
     1   1 01/01/2010 10:00AM YES 
     2   1 01/01/2010 10:00AM YES 
     3   1 01/01/2010 10:00AM 

SQL> insert into t34 
    2 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
    3/

1 row created. 

SQL> 

Hãy xây dựng một chỉ số để thực thi các quy tắc

SQL> rollback 
    2/

Rollback complete. 

SQL> create unique index t34_uidx 
    2 on t34 (loc_id, somedate, some_time, nvl2(cancelled, pk, null)) 
    3/

Index created. 

SQL> 

Các NVL2() chức năng là một dạng đặc biệt của CASE mà trả về số thứ hai nếu đối số đầu tiên là NOT NULL nếu không thì thứ ba. Chỉ mục sử dụng PK col làm đối số thứ hai vì nó là khóa chính và do đó duy nhất. Vì vậy, các chỉ số cho phép giá trị nhân bản của HỦY BỎ trừ khi họ là null:

SQL> insert into t34 
    2 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
    3/
insert into t34 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (APC.T34_UIDX) violated 


SQL> 
+0

wow !. Những công việc này. Cảm ơn. –

0

Tôi không chắc chắn điều này là hợp lệ trong Oracle, nhưng trong Postgresql bạn có thể làm điều này với một chỉ số đa phần một phần trên null, không bao gồm cột rỗng.

CREATE UNIQUE INDEX idx_foo 
ON example (Loc_ID, Date, Time) 
WHERE canceled IS NULL 
+0

Không, 'row2''s' cancel' IS NOT NULL, đây là chỉ mục một phần trên NULL. Tôi khá chắc chắn bạn có thể làm điều này trong Oracle Tôi chỉ không biết làm thế nào, và nghĩ rằng nó có thể là bổ sung tốt cho câu trả lời. –

+0

Phải. Thấy vậy. Tôi tin rằng SQL 2008 cũng hỗ trợ khái niệm này nhưng tôi không nghĩ rằng Oracle vẫn chưa. – Thomas

+0

Tôi không nghĩ như vậy, tôi nghĩ rằng đây có lẽ là SQL 99. Postgresql đã hỗ trợ nó trong ít nhất 8 năm. –

1

Điều này có thể được thực hiện với chỉ mục dựa trên chức năng duy nhất không? Một cái gì đó như:

create unique index ix on tb (
    loc_id, date, time, decode(cancelled, null, 1, null)); 
+0

sẽ không hoạt động trong oracle cho các trường hợp khi bị hủy là 'có' cho cùng một kết hợp loc_id, ngày tháng , thời gian. –

+0

Phiên bản APC với nvl2() là sạch hơn, và hoạt động rõ ràng, trong khi điều này chưa được kiểm tra.Chỉ vì lợi ích của riêng tôi, tôi đang đảo ngược giá trị rỗng, hiệu quả, đó là về cùng một ý định; nhưng có lẽ nên đã thực hiện 'giải mã (hủy bỏ, null, , pk)'. Giá trị ma thuật có thể là bất cứ thứ gì 'pk' sẽ không bao giờ là. Điều này rõ ràng là nguy hiểm, ... –

1

Nếu quy tắc là chỉ có một NULL hủy giá trị cho một sự kết hợp đặc biệt của LOC_ID, DATE_COL, và TIME_COL:

SQL> create table EXAMPLE 
    2 ( PK  number  not null, 
    3  LOC_ID number  not null, 
    4  DATE_COL date   null, 
    5  TIME_COL varchar2(10) null, 
    6  CANCELLED varchar2(3) null, 
    7  constraint EXAMPLE_PK primary key (PK) 
    8 ); 

Table created. 

SQL> 
SQL> create unique index EXAMPLE_UK01 on EXAMPLE 
    2 (case when CANCELLED is null then LOC_ID else null end, 
    3  case when CANCELLED is null then DATE_COL else null end, 
    4  case when CANCELLED is null then TIME_COL else null end 
    5 ); 

Index created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (1, 1, DATE '2010-01-01', '10:00AM', 'YES'); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (2, 1, DATE '2010-01-01', '10:00AM', 'YES'); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (3, 1, DATE '2010-01-01', '10:00AM', null); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (4, 1, DATE '2010-01-01', '10:00AM', null); 
INSERT INTO EXAMPLE VALUES 
* 
ERROR at line 1: 
ORA-00001: unique constraint ([schema].EXAMPLE_UK01) violated 
Các vấn đề liên quan