[sql] Postgres 고유 제한 조건과 인덱스

설명서 를 이해할 수 있듯이 다음 정의는 동일합니다.

create table foo (
    id serial primary key,
    code integer,
    label text,
    constraint foo_uq unique (code, label));

create table foo (
    id serial primary key,
    code integer,
    label text);
create unique index foo_idx on foo using btree (code, label);    

그러나 Postgres 9.4 매뉴얼다음 과 같은 메모가 있습니다 .

테이블에 고유 제한 조건을 추가하는 선호되는 방법은 ALTER TABLE ... ADD CONSTRAINT입니다. 고유 제한 조건을 시행하기 위해 인덱스를 사용하는 것은 직접 액세스해서는 안되는 구현 세부 사항으로 간주 될 수 있습니다.

(편집 :이 메모는 Postgres 9.5 설명서에서 제거되었습니다.)

좋은 스타일의 문제입니까? 이러한 변형 중 하나 (예 : 성능)를 선택하면 실질적인 결과는 무엇입니까?



답변

나는이 기본이지만 중요한 문제에 대해 약간의 의문이 있었으므로 모범으로 배우기로 결정했습니다.

의 테스트 테이블 만들어 보자 마스터 두 개의 열, con_id 고유 제한 조건으로하고 ind_id 고유 인덱스에 의해 색인.

create table master (
    con_id integer unique,
    ind_id integer
);
create unique index master_unique_idx on master (ind_id);

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_unique_idx" UNIQUE, btree (ind_id)

테이블 설명 (psql의 \ d)에서 고유 인덱스의 고유 제약 조건을 알 수 있습니다.

독창성

만약을 대비하여 독창성을 확인합시다.

test=# insert into master values (0, 0);
INSERT 0 1
test=# insert into master values (0, 1);
ERROR:  duplicate key value violates unique constraint "master_con_id_key"
DETAIL:  Key (con_id)=(0) already exists.
test=# insert into master values (1, 0);
ERROR:  duplicate key value violates unique constraint "master_unique_idx"
DETAIL:  Key (ind_id)=(0) already exists.
test=#

예상대로 작동합니다!

외래 키

이제 master 의 두 열을 참조하는 두 개의 외래 키로 세부 테이블을 정의 합니다 .

create table detail (
    con_id integer,
    ind_id integer,
    constraint detail_fk1 foreign key (con_id) references master(con_id),
    constraint detail_fk2 foreign key (ind_id) references master(ind_id)
);

    Table "public.detail"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Foreign-key constraints:
    "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

글쎄, 오류가 없습니다. 작동하는지 확인하십시오.

test=# insert into detail values (0, 0);
INSERT 0 1
test=# insert into detail values (1, 0);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk1"
DETAIL:  Key (con_id)=(1) is not present in table "master".
test=# insert into detail values (0, 1);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk2"
DETAIL:  Key (ind_id)=(1) is not present in table "master".
test=#

외래 키에서 두 열을 모두 참조 할 수 있습니다.

인덱스를 이용한 제약

기존 고유 인덱스를 사용하여 테이블 제약 조건을 추가 할 수 있습니다.

alter table master add constraint master_ind_id_key unique using index master_unique_idx;

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_ind_id_key" UNIQUE CONSTRAINT, btree (ind_id)
Referenced by:
    TABLE "detail" CONSTRAINT "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    TABLE "detail" CONSTRAINT "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

이제 열 제약 조건 설명에는 차이가 없습니다.

부분 인덱스

테이블 제약 조건 선언에서는 부분 인덱스를 만들 수 없습니다. 그것은에서 직접 제공 정의create table .... 고유 인덱스 선언에서 WHERE clause부분 인덱스를 생성하도록 설정할 수 있습니다. 또한 표현식 (열뿐만 아니라)에 대한 색인작성 하고 다른 매개 변수 (데이터 정렬, 정렬 순서, NULL 배치)를 정의 할 수도 있습니다 .

당신은 할 수없는 부분 인덱스를 사용하여 테이블에 제약 조건을 추가 할 수 있습니다.

alter table master add column part_id integer;
create unique index master_partial_idx on master (part_id) where part_id is not null;

alter table master add constraint master_part_id_key unique using index master_partial_idx;
ERROR:  "master_partial_idx" is a partial index
LINE 1: alter table master add constraint master_part_id_key unique ...
                               ^
DETAIL:  Cannot create a primary key or unique constraint using such an index.


답변

UNIQUE INDEXvs 를 사용하는 또 다른 이점은 UNIQUE CONSTRAINT쉽게 DROP/ CREATE인덱스 CONCURRENTLY할 수 있지만 제약 조건으로는 할 수 없다는 것입니다.


답변

독창성은 제약입니다. 인덱스는 주어진 값이 이미 존재하는지 판별하기 위해 인덱스가 기존의 모든 값을 신속하게 검색 할 수 있기 때문에 고유 인덱스를 작성하여 구현됩니다.

개념적으로 인덱스는 구현 세부 사항이며 고유성은 제약 조건과 만 관련되어야합니다.

전문

따라서 속도 성능은 동일해야합니다


답변

내가 만난 또 다른 것은 제약 조건이 아닌 고유 인덱스에서 SQL 표현식을 사용할 수 있다는 것입니다.

따라서 이것은 작동하지 않습니다.

CREATE TABLE users (
    name text,
    UNIQUE (lower(name))
);

그러나 다음 작품.

CREATE TABLE users (
    name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));


답변

다양한 사람들이 고유 제약 조건보다 고유 인덱스의 이점을 제공했기 때문에 여기에는 단점이 있습니다. 고유 제약 조건은 연기 될 수 있고 (트랜잭션 끝에서만 확인 가능) 고유 인덱스는 불가능합니다.


답변

나는 문서에서 이것을 읽었다.

table_constraint 추가 [유효하지 않음]

이 형식은와 동일한 구문과 CREATE TABLE옵션을 사용하여 테이블에 새 제약 조건을 추가합니다. 이 옵션 NOT VALID은 현재 외래 키 제약 조건에만 허용됩니다. 제한 조건이 표시 NOT VALID되면 테이블의 모든 행이 제한 조건을 만족하는지 검증하기위한 잠재적으로 긴 초기 점검이 생략 됩니다. 후속 삽입 또는 업데이트 에 대해 제한 조건이 계속 적용 됩니다 (즉, 참조 된 테이블에 일치하는 행이 없으면 실패합니다). 그러나 데이터베이스 VALIDATE CONSTRAINT 옵션을 사용하여 유효성이 검증 될 때까지 테이블의 모든 행에 대해 제한 조건이 있다고 가정 하지 않습니다 .

제약 조건을 추가하여 이것이 “부분 고유성”이라고 생각합니다.

그리고 독창성을 보장하는 방법에 대해 :

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

참고 : 테이블에 고유 제한 조건을 추가하는 선호되는 방법은 ALTER TABLE… ADD CONSTRAINT입니다. 고유 제한 조건을 시행하기 위해 인덱스를 사용하는 것은 직접 액세스해서는 안되는 구현 세부 사항으로 간주 될 수 있습니다. 그러나 고유 한 열에 인덱스를 수동으로 만들 필요가 없다는 것을 알아야합니다. 그렇게하면 자동으로 생성 된 인덱스가 복제됩니다.

따라서 고유성을 보장하기 위해 인덱스를 작성하는 제한 조건을 추가해야합니다.

이 문제를 어떻게 알 수 있습니까?

“제약”은 문법적 으로이 칼럼이 고유해야한다는 것을 목표로 하며 , 법과 규칙을 확립합니다. “인덱스”는 “구현 방법, 고유성을 달성하는 방법, 구현에있어 고유 한 의미”에 대해 의미 론적 입니다. 따라서 Postgresql이 구현하는 방식은 매우 논리적입니다. 먼저 열이 고유해야한다고 선언 한 다음 Postgresql은 고유 인덱스 추가 구현을 추가합니다 .


답변

잠금에는 차이가 있습니다.
인덱스를 추가해도 테이블에 대한 읽기 액세스는 차단되지 않습니다.
제약 조건을 추가하면 ALTER TABLE을 통해 추가되므로 테이블 잠금이 적용되므로 모든 선택이 차단 됩니다.