중복을 방지하기 위해 여러 열을 제한하지만 널 값을 무시할 수있는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/675398

문제

다음은 Oracle 데이터베이스 (10G)에서 실험 한 작은 실험입니다. (Oracle 's) 구현 편의성 외에도 일부 삽입이 수락되고 다른 삽입이 거부되는 이유를 알 수 없습니다.

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

때때로 열 값이 알려지지 않은 행이있는 경우가 종종 있다고 가정하면 중복 방지와 관련된 두 가지 사용 사례를 생각할 수 있습니다.
1. 복제를 거부하고 싶지만 제한된 열의 값을 알 수없는 경우 수락합니다.
2. 제한된 열의 값을 알 수없는 경우에도 중복을 거부하고 싶습니다.

분명히 Oracle은 다른 것을 구현합니다.
3. 복제를 거부하지만 언제 만 수락하십시오. 모두 제한된 열 값은 알려져 있지 않습니다.

Oracle의 구현을 사용하여 사례 (2)를 사용하는 방법을 생각할 수 있습니다. 그러나 나는 사례를 사용하는 방법을 알 수 없습니다 (1).

다시 말해서, 오라클은 어떻게 이렇게 행동하게 할 수 있습니까?

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
도움이 되었습니까?

해결책 2

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

기능적 인덱스! 기본적으로 나는 무시하고 싶은 모든 튜플이 모든 널로 번역되도록해야했습니다. 못 생겼지 만 엉덩이가 못 생겼습니다. 원하는대로 작동합니다.

다른 질문에 대한 해결책의 도움으로 그것을 알아 냈습니다. 데이터베이스 테이블을 제한하여 한 행만 열에서 특정 값을 가질 수있는 방법은 무엇입니까?

그러니 거기 가서 토니 앤드류스에게도 포인트를주십시오. :)

다른 팁

함수 기반 색인 시도 :

샌드 박스에서 고유 한 색인 sandbox_idx를 만듭니다 (A가 null이면 b가 null이면 null이면 null이면 null a || ','|| b 끝);

이 고양이를 피부하는 다른 방법이 있지만 이것은 그중 하나입니다.

나는 Oracle Guy는 아니지만 Oracle의 색인에 계산 된 열을 포함시킬 수 있다면 효과가있는 아이디어가 있습니다.

다음과 같이 계산 된 테이블에 추가 열을 추가하십시오. A와 B가 모두 null이 아닌 경우 NULL이며, 그렇지 않으면 테이블의 주요 키입니다. 나는 이것을 명백한 이유로 "nullbuster"라고 부릅니다.

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

이 예제는 USENET 그룹 Microsoft.public.sqlserver.programming에서 2002 년경에 여러 번 몇 번이나주었습니다. "nullbuster"라는 단어에 대해 Groups.google.com을 검색하는 경우 토론을 찾을 수 있습니다. 당신이 Oracle을 사용하고 있다는 사실은 그다지 중요하지 않습니다.

추신 SQL Server 에서이 솔루션은 필터링 된 인덱스에 의해 대체됩니다.

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

참조 된 스레드는 Oracle 이이 옵션을 제공하지 않음을 시사합니다. 또한 인덱싱 된 뷰의 가능성도 없습니까? 이것은 또 다른 대안입니까?

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

그럼 당신이 할 수 있다고 생각합니다.

그래도 기록을 위해, 나는 두 열에 간단한 고유 인덱스가 있다면 오라클이 왜 그렇게 행동하는지 설명하기 위해 단락을 남겨 둡니다.

열이 고유하게 인덱싱 된 경우 Oracle은 두 개의 (1, null) 쌍을 허용하지 않습니다.

1 쌍과 널은 "인덱스 가능한"쌍으로 간주됩니다. 한 쌍의 널 한 쌍을 색인화 할 수 없으므로 원하는만큼 널 널 짝을 삽입 할 수 있습니다.

(1, null) 1을 색인화 할 수 있으므로 색인이 표시됩니다. 다음에 (1, null)을 다시 삽입하려고하면 1이 색인에 의해 선택되고 고유 한 제약 조건이 위반됩니다.

(NULL, NULL) 색인화 할 값이 없기 때문에 색인이 표시되지 않습니다. 그렇기 때문에 독특한 제약을 위반하지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top