Postgres'de ertelenebilir benzersiz dizin


14

Değişiklik tablosu için postgres belgelerine bakıldığında , düzenli kısıtlamalar DEFERRABLE(daha somut olarak, INITIALLY DEFERREDilgilendiğim şey) olarak işaretlenebilir.

Dizinler, şu sürece bir kısıtlamayla da ilişkilendirilebilir:

Dizinde ifade sütunları veya kısmi dizin olamaz

Bu da şu anda aşağıdaki gibi koşullara sahip benzersiz bir dizine sahip olmanın bir yolu olmadığına inanmamı sağlıyor:

CREATE UNIQUE INDEX unique_booking
  ON public.booking
  USING btree
  (check_in, check_out)
  WHERE booking_status = 1;

Diğer bir INITIALLY DEFERREDdeyişle, 'kısıtlama' tekliğinin yalnızca işlemin sonunda ( SET CONSTRAINTS ALL DEFERRED;kullanılıyorsa) doğrulanacağı anlamına gelir .

Varsayım doğru mu ve eğer öyleyse, amaçlanan davranışa ulaşmanın bir yolu var mı?

Teşekkürler

Yanıtlar:


15

Bir endeks ertelenemez - UNIQUEsadece bir UNIQUEkısıtlama olsun ya da olmasın önemli değil . Kısıtlamaları Diğer tür ( FOREIGN KEY, PRIMARY KEY, EXCLUDEama -) da ertelenebilir olan CHECKkısıtlamalar.

Bu nedenle, benzersiz kısmi dizin (ve uyguladığı örtülü kısıtlama) işlemin sonunda değil, her ifadede (ve aslında geçerli uygulamada her satır ekleme / güncellemeden sonra) kontrol edilecektir.


Bu kısıtlamayı ertelenebilir olarak uygulamak istiyorsanız, tasarımda bir tablo daha eklemektir. Bunun gibi bir şey:

CREATE TABLE public.booking_status
  ( booking_id int NOT NULL,               -- same types
    check_in timestamp NOT NULL,           -- as in  
    check_out timestamp NOT NULL,          -- booking
    CONSTRAINT unique_booking
        UNIQUE (check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED,
    CONSTRAINT unique_booking_fk
        FOREIGN KEY (booking_id, check_in, check_out)
        REFERENCES public.booking (booking_id, check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED
  ) ;

Bu tasarım ve booking_statussadece 2 olası seçeneği (0 ve 1) olduğu varsayılarak, onu tamamen kaldırabilirsiniz booking(bir satır booking_statusvarsa, 0 değilse 1'dir).


Başka bir yol da (ab) bir EXCLUDEkısıtlama kullanmak olacaktır :

ALTER TABLE booking
    ADD CONSTRAINT unique_booking
        EXCLUDE 
          ( check_in  WITH =, 
            check_out WITH =, 
            (CASE WHEN booking_status = 1 THEN TRUE END) WITH =
          ) 
        DEFERRABLE INITIALLY DEFERRED ;

Dbfiddle'da test edildi .

Yukarıdakiler ne yapar:

  • CASEİfadesi olur NULLzaman booking_statusbiz yazabilirsiniz null veya 1'den farklı (CASE WHEN booking_status = 1 THEN TRUE END)olarak (booking_status = 1 OR NULL)bu artık açıkça bunu yaparsa.

  • Benzersiz ve hariç tutma kısıtlamaları, ifadelerin bir veya daha fazlasının NULL olduğu satırları kabul eder. Yani ile filtrelenmiş bir dizin gibi davranır WHERE booking_status = 1.

  • Tüm WITHoperatörler =bir UNIQUEkısıtlama görevi görürler .

  • Bu iki birleşik, kısıtlamanın filtrelenmiş benzersiz bir dizin gibi davranmasını sağlar.

  • Ancak bu bir kısıtlamadır ve EXCLUDEkısıtlamalar ertelenebilir.


2
EXCLUDE sürümü için +1, ihtiyacım olan buydu. İşte EXCLUDE'in
Benjamin Peter

(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)) WHERE (booking_status = 1)"Dışlama kısıtlamaları bir dizin kullanılarak uygulandığı için" ile değiştirilmelidir ve bu kısmi dizin WHEREdaha küçük ve daha hızlı olacaktır - postgresql.org/docs/current/sql-createtable.html ve postgresql.org/docs/current/sql- createindex.html
Denis Ryzhkov

1

Bu sorunun yılları geçmesine rağmen, İspanyolca konuşanlar için açıklığa kavuşturmak istiyorum, testler Postgres'de yapıldı:

Aşağıdaki kısıtlama, kitin birincil anahtar olduğu 1337 kayıtlık bir tabloya eklendi:

**Bloque 1**
ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit) 

Bu tablo için varsayılan bir birincil anahtar OLUŞTURMAZ, bu nedenle bir sonraki GÜNCELLEME çalışırken hata alırız:

update ele_kitscompletos
set div_nkit = div_nkit + 1; 

HATA: yinelenen anahtar benzersizlik kısıtlamasını ihlal ediyor «unique_div_nkit»

Postgres'te, her SATIR için bir GÜNCELLEME yürütülmesi, KISITLAMA veya KISITLAMANIN karşılandığını doğrular.


CONSTRAINT IMMEDIATE artık yaratılmıştır ve her ifade ayrı ayrı yürütülür:

ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY IMMEDIATE

**Bloque 2**
BEGIN;   
UPDATE ele_kitscompletos set div_nkit = div_nkit + 1;
INSERT INTO public.ele_kitscompletos(div_nkit, otro_campo)
VALUES 
  (1338, '888150502');
COMMIT;

Sorgu TAMAM, 0 satır etkilenir (yürütme süresi: 0 ms; toplam süre: 0 ms) Sorgu Tamam, 1328 satır etkilenir (yürütme süresi: 858 ms; toplam süre: 858 ms) HATA: llave duplicada viola restción de unicidad «unique_div_nkit» DETAIL : Ya existe la llave (div_nkit) = (1338).

Burada SI, ilk tam cümleyi (1328 satır) yürüttüğü için birincil anahtarın değiştirilmesine izin verir; ancak işlemde olmasına rağmen (BEGIN), CONSTRAINT her cümleyi COMMIT yapmadan bitirdikten hemen sonra doğrulanır, bu nedenle INSERT yürütülürken hata üretir. Son olarak CONSTRAINT DEFERRED'i aşağıdakileri yaptık:

**Bloque 3**
ALTER TABLE public.ele_edivipol
DROP CONSTRAINT unique_div_nkit RESTRICT;   

ALTER TABLE ele_edivipol
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY DEFERRED

** Cümle 2 ** ifadelerinin her bir cümlesini, her cümleyi ayrı ayrı yürütürsek, doğrulamadığı için INSERT'de herhangi bir hata üretilmez, ancak son COMMIT tutarsızlık bulunduğu yerde yürütülür.


İngilizce tam bilgi için bağlantıları kontrol etmenizi öneririz:

Derinlikte Ertelenebilir SQL Kısıtlamaları

ERTELENMEYEN BAŞLANGIÇLA İLGİLİ DEFERRABLE DEĞİL

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.