See pole soovitav pärimise kaudu tabelite loomise tehniliste piirangute tõttu. Vaatame näidet. Iga isik on mingis täpselt ühes seisundis. Isik võib olla klient või töötaja. Võib olla isikuid, kes on nii klient kui töötaja. Võib olla isikuid, kes pole ei klient ega töötaja. Igal isikul on null või üks kasutajakonto.
Realiseerin selle PostgreSQLis kasutades pärimise kaudu tabelite loomist ja toon välja tekkivad probleemid.
CREATE TABLE Isiku_seisundi_liik (isiku_seisundi_liik_kood SMALLINT,CONSTRAINT pk_isiku_seisundi_liik PRIMARY KEY (isiku_seisundi_liik_kood));CREATE TABLE Isik (isik_id SERIAL,e_meil VARCHAR(254) NOT NULL,eesnimi VARCHAR(50) NOT NULL,isiku_seisundi_liik_kood SMALLINT NOT NULL DEFAULT 1,CONSTRAINT pk_isik PRIMARY KEY (isik_id),CONSTRAINT ak_isik UNIQUE (e_meil),CONSTRAINT chk_isik_e_meil CHECK (e_meil LIKE '%@%'),CONSTRAINT fk_isik_isiku_seisundi_liik FOREIGN KEY (isiku_seisundi_liik_kood) REFERENCES Isiku_seisundi_liik (isiku_seisundi_liik_kood) ON UPDATE CASCADE);CREATE TABLE Klient (on_nous_tylitamisega BOOLEAN NOT NULL DEFAULT FALSE,CONSTRAINT pk_klient PRIMARY KEY (isik_id),CONSTRAINT ak_klient UNIQUE (e_meil),CONSTRAINT fk_klient_isiku_seisundi_liik FOREIGN KEY (isiku_seisundi_liik_kood) REFERENCES Isiku_seisundi_liik (isiku_seisundi_liik_kood) ON UPDATE CASCADE)INHERITS (Isik);CREATE TABLE Tootaja (palk DECIMAL(10,2) NOT NULL,CONSTRAINT pk_tootaja PRIMARY KEY (isik_id),CONSTRAINT ak_tootaja UNIQUE (e_meil),CONSTRAINT fk_tootaja_isiku_seisundi_liik FOREIGN KEY (isiku_seisundi_liik_kood) REFERENCES Isiku_seisundi_liik (isiku_seisundi_liik_kood) ON UPDATE CASCADE)INHERITS (Isik);CREATE TABLE Kasutajakonto (isik_id INTEGER,on_aktiivne BOOLEAN NOT NULL DEFAULT TRUE,CONSTRAINT pk_kasutajakonto PRIMARY KEY (isik_id),CONSTRAINT fk_kasutajakonto_isik FOREIGN KEY (isik_id) REFERENCES Isik (isik_id) ON DELETE CASCADE);INSERT INTO Isiku_seisundi_liik (isiku_seisundi_liik_kood) VALUES (1),(2);Analüüsin probleeme.
Korduv kood. Pärimise kaudu loodud tabelites tuleb PRIMARY KEY, UNIQUE, FOREIGN KEY ja EXCLUDE uue nimega uuesti defineerida, sest neid ei pärita. See tähendab kitsenduste koodi mitmekordset sisestamist. CHECK ja NOT NULL kitsendusi ei tule uuesti defineerida, sest neid päritakse.
Andmete mitmekordne registreerimine.
INSERT INTO Klient (e_meil, eesnimi, isiku_seisundi_liik_kood) VALUES ('mati@mail.ee','Mati',1);INSERT INTO Tootaja (e_meil, eesnimi, palk, isiku_seisundi_liik_kood) VALUES ('mati@mail.ee','Matti',2000,2);INSERT INTO Isik (e_meil, eesnimi) VALUES ('mati@mail.ee','Mats');SELECT * FROM Isik;Kõik need kolm INSERT lauset õnnestusid. Selle tulemusena on sama isiku andmed tabelis Isik kolmekordselt. Kõigis ridades on sama meiliaadress, kuid erinev eesnimi või seisund. Seega on andmetes vastuolud.
Kui lisan kõigepealt rea tabelisse Isik, siis ma ei saa teha nii, et neid andmeid ei tuleks uuesti sisestada kui soovin registreerida, et isiku näol on tegemist kliendi või töötajaga.
Välisvõtme piirangud.
Välisvõtme piirangud.
INSERT INTO Kasutajakonto (isik_id, on_aktiivne) VALUES (1, FALSE);Lisamine ebaõnnestus, sest tabelile Isik viitav välisvõtme kitsendus "ei näe" alamtabelitesse (Klient, Tootaja) lisatud isik_id väärtuseid.
Kuidas siis oleks parem?
CREATE TABLE Isiku_seisundi_liik (isiku_seisundi_liik_kood SMALLINT,CONSTRAINT pk_isiku_seisundi_liik PRIMARY KEY (isiku_seisundi_liik_kood));CREATE TABLE Isik (isik_id SERIAL,e_meil VARCHAR(254) NOT NULL,eesnimi VARCHAR(50) NOT NULL,isiku_seisundi_liik_kood SMALLINT NOT NULL DEFAULT 1,CONSTRAINT pk_isik PRIMARY KEY (isik_id),CONSTRAINT ak_isik UNIQUE (e_meil),CONSTRAINT chk_isik_e_meil CHECK (e_meil LIKE '%@%'),CONSTRAINT fk_isik_isiku_seisundi_liik FOREIGN KEY (isiku_seisundi_liik_kood) REFERENCES Isiku_seisundi_liik (isiku_seisundi_liik_kood) ON UPDATE CASCADE);CREATE TABLE Klient (isik_id INTEGER,on_nous_tylitamisega BOOLEAN NOT NULL DEFAULT FALSE,CONSTRAINT pk_klient PRIMARY KEY (isik_id),CONSTRAINT fk_klient_isik FOREIGN KEY (isik_id) REFERENCES Isik (isik_id) ON DELETE CASCADE);CREATE TABLE Tootaja (isik_id INTEGER,palk DECIMAL(10,2) NOT NULL,CONSTRAINT pk_tootaja PRIMARY KEY (isik_id),CONSTRAINT fk_tootaja_isik FOREIGN KEY (isik_id) REFERENCES Isik (isik_id) ON DELETE CASCADE);CREATE TABLE Kasutajakonto (isik_id INTEGER,on_aktiivne BOOLEAN NOT NULL DEFAULT TRUE,CONSTRAINT pk_kasutajakonto PRIMARY KEY (isik_id),CONSTRAINT fk_kasutajakonto_isik FOREIGN KEY (isik_id) REFERENCES Isik (isik_id) ON DELETE CASCADE);INSERT INTO Isiku_seisundi_liik (isiku_seisundi_liik_kood) VALUES (1),(2);Kui tahaksin näiteks vaadata kõiki kliente ja nende andmeid, siis saan luua vaate.
CREATE OR REPLACE VIEW Kliendid WITH (security_barrier) ASSELECT isik_id, e_meil, eesnimi, isiku_seisundi_liik_kood, on_nous_tylitamisegaFROM Isik INNER JOIN Klient USING (isik_id);Kui tahan registreerida kliendi, siis lisan kõigepealt rea tabelisse Isik ning siis selle tabeli jaoks genereeritud isik_id väärtusega rea tabelisse Klient.
WITH lisa_isik AS (INSERT INTO Isik (e_meil, eesnimi, isiku_seisundi_liik_kood) VALUES ('mati@mail.ee','Mati',1)RETURNING isik_id)INSERT INTO Klient (isik_id, on_nous_tylitamisega)SELECT isik_id, FALSE AS on_nous_tylitamisegaFROM lisa_isik;SELECT * FROM Klient;Tabelis on üks rida.