Postgres 고유 제한 조건과 인덱스


157

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

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 설명서에서 제거되었습니다.)

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


23
유일한 차이점은 고유 제한 조건에 대해서는 외래 키를 작성할 수 있지만 고유 인덱스에는 대해서는 외래 키를 작성할 수 있다는 것입니다.
a_horse_with_no_name 13:14에

29
다른 방법으로 ( 최근 다른 질문에서 나온 것처럼) 이점은 "Unique (foo) where bar is Null"과 같은 부분 고유 인덱스를 가질 수 있다는 것 입니다. AFAIK, 제약 조건으로는 그렇게 할 수 없습니다.
IMSoP

3
@a_horse_with_no_name 언제 이런 일이 있었는지 잘 모르겠지만 더 이상 사실이 아닙니다. 이 SQL 바이올린은 고유 키에 대한 외래 키 참조를 허용합니다. sqlfiddle.com/#!17/20ee9 ; 편집 : 고유 인덱스에 '필터'를 추가하면 작동이 중지됩니다 (예상대로)
user1935361

1
postgres 문서에서 : PostgreSQL은 테이블에 대해 고유 제한 조건 또는 기본 키가 정의 될 때 자동으로 고유 인덱스를 작성합니다. postgresql.org/docs/9.4/static/indexes-unique.html
maggu

@ user1935361과 동의합니다. 최소한 PG 10의 고유 인덱스에 외래 키를 만들 수 없다면 오래 전에이 문제가 발생했을 것입니다.
Andy

답변:


132

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

의 테스트 테이블 만들어 보자 마스터 두 개의 열, 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.

실제 정보입니까? 특히 부분 인덱스에 대해
anatol

1
@anatol-그렇습니다.
클라인

30

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


4
AFAIK 고유 인덱스를 동시에 삭제할 수는 없습니다. postgresql.org/docs/9.3/static/sql-dropindex.html "이 옵션을 사용할 때주의해야 할 몇 가지주의 사항이 있습니다. 하나의 색인 이름 만 지정할 수 있으며 CASCADE 옵션은 지원되지 않습니다 (따라서 색인 UNIQUE 또는 PRIMARY KEY 제약 조건을 지원하는이 방법은 삭제할 수 없습니다.) "
Rafał Cieślak

15

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

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

전문

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


4

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

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

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

그러나 다음 작품.

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

citext확장명 을 사용합니다 .
15

@ceving은 사용 사례에 따라 다릅니다. 대소 문자를 구분하지 않는 고유성을 보장하면서 케이스를 보존하려는 경우가 있습니다.
Sampson Crowley

2

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


모든 고유 제약 조건에 고유 인덱스가 있다고 가정하면 어떻게 할 수 있습니까?
Chris

1
인덱스에는 지연을위한 API가 없기 때문에 제약 조건 만 있으므로 지연 제한 메커니즘이 고유 제한 조건을 지원하기 위해 커버 아래에 있지만 인덱스를 지연 가능으로 선언하거나 지연시킬 수있는 방법은 없습니다.
Masklinn

0

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

table_constraint 추가 [유효하지 않음]

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

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

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

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

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

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

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

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


1
"그래서 제약 조건을 추가하여 이것이"부분 고유성 "이라고 생각합니다." 인덱스는 where절을 통해 레코드의 잘 정의 된 서브 세트에만 적용 할 수 있으므로 레코드가 일부 기준을 충족하는 고유 한 IFF임을 정의 할 수 있습니다. 이렇게하면 생성중인 제약 조건보다 오래된 정의되지 않은 레코드 집합에 대한 제약 조건이 비활성화됩니다. 그것은 완전히 다른, 그리고 후자는 상당히입니다 적은 그것이 내가 생각 진보적 마이그레이션에 편리하지만, 유용합니다.
Masklinn

0

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


0

인덱스가 아닌 제약 조건으로 만 수행 할 수있는 매우 사소한 일은 ON CONFLICT ON CONSTRAINT조항을 사용하는 것입니다 ( 이 질문도 참조하십시오 ).

작동하지 않습니다.

CREATE TABLE T (a INT PRIMARY KEY, b INT, c INT);
CREATE UNIQUE INDEX u ON t(b);

INSERT INTO T (a, b, c)
VALUES (1, 2, 3)
ON CONFLICT ON CONSTRAINT u
DO UPDATE SET c = 4
RETURNING *;

다음을 생성합니다.

[42704]: ERROR: constraint "u" for table "t" does not exist

인덱스를 제약 조건으로 바꾸십시오.

DROP INDEX u;
ALTER TABLE t ADD CONSTRAINT u UNIQUE (b);

그리고 INSERT진술은 이제 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.