Postgres benzersiz kısıtlama ve dizin karşılaştırması


157

Anlayabildiğim gibi Belgeleri aşağıdaki tanımlar eşdeğerdir:

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

Ancak, Postgres 9.4 kılavuzundaki şöyle diyor:

Tabloya benzersiz bir kısıtlama eklemenin tercih edilen yolu ALTER TABLE ... ADD CONSTRAINT. Benzersiz kısıtlamaları uygulamak için dizinlerin kullanılması, doğrudan erişilmemesi gereken bir uygulama ayrıntısı olarak düşünülebilir.

(Düzenle: bu not Postgres 9.5 içeren kılavuzdan kaldırılmıştır.)

Sadece iyi bir stil midir? Bu değişkenlerden birini seçmenin pratik sonuçları nelerdir (örneğin performansta)?


23
(Tek) pratik fark, benzersiz bir kısıtlama için yabancı bir anahtar oluşturabilmenizdir, ancak benzersiz bir dizin için değil.
a_horse_with_no_name

29
Diğer bir avantaj da ( son zamanlarda başka bir soruda görüldüğü gibi), "Eşsiz (foo) Çubuğun Boş Olduğu" gibi kısmi benzersiz bir dizine sahip olabilmenizdir . AFAIK, bunu bir kısıtlamayla yapmanın bir yolu yok.
IMSoP

3
@a_horse_with_no_name Bunun ne zaman olduğundan emin değilim, ama bu artık doğru gibi görünmüyor. Bu SQL keman benzersiz bir dizine yabancı anahtar başvuruları sağlar: sqlfiddle.com/#!17/20ee9 ; DÜZENLEME: benzersiz dizine bir 'filtre' eklemek, bunun çalışmayı durdurmasına neden olur (beklendiği gibi)
user1935361

1
postgres belgelerinden: Bir tablo için benzersiz bir kısıtlama veya birincil anahtar tanımlandığında PostgreSQL otomatik olarak benzersiz bir dizin oluşturur. postgresql.org/docs/9.4/static/indexes-unique.html
maggu

@ User1935361 ile hemfikirim, benzersiz bir indeks için yabancı bir anahtar oluşturmak mümkün olmasaydı (en azından PG 10 ile) Ben uzun zaman önce bu sorunla karşılaşırdım.
Andy

Yanıtlar:


132

Bu temel ama önemli konuda bazı şüphelerim vardı, bu yüzden örnek olarak öğrenmeye karar verdim.

Benzersiz sütunla con_id ve benzersiz dizinle ind_id dizinli iki sütunlu test tablosu yöneticisi oluşturalım .

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)

Tablo açıklamasında (psql cinsinden \ d) benzersiz dizinden benzersiz bir kısıtlama anlatabilirsiniz.

benzersizlik

Her ihtimale karşı benzersizliği kontrol edelim.

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=#

Beklendiği gibi çalışıyor!

Yabancı anahtarlar

Şimdi , master'daki iki sütunumuza değinen iki yabancı anahtar içeren ayrıntı tablosunu tanımlayacağız .

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)

Hata yok. Çalıştığından emin olalım.

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=#

Her iki sütuna da yabancı anahtarlarda başvurulabilir.

Dizin kullanarak kısıtlama

Mevcut benzersiz dizini kullanarak tablo kısıtı ekleyebilirsiniz.

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)

Şimdi sütun kısıtlamaları açıklaması arasında fark yoktur.

Kısmi dizinler

Tablo kısıtlaması bildiriminde kısmi dizin oluşturamazsınız. Bu doğrudan gelir tanımı içinde create table .... Benzersiz dizin bildiriminde WHERE clausekısmi dizin oluşturmak için ayarlayabilirsiniz . Ayrıca dizin oluşturabilirsiniz ifadede (yalnızca sütunda değil) ve diğer bazı parametreleri de tanımlayabilirsiniz (harmanlama, sıralama düzeni, NULL yerleştirme).

Sen olamaz kısmi dizini kullanarak tablo kısıtlaması ekleyin.

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.

gerçek bilgi mi? özellikle kısmi endeksler hakkında
anatol

1
@anatol - evet, öyle.
klin

30

Kullanmanın Bir daha avantaj UNIQUE INDEXVS. UNIQUE CONSTRAINTolduğunu yapabilirsiniz kolayca DROP/ CREATEbir dizin CONCURRENTLYise bir kısıtlama ile bunu yapamazsınız.


4
AFAIK, aynı anda benzersiz bir indeks bırakmak mümkün değildir. postgresql.org/docs/9.3/static/sql-dropindex.html "Bu seçeneği kullanırken dikkat edilmesi gereken birkaç uyarı vardır. Yalnızca bir dizin adı belirtilebilir ve CASCADE seçeneği desteklenmez. (Bu nedenle, bir dizin EŞSİZ veya BİRİNCİL ANAHTAR kısıtlamayı destekleyen bu şekilde düşürülemez.) "
Rafał Cieślak

15

Benzersizlik bir kısıtlamadır. Bir indeks, belirli bir değerin zaten mevcut olup olmadığını belirlemek için mevcut tüm değerleri hızlı bir şekilde arayabildiğinden, benzersiz bir dizin oluşturulması yoluyla uygulanır.

Kavramsal olarak endeks bir uygulama detayıdır ve teklik sadece kısıtlamalarla ilişkilendirilmelidir.

Tam metin

Yani hız performansı aynı olmalı


4

Karşılaştığım başka bir şey, sql ifadelerini benzersiz dizinlerde kullanabilmeniz, ancak kısıtlamalarda kullanmamanızdır.

Yani, bu işe yaramaz:

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

ancak işleri takip etmek.

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

citextUzantıyı kullanırdım .
ceving

@ceving, kullanım durumuna bağlıdır. bazen büyük / küçük harfe duyarlı olmayan benzersizliği sağlarken muhafazayı korumak istersiniz
Sampson Crowley

2

Çeşitli insanlar benzersiz kısıtlamalardan benzersiz kısıtlamalara göre avantaj sağladığından, bir dezavantaj vardır: benzersiz bir kısıtlama ertelenebilir (yalnızca işlemin sonunda kontrol edilebilir), benzersiz bir dizin olamaz.


Tüm benzersiz kısıtlamaların benzersiz bir dizini olduğu düşünüldüğünde bu nasıl olabilir?
Chris

1
Endekslerin erteleme için bir API'sı olmadığından, yalnızca kısıtlamalar vardır, bu nedenle erteleme makineleri benzersiz kısıtlamaları desteklemek için kapak altında bulunurken, bir dizini ertelenebilir olarak bildirmenin veya ertelemenin bir yolu yoktur.
Masklinn

0

Bunu dokümanda okudum:

Table_constraint EKLE [VALID DEĞİL]

Bu form , şu anda yalnızca yabancı anahtar kısıtlamaları için izin verilen CREATE TABLEseçenekle aynı sözdizimini kullanan bir tabloya yeni bir kısıtlama ekler NOT VALID. Kısıtlama işaretlenirse NOT VALID, tablodaki tüm satırların kısıtlamayı karşıladığını doğrulamak için potansiyel olarak uzun olan ilk kontrol atlanır . Kısıtlama, sonraki eklemelere veya güncellemelere karşı yine de uygulanacaktır (yani, referans verilen tabloda eşleşen bir satır olmadığı sürece başarısız olurlar). Ama veritabanı olmaz o VALIDATE CONSTRAıNT'I seçeneğini kullanarak doğrulandıktan kadar kısıt, tablodaki tüm satırlar için geçerlidir varsayalım.

Bu yüzden bir kısıtlama ekleyerek buna "kısmi teklik" adını verdiğinizi düşünüyorum.

Ve benzersizliğin nasıl sağlanacağı hakkında:

Benzersiz bir kısıt eklenmesi, otomatik olarak kısıtta listelenen sütun veya sütun grubunda benzersiz bir B-ağacı dizini oluşturur. Yalnızca bazı satırları kapsayan benzersizlik kısıtlaması benzersiz bir kısıtlama olarak yazılamaz, ancak benzersiz bir kısmi dizin oluşturarak böyle bir kısıtlamanın uygulanması mümkündür.

Not: Bir tabloya benzersiz bir kısıtlama eklemenin tercih edilen yolu ALTER TABLE… CONSTRAINT EKLEMEKTİR. Benzersiz kısıtlamaları zorlamak için dizinlerin kullanılması, doğrudan erişilmemesi gereken bir uygulama ayrıntısı olarak düşünülebilir. Bununla birlikte, benzersiz sütunlarda manuel olarak dizin oluşturmaya gerek olmadığının farkında olunmalıdır; bunu yapmak otomatik olarak oluşturulan dizini çoğaltır.

Bu nedenle, benzersizliği sağlamak için bir dizin oluşturan bir kısıtlama eklemeliyiz.

Bu sorunu nasıl görüyorum?

"Kısıtlama" , bu sütunun benzersiz olması gerektiğini gramatik olarak sağlamayı, bir yasa, kural oluşturur; "index" anlamsaldır , "nasıl uygulanır, benzersizliğe nasıl ulaşılır, uygulama söz konusu olduğunda benzersiz ne anlama gelir" hakkında. Bu nedenle, Postgresql'in onu uygulama şekli çok mantıklı: ilk olarak, bir sütunun benzersiz olması gerektiğini beyan edersiniz, ardından Postgresql sizin için benzersiz bir dizin ekleme uygulamasını ekler .


1
"Bu yüzden bir kısıtlama ekleyerek buna" kısmi teklik "adını verdiğinizi düşünüyorum." dizinler, whereyan tümce yoluyla yalnızca kayıtların iyi tanımlanmış bir alt kümesine uygulanabilir , böylece kayıtların bazı ölçütleri karşıladıkları benzersiz IFF olduğunu tanımlayabilirsiniz. Bu, yalnızca oluşturulan kısıtlamanın önüne geçen tanımsız bir kayıt kümesi için kısıtlamaları devre dışı bırakır. Tamamen farklı ve ikincisi, sanırım ilerici göçler için uygun olsa da , önemli ölçüde daha az kullanışlıdır.
Masklinn

0

Kilitlemede bir fark var.
Dizin eklemek tabloya okuma erişimini engellemez.
Bir kısıtlama eklemek, ALTER TABLE ile eklendiğinden bir tablo kilidi koyar (böylece tüm seçimler engellenir) .


0

Dizinlerle değil, yalnızca kısıtlamalarla yapılabilecek çok küçük bir şey, tümceyi kullanmaktır ON CONFLICT ON CONSTRAINT( ayrıca bu soruya bakın ).

Bu işe yaramaz:

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

Ürettiği:

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

Dizini bir kısıtlamaya dönüştürün:

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

Ve INSERTifade şimdi işe yarıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.