In relationele databases ligt de verantwoordelijkheid voor gegevensuniekheid vaak bij UNIQUE-beperkingen. Maar in de zakelijke praktijk zijn er situaties waarin uniekheid vereist is op basis van een combinatie van velden, waarvan sommige NULL kunnen zijn (bijvoorbeeld een unieke combinatie van e-mail + telefoon, maar telefoon kan onbekend zijn).
Geschiedenis van de vraag: In SQL garanderen standaard UNIQUE CONSTRAINTS geen uniekheid wanneer een van de kolommen NULL is — de specificatie beschouwt dergelijke waarden als wederzijds exclusief.
Probleem: Voor complexe sleutels met NULL leidt de standaardbeperkingen tot duplicaten. Dit is vooral kritiek wanneer gegevensintegriteit noodzakelijk is: bij invoer, migratie, massale updates.
Oplossing: Gebruik een berekende kolom die alle waarden (inclusief NULL) in overweging neemt, en leg uniekheid op die kolom op of gebruik triggerlogica.
Voorbeeld van code:
ALTER TABLE my_table ADD computed_uniqueness AS ( ISNULL(email, '') + '#' + ISNULL(phone, '') ); CREATE UNIQUE INDEX idx_my_table_computed_uniqueness ON my_table(computed_uniqueness);
Of (voor PostgreSQL, waar uitdrukkingen in indexen en NULL als verschillend worden beschouwd):
CREATE UNIQUE INDEX idx_unique_email_phone ON my_table ((COALESCE(email, '##')),(COALESCE(phone, '##')));
Belangrijke kenmerken:
Kan ik een normale UNIQUE index gebruiken om uniekheid te waarborgen, als een deel van de kolommen NULL toestaat?
Nee. Volgens ANSI SQL staat een UNIQUE index meerdere rijen toe waarbij ten minste een van de kolommen in de combinatie NULL is, omdat NULL niet als gelijk aan een andere waarde wordt beschouwd, inclusief NULL.
Wat is het verschil tussen het gebruik van een unieke index op een uitdrukking en het gebruik van een BEFORE INSERT-trigger voor het controleren van uniekheid?
Een unieke index is eenvoudiger te onderhouden en sneller in gebruik, maar kan niet altijd complexe zakelijke regels implementeren (bijvoorbeeld uitzonderingen of aangepaste combinaties). Een trigger is flexibeler, maar langzamer en moeilijker te onderhouden.
Voorbeeld:
CREATE OR REPLACE FUNCTION check_custom_unique() RETURNS TRIGGER AS $$ BEGIN IF EXISTS ( SELECT 1 FROM my_table WHERE COALESCE(NEW.email, '##') = COALESCE(email, '##') AND COALESCE(NEW.phone, '##') = COALESCE(phone, '##') ) THEN RAISE EXCEPTION 'Duplicate found'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_custom_unique BEFORE INSERT OR UPDATE ON my_table FOR EACH ROW EXECUTE FUNCTION check_custom_unique();
Kan ik DISTINCT in SELECT gebruiken om op applicatieniveau de unieke probleem op te lossen?
Ja, maar alleen voor selectie — dit voorkomt niet dat duplicaten bij datamodificaties worden ingevoerd en is geen vervanging voor beperkingen op tabelniveau.
Een bedrijf implementeert uniekheid e-mail+telefoon via UNIQUE(email, phone). In de database verschijnen duplicaten op e-mail als phone=NULL.
Voordelen:
Nadelen:
Een berekende kolom (COALESCE(email, '##') + '#' + COALESCE(phone, '')), waarop een unieke index wordt geplaatst.
Voordelen:
Nadelen: