2009-03-23 7 views
8

다음은 Oracle 데이터베이스 (10g)에서 실행 된 약간의 실험입니다. 오라클의 구현 편의성과는 별도로 일부 삽입이 허용되고 다른 일부는 거부되는 이유를 알 수 없습니다. , 나는 약간의 열이 알 수없는 값으로 때때로 일부 행을 가지고 의미가 있다고 가정중복을 방지하기 위해 여러 열을 제한 할 수 있지만 null 값은 무시합니까?

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 

을 방지 중복과 관련된 두 가지 사용 사례를 생각할 수 있습니다 : I 중복을 거부 할
1. 있지만 제한된 경우 동의 열의 값을 알 수 없습니다.
2. 제한된 열의 값을 알 수없는 경우에도 복제본을 거부하고 싶습니다.

은 분명히 오라클은하지만 다른 뭔가를 구현합니다
3. 중복을 거부하지만, 동의 (만) 모든 제약 열 값을 알 수없는 경우.

예를 들어, "unknown"에 대해 특별한 값을 가지며 열을 null이 아닌 값으로 만들 수 있도록 Oracle 구현을 사용하는 방법을 생각할 수 있습니다 (2). 그러나 나는 사건 (1)을 사용하는 방법을 알아낼 수 없다.

다른 말로하면 어떻게 Oracle을 이런 식으로 행동하게 할 수 있습니까?

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

완벽한 예 (플러스 그것이 내가 대답하여 필요 하나!) – orbfish

답변

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); 

기능 색인! 기본적으로 나는 무시하고자하는 모든 튜플 (즉, 수락)이 모든 null로 변환되도록해야합니다. 추악한,하지만 못생긴 엉덩이. 원하는대로 작동합니다. How to constrain a database table so only one row can have a particular value in a column?

그래서 가서 토니 앤드류스가 너무 포인트 제공 :

또 다른 질문에 대한 해결책의 도움으로 그것을 알아 냈다.:)

+0

나는이 못 생겼어 전혀 찾지 못한다. 허용 된 답변보다 훨씬 깔끔한 IMHO입니다. 두 열을 함께 뭉개지게 만들 수도 있고, 심지어 같은 데이터 유형조차도 없어도 일부 프랑켄슈타인 고유 키를 만들 수 있습니다 (다중 열의 올바른 구문을 보여주지 않으면 사용하지 않았을 수도 있음).). – orbfish

1

그런 것 같습니다.

기록하지만, 난 당신이 두 개의 컬럼에 대한 간단한 고유 인덱스가있는 경우 오라클이 그렇게 행동하는 이유를 설명하기 위해 내 단락을 떠나 그냥 위해 :

오라클은이 (1, NULL) 동의하지 않습니다를 쌍으로 열 경우 고유하게 색인됩니다.

1과 null 쌍은 "색인 가능"쌍으로 간주됩니다. 한 쌍의 두 널을 인덱싱 할 수 없기 때문에 원하는만큼 많은 널, 널 쌍을 삽입 할 수 있습니다.

(1, null)은 1을 인덱싱 할 수 있기 때문에 인덱싱됩니다. 다음 번에 (1, null)을 다시 삽입하려고하면 인덱스에 의해 1이 선택되고 고유 제한 조건이 위반됩니다.

(null, null)은 인덱싱 할 값이 없기 때문에 인덱싱되지 않습니다. 그것이 유일한 제약 조건을 위반하지 않는 이유입니다.

+0

이 오라클 함수 기반 인덱스의 이행을위한 하나의 이유이다. 비즈니스가 인덱스를 자신의 비즈니스 규칙에 맞출 수 있습니다. – DCookie

+0

그래, 난 고쳐서 :-) – Petros

7

함수 기반 인덱스보십시오 (b가 NULL ELSE THEN는 null || ''|| B의 END 인 경우는 NULL 후 NULL 인 경우 CASE)를

샌드 박스에 고유 인덱스 sandbox_idx를 생성하는 단계;

이 고양이를 껍질을 벗기는 다른 방법이 있지만 이것도 그 중 하나입니다.

2

나는 오라클 사람이 아니지만 오라클의 색인에 계산 열을 포함 할 수 있다면 아이디어가 필요합니다.

다음과 같이 계산 된 추가 열을 테이블 (및 UNIQUE 인덱스)에 추가합니다. a와 b가 모두 NULL이 아닌 경우에는 NULL이고, 그렇지 않으면 테이블의 기본 키입니다. 분명한 이유로이 추가 열을 "null 버스터 (null buster)"라고 부릅니다.

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); 

나는 2002 년의 주위에 정도 유즈넷 그룹 microsoft.public.sqlserver.programming이 예를 여러 번했다. groups.google.com에서 "nullbuster"라는 단어를 검색하면 토론을 찾을 수 있습니다. 오라클을 사용한다는 사실은별로 중요하지 않습니다.

P. SQL 서버에서,이 솔루션은 거의 필터링 된 인덱스로 대체됩니다 : 당신이 참조

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

스레드는 오라클이 당신에게이 옵션을 제공하지 않는 것이 좋습니다. 또 다른 대안 인 인덱싱 된 뷰의 가능성도 있지 않습니까? 좋은 질문의

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

내 응용 프로그램에 너무 정교하지만 좋은 답변입니다. 질문에 답변하기 위해 오라클에는 필터링 된 인덱스가 없지만 "인덱싱 된 뷰"는 오라클의 구체화 된 뷰에서 다루는 것으로 보이며 인덱싱 할 수 있으며 종종 참조 무결성을위한 것입니다. – orbfish