Postgresql: Batasan unik bersyarat

116

Saya ingin menambahkan batasan yang memberlakukan keunikan pada kolom hanya di sebagian tabel.

ALTER TABLE stop ADD CONSTRAINT myc UNIQUE (col_a) WHERE (col_b is null);

Bagian di WHEREatas adalah angan-angan.

Ada cara untuk melakukan ini? Atau haruskah saya kembali ke papan gambar relasional?

EoghanM
sumber
2
Biasa dilakukan. Lihat "indeks unik parsial"
Craig Ringer
11
@yvesonline tidak, itu kendala unik biasa. Poster menginginkan batasan unik parsial .
Craig Ringer

Jawaban:

186

PostgreSQL tidak mendefinisikan batasan parsial (yaitu bersyarat) UNIQUE- namun, Anda dapat membuat indeks unik parsial . PostgreSQL menggunakan indeks unik untuk menerapkan batasan unik, jadi efeknya sama, Anda tidak akan melihat batasan yang tercantum di information_schema.

CREATE UNIQUE INDEX stop_myc ON stop (col_a) WHERE (col_b is NOT null);

Lihat indeks parsial .

Craig Ringer
sumber
24
Super! Tidak disadari bahwa "kendala" tidak muncul sebagai kendala, tetapi tetap memberikan kesalahan yang diinginkanERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM
7
Perlu dicatat bahwa ini tidak akan memungkinkan pembuatan referensi FK yang sebagian unik bidangnya.
ffflabs
11
Perlu juga dicatat bahwa efek indeks ini tidak dapat ditangguhkan. Jika Anda perlu melakukan pembaruan massal, ini mungkin menimbulkan masalah karena keunikan diperiksa setelah setiap baris, bukan setelah pernyataan seperti itu akan menjadi kendala atau setelah transaksi seperti itu akan menjadi untuk kendala yang dapat ditangguhkan.
sage88
37

telah dikatakan bahwa PG tidak mendefinisikan batasan UNIK parsial (yaitu bersyarat). Dokumentasi juga mengatakan bahwa cara yang disukai untuk menambahkan batasan unik ke tabel adalah ADD CONSTRAINT Indeks Unik

Cara yang disukai untuk menambahkan batasan unik ke tabel adalah ALTER TABLE ... ADD CONSTRAINT. Penggunaan indeks untuk memberlakukan batasan unik dapat dianggap sebagai detail implementasi yang tidak boleh diakses secara langsung. Namun, orang harus menyadari bahwa tidak perlu membuat indeks secara manual pada kolom unik; melakukannya hanya akan menduplikasi indeks yang dibuat secara otomatis.

Ada cara untuk mengimplementasikannya menggunakan Exclusion Constraints , (terima kasih @dukelion untuk solusi ini)

Dalam kasus Anda, itu akan terlihat seperti

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);
Peter Yeremenko
sumber
pada pendekatan itu Anda tidak menggunakan "menggunakan" untuk mendefinisikan metode indeks, sehingga bisa sangat lambat atau postgres membuat indeks default untuk itu? Metode itu adalah pilihan kanonik, tetapi bukan pilihan yang lebih baik! Saya pikir Anda akan membutuhkan klausa "menggunakan" dengan indeks untuk membuat pilihan itu menjadi yang lebih baik.
Natan Medeiros
10
Meskipun lebih lambat, keuntungan dari solusi pengecualian adalah dapat ditunda (dan secara default menunda hingga akhir pernyataan). Sebaliknya, solusi indeks unik yang diterima tidak dapat ditangguhkan (dan diperiksa setelah setiap perubahan baris). Jadi pembaruan massal sering kali tidak memungkinkan karena langkah-langkah selama pembaruan akan melanggar batasan unik, bahkan jika itu tidak akan dilanggar di akhir pernyataan pembaruan atom.
sage88
1
Catatan ini telah dihapus dari dokumen pada Agustus 2015
Raniz