Уникальное ограничение 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);
однако вы можете прочитать в примечании:предпочтительный способ, чтобы добавить ограничение уникальности для таблицы Alter таблицы ... ДОБАВИТЬ ОГРАНИЧЕНИЕ. Использование индексов для применения уникальных ограничений можно рассматривать как деталь реализации, к которой не следует обращаться напрямую.
Это только вопрос хорошего стиля? Каковы практические последствия выбора один из этих вариантов (например, в производительности)?
4 ответа:
у меня были некоторые сомнения по этому основному, но важному вопросу, поэтому я решил учиться на примере.
давайте создадим тестовую таблицу мастер С двумя колонками, 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)
в описании таблицы (\d в psql) вы можете определить уникальное ограничение из уникального индекса.
уникальность
давайте проверим уникальность, просто в случай.
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=#
он работает, как и ожидалось!
внешние ключи
теперь определим деталь таблица с двумя внешними ключами, ссылающимися на наши два столбца в мастер.
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
для создания частичного индекса. Вы также можете создать индекс по выражению (не только по столбцу) и определить некоторые другие параметры (параметры сортировки, порядок сортировки, размещение нулей).вы не можете добавить ограничение таблицы с помощью частичного индекса.
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 INDEX
и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));