PostgreSQL 다중 열 고유 제약 조건 및 NULL 값


93

다음과 같은 테이블이 있습니다.

create table my_table (
    id   int8 not null,
    id_A int8 not null,
    id_B int8 not null,
    id_C int8 null,
    constraint pk_my_table primary key (id),
    constraint u_constrainte unique (id_A, id_B, id_C)
);

그리고 나는 (id_A, id_B, id_C)어떤 상황에서도 분명하게 보이고 싶습니다 . 따라서 다음 두 삽입물에 오류가 발생해야합니다.

INSERT INTO my_table VALUES (1, 1, 2, NULL);
INSERT INTO my_table VALUES (2, 1, 2, NULL);

그러나 설명서에 따르면 두 NULL값이 서로 비교되지 않으므로 두 인서트가 모두 오류없이 전달되므로 예상대로 작동하지 않습니다 .

이 경우 에도 내 고유 제약 조건을 어떻게 보장 id_C할 수 NULL있습니까? 실제로, 실제 질문은 "순수한 SQL"에서 이런 종류의 고유성을 보장 할 수 있습니까, 아니면 더 높은 수준에서 구현해야합니까 (필자의 경우 Java)?


그래서, 당신은 가치가 있다고 가정 (1,2,1)하고 (1,2,2)(A,B,C)열입니다. (1,2,NULL)추가가 허용 되어야합니까 ?
ypercubeᵀᴹ

A와 B는 null 일 수 없지만 C는 null이거나 양의 정수 값일 수 있습니다. 따라서 (1,2,3) 및 (2,4, null)은 유효하지만 (null, 2,3) 또는 (1, null, 4)는 유효하지 않습니다. 그리고 [(1,2, null), (1,2,3)]은 고유 제한 조건을 위반하지 않지만 [(1,2, null), (1,2, null)]은이를 제한해야합니다.
Manuel Leduc

2
해당 열에 절대로 나타나지 않는 값이 있습니까 (음수 값과 같습니까?)
a_horse_with_no_name

제약 조건에 pg로 레이블을 지정할 필요는 없습니다. 자동적으로 이름을 생성합니다. 참고로
Evan Carroll

답변:


93

순수한 SQL 에서 그렇게 할 수 있습니다 . 가지고있는 인덱스 외에 부분 고유 인덱스 작성하십시오 .

CREATE UNIQUE INDEX ab_c_null_idx ON my_table (id_A, id_B) WHERE id_C IS NULL;

이 방법으로 (a, b, c)테이블에 입력 할 수 있습니다 .

(1, 2, 1)
(1, 2, 2)
(1, 2, NULL)

그러나이 중 두 번째는 없습니다.

또는 두 개의 부분 UNIQUE인덱스를 사용하고 완전한 인덱스 (또는 제약 조건)를 사용 하지 마십시오 . 최상의 솔루션은 요구 사항의 세부 사항에 따라 다릅니다. 비교:

이것은 UNIQUE인덱스 의 단일 nullable 열에 효율적이지만 우아하지만 더 빨리 사용할 수 없습니다. 이것에 대해 논의하고 부분 인덱스와 함께 UPSERT를 사용하는 방법 :

옆으로

PostgreSQL에서 큰 따옴표없이 대소 문자혼합 한 식별자에는 사용하지 마십시오 .

당신은 할 수 고려 serial 기본 키 또는 IDENTITY (10) 이후 포스트 그레스에 있습니다. 관련 :

그래서:

CREATE TABLE my_table (
   my_table_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY  -- for pg 10+
-- my_table_id bigserial PRIMARY KEY  -- for pg 9.6 or older
 , id_a int8 NOT NULL
 , id_b int8 NOT NULL
 , id_c int8
 , CONSTRAINT u_constraint UNIQUE (id_a, id_b, id_c)
);

테이블 수명 (폐기물 및 삭제 된 행 포함) 동안 20 억 개가 넘는 행 (> 2147483647)이 예상되지 않으면 (8 바이트) integer대신 (4 바이트)를 고려하십시오 bigint.


1
문서 는이 방법을 옹호합니다 . 고유 제약 조건을 추가하면 제약 조건에 나열된 열 또는 열 그룹에 고유 B 트리 인덱스가 자동으로 생성됩니다. 일부 행만 포함하는 고유성 제한은 고유 제한 조건으로 작성할 수 없지만 고유 부분 인덱스를 작성하여 이러한 제한을 적용 할 수 있습니다.
Evan Carroll

12

나는 같은 문제가 있었고 테이블에 고유 NULL을 갖는 다른 방법을 찾았습니다.

CREATE UNIQUE INDEX index_name ON table_name( COALESCE( foreign_key_field, -1) )

필자의 경우 필드 foreign_key_field는 양의 정수이며 절대 -1이 아닙니다.

따라서 Manual Leduc에 대답하기 위해 다른 해결책은

CREATE UNIQUE INDEX  u_constrainte (COALESCE(id_a, -1), COALESCE(id_b,-1),COALESCE(id_c, -1) )

id가 -1이 아니라고 가정합니다.

부분 인덱스를 만들면 어떤 이점이 있습니까?
경우 당신은 NOT NULL 절을하지 않는 곳 id_a, id_b그리고 id_c함께 한 번만 NULL이 될 수 있습니다.
부분 인덱스를 사용하면 3 개의 필드가 두 번 이상 NULL 일 수 있습니다.


3
> 부분 인덱스를 만들면 어떤 이점이 있습니까? 이 방법을 사용하면 COALESCE복제본을 제한하는 데 효과적 일 수 있지만 인덱스는 쿼리 표현식과 일치하지 않는 표현식 인덱스로 쿼리하는 데별로 유용하지 않습니다. 즉 SELECT COALESCE(col, -1) ..., 색인을 누르지 않는 한입니다.
Bo Jeanes

@BoJeanes 성능 문제에 대한 인덱스가 만들어지지 않았습니다. 비즈니스 요구 사항을 충족시키기 위해 작성되었습니다.
Luc M

8

널 (Null)은 현재 해당 행에 대해 값이 알려지지 않았지만 나중에 알려진 경우 ( FinishDate실행중인 경우 Project) 추가되거나 해당 행에 대한 값을 적용 할 수 없음 ( EscapeVelocity블랙홀의 경우 Star) 을 의미 할 수 있습니다 .

제 생각에는 일반적으로 모든 Null을 제거하여 테이블을 정규화하는 것이 좋습니다.

귀하의 경우에는 NULLs열에 허용 하고 싶지만 하나만 NULL허용 하려고합니다 . 왜? 두 테이블간에 어떤 관계가 있습니까?

아마도 결코 나타나지 않는 것으로 알려진 특수 값 (과 같은 ) NOT NULL대신에 열을 변경하고 대신 저장할 수 있습니다 . 이렇게하면 고유성 제약 문제가 해결 될 수 있습니다 (하지만 원치 않는 다른 부작용이있을 수 있습니다. 예를 들어, "알 수 없음 / 적용하지 않음"을 의미하는 경우 열의 합계 또는 평균 계산이 왜곡됩니다. 또는 이러한 모든 계산에는 특별한 가치를 고려하고 무시하십시오.)NULL-1-1


2
내 경우에는 NULL이 실제로 NULL입니다 (id_C는 예를 들어 table_c에 대한 외래 키이므로 -1 값을 가질 수 없음). "my_table"과 "table_c"사이의 관계가 없음을 의미합니다. 따라서 기능적 의미가 있습니다. 그런데 [(1, 1,1, null), (2, 1,2, null), (3,2,4, null)]은 삽입 된 데이터의 유효한 목록입니다.
Manuel Leduc

1
모든 행에서 하나만 원하기 때문에 SQL에서 사용되는 Null이 아닙니다. -1을 table_c에 추가하거나 다른 테이블 (subtype table_c에 수퍼 타입이 됨)을 추가하여 데이터베이스 스키마를 변경할 수 있습니다.
ypercubeᵀᴹ

3
@Manuel 에게이 답변의 null에 대한 의견은 보편적으로 유지되지 않으며 많은 논쟁의 여지가 있음을 지적하고 싶습니다. 나와 같은 많은 사람들은 널이 원하는 어떤 목적 으로든 사용될 수 있다고 생각합니다 (그러나 각 필드에 대해 하나의 것을 의미 하고 필드 이름 또는 열 주석에 문서화 해야 함 )
Jack Douglas

1
열이 FOREIGN KEY 인 경우 더미 값을 사용할 수 없습니다.
Luc M

1
+1 저는 당신과 함께 있습니다 : 만약 우리가 열의 조합을 독특하게하려면,이 열의 조합이 PK 인 엔티티를 고려해야합니다. OP의 데이터베이스 스키마는 아마도 상위 테이블과 하위 테이블로 변경되어야합니다.
AK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.