Ograniczenie unikalności z zakresem dat

15

Rozważ pricestabelę z tymi kolumnami:

id         integer primary key
product_id integer -- foreign key
start_date date not null
end_date   date not null
quantity   integer
price      numeric

Chciałbym, aby baza danych egzekwowała zasadę, że produkt może mieć tylko jedną cenę w określonej ilości w zakresie dat (via where <date> BETWEEN start_date AND end_date).

Czy tego rodzaju ograniczenie oparte na zakresie jest wykonalne?

kolec
źródło

Odpowiedzi:

23

Tak, możesz użyć EXCLUDEograniczenia, które jest uogólnieniem UNIQUEograniczeń:

ALTER TABLE prices 
  ADD CONSTRAINT unique_price_per_product_quantity_daterange
    EXCLUDE  USING gist
    ( product_id WITH =, 
      quantity WITH =, 
      daterange(start_date, end_date, '[]') WITH &&   -- this is the crucial
    );

Ograniczenie można interpretować jako powiedzenie:

Nie zezwalaj na dwa wiersze, które mają takie same product_id, takie same quantityi pokrywające się ( &&) zakresy dat.

Jest '[]'to dla poszukiwanego zakresu dat obejmującego wszystkie (domyślnie [)dla typów zakresów).

Zobacz dokumentację ograniczeń typów typów . Prawdopodobnie musisz także dodać rozszerzenie, uruchamiając (raz, dla każdej bazy danych, w której chcesz to zainstalować):

CREATE EXTENSION btree_gist;
ypercubeᵀᴹ
źródło
To jest niesamowite. Nie sądzę, że daterangejest dokładnie tak samo, ponieważ ma wyłączną dolną granicę, ale łatwo to naprawić. Czy naprawdę powinienem migrować moje dane, aby użyć daterangetypu kolumny (może to stanowić osobne pytanie, jeśli jest to lepsze), czy też jest to rozsądne?
skok
Domyślnie w dolnych granicach i górnych granicach, jeśli dobrze pamiętam. Przeredaguję dla all-inclusive. Zwykle wolę ustawienie domyślne, ponieważ jest powszechne w aplikacjach podobnych do hoteli. (Wchodzę do hotelu na 2, wysiadam na 8,
zostaję
Mogę być przerzucony na to, co ... właśnie dowiedziałem się dzisiaj o typach zakresów i czytam dokumenty!
skok
Nie jestem pewien, co jest lepsze, 2 kolumny lub jedna z datownikiem. Możesz zadać osobne pytanie. Prawdopodobnie będzie to zależeć od pożądanego użycia, zapytań, łatwości użycia (i potrzeb indeksu). Jeśli istnieją osobne kolumny, łatwiej byłoby na przykład mieć indeks (product_id, start_date). Z datownikiem musiałby to być indeks on(product_id, lower(range_column))
ypercubeᵀᴹ