Mám tabulku jako je tato:
create table my_table (
id int8 not null,
id_A int8 not null,
id_B int8 not null,
id_C int8 null,
constraint pk_my_table primary key (id),
constraint u_constrainte unique (id_A, id_B, id_C)
);
A já chci (id_A, id_B, id_C)
být odlišný v každé situaci. Následující dvě přílohy musí tedy vést k chybě:
INSERT INTO my_table VALUES (1, 1, 2, NULL);
INSERT INTO my_table VALUES (2, 1, 2, NULL);
Ale nechová se tak, jak se očekávalo, protože podle dokumentace nejsou dvě hodnoty NULL
vzájemně porovnávány, takže obě vložky procházejí bez chyby.
Jak mohu zaručit své jedinečné omezení, i když id_C
může být v tomto případě NULL
? Skutečná otázka zní: mohu zaručit tento druh jedinečnosti v „čistém sql“ nebo musím implementovat na vyšší úrovni (v mém případě Java)?
Můžete to udělat v čistém SQL . Vytvořte částečný jedinečný index navíc k tomu, který máte:
CREATE UNIQUE INDEX ab_c_null_idx ON my_table (id_A, id_B) WHERE id_C IS NULL;
Tímto způsobem můžete zadat (a, b, c)
v tabulce:
(1, 2, 1)
(1, 2, 2)
(1, 2, NULL)
Ale nic z toho podruhé.
Nebo použijte dva částečné UNIQUE
indexy a žádný úplný index (nebo omezení). Nejlepší řešení závisí na detailech vašich požadavků. Porovnat:
I když je to elegantní a účinný pro jeden sloupec s nulovatelným nulovým číslem v indexu UNIQUE
, rychle se vymkne z ruky pro další. Diskutujte o tom - a jak používat UPSERT s částečnými indexy:
Žádné použití pro smíšené případy bez dvojitých uvozovek v PostgreSQL.
Můžete považovat serial
sloupec za primární klíč nebo IDENTITY
sloupec in Postgres 10 nebo novější. Příbuzný:
Tak:
CREATE TABLE my_table (
my_table_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY -- for pg 10+
-- my_table_id bigserial PRIMARY KEY -- for pg 9.6 or older
, id_a int8 NOT NULL
, id_b int8 NOT NULL
, id_c int8
, CONSTRAINT u_constraint UNIQUE (id_a, id_b, id_c)
);
Pokud neočekáváte více než 2 miliardy řádků (> 2147483647) po celou dobu životnosti vaší tabulky (včetně řádků s odpadem a odstraněním), zvažte místo integer
(8 bajtů) místo bigint
(8 bajtů).
Měl jsem stejný problém a našel jsem jiný způsob, jak mít jedinečný NULL do stolu.
CREATE UNIQUE INDEX index_name ON table_name( COALESCE( foreign_key_field, -1) )
V mém případě pole foreign_key_field
je kladné celé číslo a nikdy nebude -1.
Takže na odpověď na Manual Leduc by mohlo být jiné řešení
CREATE UNIQUE INDEX u_constrainte (COALESCE(id_a, -1), COALESCE(id_b,-1),COALESCE(id_c, -1) )
Předpokládám, že idy nebudou -1.
Jaká je výhoda při vytváření částečného indexu?
V případě, že nemáte klauzuli NOT NULL, id_a
, id_b
a id_c
může být NULL společně pouze jednou.
S částečným indexem by mohla být 3 pole NULL více než jednou.
Null může znamenat, že hodnota není pro daný řádek v tuto chvíli známa, ale bude přidána, pokud je známa, v budoucnosti (příklad FinishDate
pro běžící Project
) nebo že žádná hodnota nemůže být použito pro tento řádek (příklad EscapeVelocity
pro černou díru Star
).
Podle mého názoru je obvykle lepší normalizovat tabulky odstraněním všech hodnot Null.
Ve vašem případě chcete povolit ve vašem sloupci NULLs
, ale přesto chcete povolit pouze jeden NULL
. Proč? Jaký je to vztah mezi oběma tabulkami?
Možná můžete jednoduše změnit sloupec na NOT NULL
a místo NULL
uložte speciální hodnotu (jako -1
) je známo, že se nikdy neobjeví. Tím se vyřeší problém omezení jedinečnosti (ale může to mít další nežádoucí vedlejší účinky. Například použití -1
znamená, že „není známo/nevztahuje se“, bude ve sloupci zkosena jakákoli suma nebo průměrné výpočty. Nebo všechny takové výpočty budou muset vzít v úvahu speciální hodnotu a ignorovat ji.)