Mam kolumnę: standard BOOLEAN NOT NULL
Chciałbym egzekwować jeden wiersz Prawda, a wszystkie inne Fałsz. Nie ma FK ani nic innego w zależności od tego ograniczenia. Wiem, że mogę to zrobić przy pomocy PLpgSQL, ale wydaje się to młotem. Wolałbym coś w rodzaju ograniczenia CHECK
lub UNIQUE
ograniczenia. Im prościej, tym lepiej.
Jeden wiersz musi być prawdziwy, nie wszystkie mogą być fałszywe (więc pierwszy wstawiony wiersz musiałby być prawdziwy).
Wiersz będzie musiał zostać zaktualizowany, co oznacza, że muszę czekać, aby sprawdzić ograniczenia, aż aktualizacje zostaną wykonane, ponieważ wszystkie wiersze mogą być ustawione na False pierwszy, a jeden wiersz True później.
Istnieje FK pomiędzy products.tax_rate_id
i tax_rate.id
, ale nie ma to nic wspólnego z domyślną lub standardową stawką podatkową, którą użytkownik może wybrać, aby ułatwić tworzenie nowych produktów.
PostgreSQL 9.5, jeśli ma to znaczenie.
tło
Tabela to stawka podatku. Jedna ze stawek podatkowych jest domyślna ( standard
ponieważ domyślnie jest to polecenie Postgres). Po dodaniu nowego produktu do produktu stosuje się standardową stawkę podatku. Jeśli nie standard
, baza danych musi zgadnąć lub wykonać wszelkiego rodzaju niepotrzebne kontrole. Pomyślałem, że najprostszym rozwiązaniem jest upewnienie się, że istnieje standard
.
Przez „domyślny” powyżej rozumiem warstwę prezentacji (UI). Istnieje możliwość zmiany domyślnej stawki podatkowej przez użytkownika. Muszę albo dodać dodatkowe kontrole, aby upewnić się, że GUI / użytkownik nie próbuje ustawić tax_rate_id na NULL, lub po prostu ustawić domyślną stawkę podatku.
Odpowiedzi:
Wariant 1
Ponieważ wszystko, czego potrzebujesz, to jedna kolumna z
standard = true
, ustaw standard na NULL we wszystkich innych wierszach. Następnie działa prosteUNIQUE
ograniczenie, ponieważ wartości NULL nie naruszają go:DEFAULT
jest opcjonalnym przypomnieniem, że pierwszy wprowadzony wiersz powinien stać się domyślny. Niczego to nie wymusza . Chociaż nie możesz ustawić więcej niż jednego wierszastandard = true
, nadal możesz ustawić wszystkie wiersze na NULL. Nie ma czystego sposobu, aby temu zapobiec, stosując jedynie ograniczenia w jednej tabeli.CHECK
ograniczenia nie uwzględniają innych wierszy (bez brudnych lew).Związane z:
Ogranicz dwie określone wartości kolumn z istniejącej w tym samym czasie
Dodaj ograniczenie, aby kolumna była unikalna dla grupy wierszy
Aktualizować:
Aby zezwolić na polecenie takie jak (gdzie ograniczenie jest spełnione tylko na końcu instrukcji):
..
UNIQUE
ograniczenie musiałoby byćDEFERRABLE
. Widzieć:dbfiddle tutaj
Wariant 2
Miej drugi stolik z jednym rzędem, taki jak:
Utwórz to jako superużytkownik:
Teraz zawsze jest jeden wiersz wskazujący na standard (w tym prostym przypadku również bezpośrednio reprezentujący stawkę standardową). Tylko superużytkownik może to złamać. Możesz także tego zabronić za pomocą spustu
BEFORE DELETE
.dbfiddle tutaj
Związane z:
Możesz dodać a,
VIEW
aby zobaczyć to samo, co w wariancie 1 :W zapytaniach, w których wszystko czego potrzebujesz to standardowa stawka, użyj (tylko)
taxrate_standard.taxrate
bezpośrednio.Później dodałeś:
A realizacja biedaka wariantu 2 byłby po prostu dodać wiersz do
products
(lub jakiegokolwiek podobnego tabela) skierowaną do standardowej stawki podatkowej; fikcyjny produkt, który możesz nazwać „standardową stawką podatkową” - jeśli konfiguracja na to pozwala.Ograniczenia FK wymuszają integralność referencyjną. Aby go ukończyć, wyegzekwuj
tax_rate_id IS NOT NULL
dla wiersza (jeśli nie jest tak w przypadku całej kolumny). I nie zezwalaj na jego usunięcie. Oba można włączyć w wyzwalacze. Bez dodatkowego stołu, ale mniej elegancki i nie tak niezawodny.źródło
CROSS JOIN
przeciw standardowi,LEFT JOIN
do konkretnego, a następnieCOALESCE
między nimi.CONSTRAINT standard_only_1_true UNIQUE (standard)
: Przypuszczam, że tabela nie będzie duża, więc nie ma to większego znaczenia, ale ponieważ ograniczenie definiuje indeks dla całej tabeli, czy częściowy unikatowy indeksWHERE (standard)
zużywałby mniej miejsca?Możesz użyć filtrowanego indeksu
dbfiddle tutaj
Ale jak powiedziałeś, pierwszy wiersz musi być prawdziwy, wtedy możesz użyć ograniczenia CHECK, ale nawet używając funkcji możesz usunąć pierwszy wiersz później.
dbfiddle tutaj
Możesz to rozwiązać, dodając wyzwalacz PRZED USUNIĘCIEM, aby upewnić się, że pierwszy wiersz (foo to prawda) nigdy nie zostanie usunięty.
dbfiddle tutaj
źródło