Tabellen,
Constraints und relationaler Entwurf
Grundlagen
zur Definition von Tabellen
Erzeugung von Tabellen
CREATE TABLE products (
product_no integer,
name text,
price numeric
);
CREATE TABLE
orders (
order_id integer,
product_no integer,
quantity integer
);
Löschen
von Tabellen
DROP TABLE products;
DROP TABLE orders;
Default-Werte
CREATE TABLE products (
product_no integer,
name
text DEFAULT '',
price numeric DEFAULT
9.99
);
Arten von Constraints
- Check
Constraint
- NOT NULL Constraint
- Unique Constraint
- Primary Key (Primärschlüssel)
- Foreign Key
(Fremdschlüssel)
Check
Constraint
Check-Klause,
Column-Constraint, Constraint-Name
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK
(price > 0)
);
CREATE TABLE products (
product_no integer,
name text,
price numeric
CONSTRAINT positive_price CHECK (price
> 0)
);
Constraint-Name wird in Fehlermeldungen bei Constraint-Verletzungen
angegeben
Table
Constraint
CREATE TABLE
products (
product_no
integer,
name
text,
price
numeric CHECK (price > 0),
discounted_price numeric CHECK (discounted_price
> 0),
CHECK (price > discounted_price)
);
CREATE
TABLE products (
product_no
integer,
name
text,
price
numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CHECK (price > discounted_price)
);
CREATE TABLE products (
product_no
integer,
name
text,
price numeric CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0 AND price >
discounted_price)
);
CREATE TABLE products (
product_no
integer,
name
text,
price
numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price >
discounted_price)
);
Not-Null
Constraint
CREATE TABLE products (
product_no integer NOT NULL,
name
text NOT NULL,
price numeric
);
CREATE TABLE products (
product_no integer NOT NULL,
name
text NOT NULL,
price numeric NOT NULL
CHECK (price > 0)
);
CREATE TABLE products (
product_no integer NULL,
name
text NULL,
price numeric NULL
);
NULL Constraint *nicht* im SQL-Standard
Unique
Constraints
Die Angabe UNIQUE
bedeutet, dass die Spalte oder die
Gruppe von Spalten in der Tabelle für jedes Tupel eindeutig
CREATE
TABLE products (
product_no integer UNIQUE,
name text,
price numeric
);
CREATE TABLE products (
product_no integer,
name text,
price numeric,
UNIQUE (product_no)
);
CREATE TABLE person (
fname text,
lname text,
address text,
UNIQUE (fname, lname)
);
CREATE TABLE products (
product_no integer CONSTRAINT must_be_unique
UNIQUE,
name text,
price numeric
);
Primary
Keys
Primary Key Constraint: Kombination von Unique Constraint und Not-NULL
Constraint
CREATE TABLE products (
product_no integer UNIQUE NOT NULL,
name text,
price numeric
);
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE
person (
fname text,
lname text,
address text,
PRIMARY KEY (fname, lname)
);
Die Definition eines Primary Key bedeutet, dass die Spalte oder die
Gruppe von Spalten in der Tabelle für jedes Tupel eindeutig und
nicht NULL sind
Foreign
Keys
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no),
quantity integer
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products,
quantity integer
);
CREATE TABLE company (
cname text
PRIMARY KEY,
manager_fname text,
manager_lname text,
FOREIGN KEY (manager_fname, manager_lname)
REFERENCES person (fname, lname)
);
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer
PRIMARY KEY,
shipping_address text,
shipping_date text
);
CREATE TABLE order_items (
product_no integer REFERENCES products,
order_id integer REFERENCES orders,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
Ein Fremdschlüssel muss Spalten referenzieren die (in der
referenzierten Tabelle) entweder als
Primärschlüssel oder mit einem UNIQUE Constraint
gekennzeichnet sind.
Optionen für den Fall, dass versucht wird ein referenziertes Tupel
zu löschen
- Verbot des Löschens des referenzierten Tupels
- Löschen des referenzierten Tupels und Löschen der
referenzierenden Tupels
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer
PRIMARY KEY,
shipping_address text,
shipping_date
text
);
CREATE TABLE order_items (
product_no integer REFERENCES products ON DELETE
RESTRICT,
order_id integer REFERENCES orders ON
DELETE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
RESTRICT verhindert das Löschen des referenzierten Tupels
CASCADE zeigt an, dass das referenzierende Tupel auch gelöscht
werden soll
Weitere Optionen: SET NULL und SET DEFAULT; das referenzierende Tupel
erhält als Referenzwert NULL bzw. den Default-Wert
Analog zu ON DELETE ist auch die Angabe ON UPDATE erlaubt

Beispiel
CREATE
TABLE author ( fname VARCHAR,
lname VARCHAR,
title VARCHAR,
yearp INTEGER ); -- publication
INSERT INTO author VALUES ('Ada','Black','UML',2002);
INSERT INTO author VALUES ('Bob','Green','UML',2002);
INSERT INTO author VALUES ('Cyd','White','DBS',1990);
CREATE TABLE person ( fname VARCHAR,
lname VARCHAR,
yearb INTEGER ); -- birth
INSERT INTO person VALUES ('Ada','Black',1965);
INSERT INTO person VALUES ('Ada','White',1975);
INSERT INTO person VALUES ('Bob','Green',1970);
INSERT INTO person VALUES ('Dan','Green',1992);
Ohne Contraints ist eine Tabelle
*keine* Relation
INSERT INTO person VALUES
('Flo','Brown',1992);
INSERT INTO person VALUES ('Flo','Brown',1992);
SELECT * FROM person ORDER BY 1, 2;
fname | lname | yearb
-------+-------+-------
Ada | Black | 1965
Ada | White | 1975
Bob | Green | 1970
Dan | Green | 1992
Flo | Brown | 1992
Flo | Brown | 1992
(6 rows)
-- TABLE person war *ohne* Key und *ohne* Constraints definiert worden
DELETE FROM person WHERE fname='Flo';
>DELETE
2
SELECT * FROM person;
fname | lname | yearb
-------+-------+-------
Ada | Black | 1965
Ada | White | 1975
Bob | Green | 1970
Dan | Green | 1992
(4 rows)
ALTER TABLE person ADD CONSTRAINT personIsRelation
UNIQUE (fname,lname,yearb);
> NOTICE: ALTER TABLE / ADD UNIQUE will create implicit
> index "personisrelation" for table "person"
> ALTER TABLE
INSERT INTO person VALUES ('Flo','Brown',1992);
> INSERT 0 1
SELECT * FROM person;
fname | lname | yearb
-------+-------+-------
Ada | Black | 1965
Ada | White | 1975
Bob | Green | 1970
Dan | Green | 1992
Flo | Brown | 1992
(5 rows)
INSERT INTO person VALUES ('Flo','Brown',1992);
> ERROR: duplicate key violates unique constraint "personisrelation"
DELETE FROM person WHERE fname='Flo';
>DELETE
1
Beispiel für Zyklische
Fremdschlüssel
ALTER TABLE author ADD PRIMARY KEY (fname,lname);
> NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit
> index "author_pkey" for table "author"
> ALTER TABLE
ALTER TABLE person ADD PRIMARY KEY (fname,lname);
> NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit
> index "person_pkey" for table "person"
> ALTER TABLE
ALTER TABLE author ADD FOREIGN KEY (fname,lname)
REFERENCES person(fname,lname);
> ERROR: insert or update on table "author" violates foreign
key
> constraint "author_fname_fkey"
> DETAIL: Key (fname,lname)=(Cyd,White) not present in "person".
DELETE FROM author WHERE fname='Cyd' AND lname='White';
> DELETE 1
ALTER TABLE author ADD FOREIGN KEY (fname,lname)
REFERENCES person(fname,lname);
> ALTER TABLE
ALTER TABLE person ADD FOREIGN KEY (fname,lname)
REFERENCES author(fname,lname);
> ERROR: insert or update on table "person" violates foreign
key
> constraint "person_fname_fkey"
> DETAIL: Key (fname,lname)=(Ada,White) not present in "author".
DELETE FROM person WHERE (fname='Ada' AND lname='White') OR
(fname='Dan' AND lname='Green');
> DELETE 2
ALTER TABLE person ADD FOREIGN KEY (fname,lname)
REFERENCES author(fname,lname);
> ALTER TABLE
SELECT * FROM author;
fname | lname | title | yearp
-------+-------+-------+-------
Ada | Black | UML | 2002
Bob | Green | UML | 2002
SELECT * FROM person;
fname | lname | yearb
-------+-------+-------
Ada | Black | 1965
Bob | Green | 1970
PRIMARY KEY (fname,lname) in author eigentlich unglücklich,
da
z.B. ('Ada','Black','DBS',2004) nicht eingefügt werden kann
Syntax
für CREATE TABLE
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ]
TABLE table_name ( [
{ column_name data_type [ DEFAULT default_expr ]
[ column_constraint [ ... ] ]
| table_constraint
| LIKE parent_table [ { INCLUDING | EXCLUDING }
{ DEFAULTS | CONSTRAINTS } ] ... }
[, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) |
WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace ]
column_constraint:
[ CONSTRAINT constraint_name ]
{ NOT NULL |
NULL |
UNIQUE index_parameters |
PRIMARY KEY index_parameters |
CHECK ( expression ) |
REFERENCES reftable [ ( refcolumn ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ]
[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
table_constraint:
[ CONSTRAINT constraint_name ]
{ UNIQUE ( column_name [, ... ] ) index_parameters |
PRIMARY KEY ( column_name [, ... ] ) index_parameters |
CHECK ( expression ) |
FOREIGN KEY ( column_name [, ... ] )
REFERENCES reftable [ ( refcolumn [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ]
[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
index_parameters in UNIQUE und PRIMARY KEY constraints:
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ USING INDEX TABLESPACE tablespace ]
Zusammenfassung
Tabellen
- Erzeugung, Löschen, Ändern von Tabellen
CREATE TABLE, DROP TABLE, ALTER TABLE
- Spalten (Attribute) mit Datentypen und möglicherweise
mit Default-Werten
- Constraints:
Check, Not Null, Unique, Primary key, Foreign Key
- CREATE FUNCTION zur Kapselung von komplexen SQL-Ausdrücken
Conceptual
UML and OCL Example Model
Classes
and Associations
model
MotherFatherWorld
enum Gender {female, male}
class Person
attributes
fName:String
lName:String
gender:Gender
operations
offspringsAux(children:Set(Person)):Set(Person)=
let oneStep:Set(Person)=
children.motherChild->union(children.fatherChild)->asSet in
if
oneStep->exists(p|not(children->includes(p)))
then
offspringsAux(children->union(oneStep))
else children endif
offsprings():Set(Person)=
offspringsAux(self.motherChild->union(self.fatherChild))
end
association Motherhood between
Person[0..1] role mother
Person[0..*] role motherChild
end
association Fatherhood between
Person[0..1] role father
Person[0..*] role fatherChild
end
Constraints
constraints
context
self:Person inv fNameLNameIsKey:
Person.allInstances->forAll(self2| self<>self2 implies
(self.fName<>self2.fName or
self.lName<>self2.lName))
context self:Person inv onlyFemaleAsMother:
self.motherChild->notEmpty implies
(self.gender=#female and self.fatherChild->isEmpty)
context self:Person inv onlyMaleAsFather:
self.fatherChild->notEmpty implies
(self.gender=#male and self.motherChild->isEmpty)
context self:Person inv offspringsAcyclic:
not(self.offsprings()->includes(self))
Class
Diagram

Object
Diagram Violating Constraints

Object
Diagram Satisfying Constraints

Transformation
zwischen UML und relationalen DB-Schemata
Übersetzung
von Klassen

CREATE TABLE C (
a1 d1,
a2 d2,
PRIMARY KEY ( a1 )
)
Übersetzung
von funktionalen Assoziationen (Multiplizität 1 oder 0..1)

CREATE TABLE C (
a1 d1,
a2 d2,
role d3 NOT NULL / NULL,
PRIMARY KEY ( a1 ),
FOREIGN KEY ( role ) REFERENCES D
)
Liegt eine (1/0..1,1/0..1) vor, kann diese Assoziation (in C) oder (in
D) oder (in C und D mit zusätzlichem Constraint) realisiert werden
Übersetzung
von allgemeinen Assoziationen

CREATE TABLE C ( ... )
CREATE TABLE D ( ... )
CREATE TABLE Assoc (
roleC d1,
roleD d3,
PRIMARY KEY ( roleC, roleD ),
FOREIGN KEY ( roleC ) REFERENCES C,
FOREIGN KEY ( roleD ) REFERENCES D
)
Übersetzung
von Generalisierungen (3 Varianten, 2 Varianten nur bedingt einsetzbar)
Schlüssel werden in der Generalisierungsklasse, nicht in den
Spezialisierungsklassen angegeben

Erinnerung: In UML gibt es als Constraints für Generalisierungen
{incomplete, complete} und {overlapping, disjoint}. Default:
incomplete, overlapping.
(1)
Generalisierungsklasse und Spezialisierungsklassen in getrennten
Tabellen mit Fremdschlüsseln
CREATE TABLE C (
a1 d1,
a2 d2,
PRIMARY KEY ( a1 )
)
CREATE TABLE E (
a1 d1,
e d3,
PRIMARY KEY ( a1 ),
FOREIGN KEY ( a1 ) REFERENCES C
)
CREATE TABLE F (
a1 d1,
f d4,
PRIMARY KEY ( a1 ),
FOREIGN KEY ( a1 ) REFERENCES C
)
(2)
Generalisierungsklasse und Spezialisierungsklassen in einer Tabelle
Übersetzung nur bedingt einsetzbar!
Problem bei Transformation (2): Umgang mit Assoziationen auf
Spezialisierungsklassen
CREATE TABLE C (
a1 d1,
a2 d2,
e d3 NULL,
f d4 NULL,
PRIMARY KEY ( a1 )
)
( a1 , a2 , e , f )
Für Objekt o in C mit o nicht in F gilt: ( ..., ..., ... , NULL )
Für Objekt o in C mit o nicht in E gilt: ( ..., ..., NULL,
... )
(3)
Generalisierungsklasse und Spezialisierungsklassen in getrennten
Tabellen mit Disjunktheitsforderung
Übersetzung
nur bedingt einsetzbar!
Problem bei
Transformation (3): Umgang mit Assoziationen auf Generalisierungsklasse
CREATE
TABLE C (
a1 d1,
a2 d2,
PRIMARY KEY ( a1 )
)
CREATE TABLE E (
a1 d1,
a2 d2,
e d3,
PRIMARY KEY ( a1 )
)
CREATE TABLE F (
a1 d1,
a2 d2,
f d4,
PRIMARY KEY ( a1 )
)
Für Objekt o in E gilt: o nicht in C
Für Objekt o in F gilt: o nicht in C
Für Objekt o in C gilt: o nicht in E oder F
Äquivalent:
SELECT * FROM E
WHERE EXISTS (SELECT * FROM C WHERE E.a1=C.a1)
= EMPTY
SELECT * FROM F
WHERE EXISTS (SELECT * FROM C WHERE F.a1=C.a1)
= EMPTY
SELECT * FROM C
WHERE EXISTS (SELECT * FROM E WHERE C.a1=E.a1) OR
EXISTS (SELECT * FROM F WHERE C.a1=F.a1)
= EMPTY
Beispiel

CREATE TABLE unternehmen (
name VARCHAR,
adresse VARCHAR,
PRIMARY KEY ( name )
)
CREATE TABLE job (
arbeitgeber VARCHAR,
arbeitnehmer VARCHAR,
PRIMARY KEY ( arbeitgeber, arbeitnehmer ),
FOREIGN KEY ( arbeitgeber ) REFERENCES unternehmen,
FOREIGN KEY ( arbeitnehmer ) REFERENCES person,
)
(1)
CREATE TABLE person (
name VARCHAR,
alter VARCHAR,
PRIMARY KEY ( name )
)
CREATE TABLE frau (
name VARCHAR,
schwanger BOOLEAN,
PRIMARY KEY ( name ),
FOREIGN KEY ( name ) REFERENCES person
)
CREATE TABLE mann (
name VARCHAR,
wehrdienst BOOLEAN,
PRIMARY KEY ( name ),
FOREIGN KEY ( name ) REFERENCES person
)
(2)
CREATE TABLE person (
name VARCHAR,
alter VARCHAR,
schwanger BOOLEAN NULL,
wehrdienst BOOLEAN NULL,
PRIMARY KEY ( name )
)
Nachteilig an dieser Lösung ist, dass Attribute teilweise
unnötig geführt werden. Ferner bleibt die Behandlung von
Assoziationen auf die Klassen Frau oder Mann offen.
(3)
CREATE TABLE person (
name VARCHAR,
alter VARCHAR,
PRIMARY KEY ( name )
)
CREATE TABLE frau (
name VARCHAR,
alter VARCHAR,
schwanger BOOLEAN,
PRIMARY KEY ( name ),
)
CREATE TABLE mann (
name VARCHAR,
alter VARCHAR,
wehrdienst BOOLEAN,
PRIMARY KEY ( name ),
)
Die Tabellen person, mann und frau sollten disjunkte Sachverhalte
wiedergeben, denn das Attribut alter wird in allen drei Tabellen
geführt. Bei Nichtdisjunktheit besteht die Möglichkeit der
Inkonsistenz. Nachteilig an dieser Lösung ist, dass die obige
Tabelle job aber nur auf die Tabelle person, nicht aber auf frau oder
mann verweist.
Relationaler
Datenbankentwurf
Grundidee:
Zerlegung von Tabellen
SKRIPT(VeranstNr, PersonalNr, Name, SkriptNr, Preis)
VeranstNr, PersonalNr -> Name, SkriptNr, Preis
PersonalNr -> Name
SkriptNr -> Preis
SKRIPT aufteilen wegen PersonalNr -> Name:
DOZENT(PersonalNr, Name)
SKRIPT2(VeranstNr, PersonalNr, SkriptNr, Preis)
SKRIPT2 aufteilen wegen SkriptNr -> Preis:
SKRIPT3(VeranstNr, PersonalNr, SkriptNr)
SKRIPTPREIS(SkriptNr, Preis)
Resultat:
DOZENT(PersonalNr, Name)
SKRIPT3(VeranstNr, PersonalNr, SkriptNr)
SKRIPTPREIS(SkriptNr, Preis)
Resultat erweitert um Fremdschlüssel:
DOZENT(PersonalNr, Name)
SKRIPT3(VeranstNr,
PersonalNr->DOZENT.PersonalNr,
SkriptNr->SKRIPTPREIS.SkriptNr)
SKRIPTPREIS(SkriptNr, Preis)
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Skript
VeranstNr PersonalNr Name SkriptNr Preis
112
198 Schulz
2 25
112
237 Lange
9 44
112
011 Meyer
2 25
202
198 Schulz
4 22
Redundanz: VeranstNr PersonalNr Name ... [PersonalNr ->
Name]
112
198 Schulz
202
198 Schulz
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DOZENT
PersonalNr Name
198 Schulz
237 Lange
011 Meyer
Skript2 VeranstNr PersonalNr SkriptNr Preis
112
198
2 25
112
237
9 44
112
011
2 25
202
198
4 22
Redundanz: ... PersonalNr SkriptNr Preis [SkriptNr -> Preis]
198
2 25
011
2 25
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DOZENT
PersonalNr Name
198 Schulz
237 Lange
011 Meyer
Skript3 VeranstNr PersonalNr SkriptNr
112
198 2
112
237 9
112
011 2
202
198 4
SkriptPREIS SkriptNr Preis
2 25
9 44
4 22
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Funktionale
Abhängigkeiten und Normalformen
Zentrale Begriffe im relationalen Datenbankentwurf: Funktionale
Abhängigkeit, Schlüssel, partielle Abhängigkeit,
transitive Abhängigkeit, Zerlegung von Relationenschemata in
Normalform (NF), 1NF, 2NF, 3NF
Gegeben sei ein (oder mehrere) Relationenschema R(A1, ..., An)
über einer Attributmenge A={A1, ..., An}. Eine funktionale
Abhängigkeit (FA) ist ein Paar von Attributmengen notiert als X
-> Y (X und Y Teilmengen von A).
Zentraler Schritt im Entwurf: Alle funktionalen Abhängigkeiten
über der Attributmenge finden. Also fest vorgeben: Menge F von
funktionalen Abhängigkeiten. Gegenenfalls: Aufdecken aller
funktionalen Abhängigkeiten durch systematisches Untersuchen aller
Teilmengen X und Y von A.
Beispiel: SKRIPT(VeranstNr, PersonalNr, Name, SkriptNr, Preis)
F1: VeranstNr, PersonalNr -> Name, SkriptNr, Preis
PersonalNr -> Name
SkriptNr -> Preis
Jede funktionale Abhängigkeit X -> Y schränkt die
erlaubten Datenbankzustände ein: Wenn in R zwei Tupel in X
übereinstimmen, müssen sie auch in Y übereinstimmen.
Anders ausgedrückt: X bestimmt Y, oder X legt Y fest. Eine
funktionale Abhängigkeit ist also eine spezielle
Integritätsbedingung. Die Integritätsbedingung verbietet
Tupel t1 und t2 mit t1.X=t2.X und t1.Y<>t2.Y.
Beispiel: Durch SkriptNr -> Preis wird z.B. verboten:
Skript VeranstNr PersonalNr Name SkriptNr Preis
112
198 Schulz
2 25
112
237 Lange
2 44
Ein Schlüssel S bezüglich R und F ist eine Attributmenge S
(Teilmenge von A), so daß der Schlüssel S die
Gesamtattributmenge bestimmt (S -> A) und S minimal ist, d.h. aus S
darf nichts weggelassen werden. Ein Nicht-Schlüsselattribut ist
ein Attribut das in *keinem* Schlüssel vorkommt.
Beispiel: VeranstNr, PersonalNr ist Schlüssel hinsichtlich F1:
Mit VeranstNr, PersonalNr -> Name, SkriptNr, Preis und
VeranstNr, PersonalNr -> VeranstNr, PersonalNr ergibt sich
VeranstNr, PersonalNr -> Name, SkriptNr, Preis, VeranstNr, PersonalNr
Aus der Menge VeranstNr, PersonalNr darf nichts weggelassen werden,
andernfalls würde VeranstNr, PersonalNr -> SkriptNr nicht
gelten.
Beispiel: VeranstNr, PersonalNr, Name ist kein Schlüssel
hinsichtlich F1, da zwar die Gesamtattributmenge bestimmt wird, die
Menge aber nicht minimal ist, denn Name kann weggelassen werden.
Beispiel: Die Menge bestehend nur aus VeranstNr ist kein
Schlüssel, da z.B. nicht gilt VeranstNr -> SkriptNr. Ebenso
wäre die Menge bestehend nur aus PersonalNr kein Schlüssel.
Beispiel: SKRIPT(VeranstNr, PersonalNr, Name, SkriptNr, Preis)
F2: VeranstNr, PersonalNr -> SkriptNr
PersonalNr -> Name
SkriptNr -> Preis
Die Menge VeranstNr, PersonalNr ist ein Schlüssel hinsichtlich F2.
Aus der FA-Menge F1 und der FA-Menge F2 ergeben sich die gleichen
funktionalen Abhängigkeiten als Konsequenzen.
Beispiel: STADT(SName,XKoor,YKoor,EinwAnz)
F3: SName -> XKoor, YKoor, EinwAnz
XKoor, YKoor -> SName
Es gibt 2 Schlüssel
- SName
- XKoor, YKoor
und ein Nicht-Schlüsselattribut
- EinwAnz
Ein Nicht-Schlüsselattribut N ist partiell abhängig von einem
Schlüssel S, wenn es eine echte Teilmenge Y von S gibt mit Y ->
N und N nicht Element von Y.
Ein Nicht-Schlüsselattribut N ist transitiv abhängig von
einem Schlüssel S, wenn es eine Teilmenge Y von A gibt mit S ->
Y, Y -> N, N nicht Element von Y und nicht (Y -> S).
Ein Relationenschema ist bezüglich einer gegebenen Menge F von
funktionalen Abhängigkeiten in
- erster Normalform (1NF), wenn alle Attributwerte atomar sind, d.h.
ein Attributwert nicht eine Menge von Werten darstellt,
- zweiter Normalform (2NF), wenn es keine partiellen
Abhängigkeiten eines Nicht-Schlüsselattributs von einem
Schlüssel gibt, und
- dritter Normalform (3NF), wenn es keine transitiven
Abhängigkeiten eines Nicht-Schlüsselattributs von einem
Schlüssel gibt.
Beispiel:
SKRIPT(VeranstNr, PersonalNr, Name, SkriptNr, Preis)
mit VeranstNr, PersonalNr -> Name, SkriptNr, Preis
PersonalNr -> Name
SkriptNr -> Preis
Schlüssel: VeranstNr, PersonalNr
Nicht-Schlüsselattribute: Name, SkriptNr, Preis
Nicht in 2NF wegen PersonalNr -> Name
Nicht in 3NF wegen VeranstNr, PersonalNr -> SkriptNr -> Preis
Beispiel:
SKRIPT(VPId, VeranstNr, PersonalNr, Name, SkriptNr, Preis)
mit VPId -> VeranstNr, PersonalNr, Name, SkriptNr, Preis
Schlüssel: VPId
Nicht-Schlüsselattribute: VeranstNr,
PersonalNr, Name, SkriptNr, Preis
Relationenschema
inklusive der FAen ist zwar in 2NF und 3NF,
aber nur weil die FAen *nicht* vollständig angegeben worden sind.
Beispiel:
DOZENT(PersonalNr, Name)
mit PersonalNr -> Name
SKRIPT3(VeranstNr, PersonalNr, SkriptNr)
mit VeranstNr PersonNr -> SkriptNr
SKRIPTPREIS(SkriptNr, Preis)
mit SkriptNr -> Preis
Jedes Relationenschema ist bzgl. der angegebenen FAen in 3NF.
Beispiel: STADT(SName,XKoor,YKoor,EinwAnz)
F3: SName -> XKoor, YKoor, EinwAnz
XKoor, YKoor -> SName
Ist in 3NF obwohl XKoor, YKoor -> SName -> EinwAnz gilt
(Es gilt SName -> XKoor, YKoor)
F4: SName -> XKoor, Ykoor
XKoor, YKoor -> SName, EinwAnz
F3 und F4 haben die gleichen funktionalen Abhängigkeiten als
Konsequenzen.
More
complex example: Persons and their children
A person has a first and a last name and a gender indicated by 'F' for
female or 'M' for male. A person can have other another person as a
mother or as a father. Only female persons can play the mother role,
only male persons the father role. A female person cannot be mother of
herself. Analogously for male persons: A male person cannot be father
of himself. The following cycle involving two persons is not allowed:
There are two distinct persons p1 and p2 where p1 is the mother of p2
and p2 is the mother of p1; analogously for fathers. An analogous
requirement for cycles with three persons has to be guaranteed as well.
Desirable: Exclude cycles with arbitrary lenght; not possible in
SQL-86, SQL-89, or SQL-92; possible in SQL:1999 with recursive query
feature
Table
definition
CREATE TABLE person (
fname VARCHAR NOT NULL,
lname VARCHAR NOT NULL,
gender CHAR(1) NOT NULL,
motherfname VARCHAR NULL,
motherlname VARCHAR NULL,
fatherfname VARCHAR NULL,
fatherlname VARCHAR NULL,
CONSTRAINT fname_lname_key
PRIMARY KEY ( fname, lname ),
CONSTRAINT mother_fname_lname_foreign_key
FOREIGN KEY ( motherfname, motherlname ) REFERENCES
person,
CONSTRAINT father_fname_lname_foreign_key
FOREIGN KEY ( fatherfname, fatherlname ) REFERENCES
person
);
> NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
> "fname_lname_key" for table "person"
> CREATE TABLE
Remark: Responses from PostgreSQL are indicated by '> '.
Constraints
on name format and gender
CREATE FUNCTION name_format(TEXT) RETURNS BOOLEAN AS $$
(SELECT NOT ( $1='' OR
$1 LIKE '%0%' OR
$1 LIKE '%1%' OR $1 LIKE '%2%' OR
$1 LIKE '%3%' OR
$1 LIKE '%4%' OR $1 LIKE '%5%' OR
$1 LIKE '%6%' OR
$1 LIKE '%7%' OR $1 LIKE '%8%' OR
$1 LIKE '%9%' OR
$1 LIKE '% %' OR $1 LIKE '%/_%' ) )
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT fname_format
CHECK ( name_format(fname) );
> ALTER TABLE
ALTER TABLE person ADD CONSTRAINT lname_format
CHECK ( name_format(lname) );
> ALTER TABLE
ALTER TABLE person ADD CONSTRAINT gender_female_or_male
CHECK ( gender='F' OR gender='M' );
> ALTER TABLE
Constraints
no_mother_cycle
CREATE FUNCTION cycle1(TEXT,TEXT,TEXT,TEXT) RETURNS BOOLEAN AS $$
(SELECT $1=$3 AND $2=$4)
$$ LANGUAGE SQL;
> CREATE FUNCTION
-- Forbid:
-- new [ fname lname fname
lname
...
... ]
-- FNAME LNAME MOTHFNAME
MOTHLNAME FATHFNAME FATHLNAME
ALTER TABLE person ADD CONSTRAINT no_mother_cycle1
--CHECK ( NOT
(fname,lname)=(motherfname,motherlname) );
CHECK ( NOT cycle1(fname,lname,motherfname,motherlname ) );
> ALTER TABLE
CREATE FUNCTION mother_cycle2(TEXT,TEXT,TEXT,TEXT) RETURNS BOOLEAN AS $$
(SELECT EXISTS (SELECT * FROM person p
WHERE p.motherfname=$1 AND p.motherlname=$2 AND
p.fname=$3 AND p.lname=$4) )
$$ LANGUAGE SQL;
> CREATE FUNCTION
-- Forbid:
-- old [ mothfname mothlname
fname
lname ... ... ]
-- new [ fname
lname mothfname mothlname ...
... ]
ALTER TABLE person ADD CONSTRAINT no_mother_cycle2
CHECK ( NOT mother_cycle2(fname,lname,motherfname,motherlname) );
> ALTER TABLE
CREATE FUNCTION mother_cycle3(TEXT,TEXT,TEXT,TEXT) RETURNS BOOLEAN AS $$
(SELECT EXISTS
(SELECT * FROM person p1, person p2
WHERE p1.motherfname=$1 AND p1.motherlname=$2
AND
p2.motherfname=p1.fname AND p2.motherlname=p1.lname AND
p2.fname=$3 AND p2.lname=$4) )
$$ LANGUAGE SQL;
> CREATE FUNCTION
-- Forbid:
-- old1 [ xxx
yyy
fname
lname ... ... ]
-- old2 [ mothfname mothlname
xxx
yyy ... ... ]
-- new [ fname
lname mothfname mothlname ...
... ]
ALTER TABLE person ADD CONSTRAINT no_mother_cycle3
CHECK ( NOT mother_cycle3(fname,lname,motherfname,motherlname) );
> ALTER TABLE
Constraints
no_father_cycle
ALTER TABLE person ADD CONSTRAINT no_father_cycle1
--CHECK ( NOT
(fname,lname)=(fatherfname,fatherlname) );
CHECK (
NOT cycle1(fname,lname,fatherfname,fatherlname) );
> ALTER TABLE
CREATE FUNCTION father_cycle2(TEXT,TEXT,TEXT,TEXT) RETURNS BOOLEAN AS $$
(SELECT EXISTS (SELECT * FROM person p
WHERE p.fatherfname=$1 AND p.fatherlname=$2 AND
p.fname=$3 AND p.lname=$4) )
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT no_father_cycle2
CHECK ( NOT father_cycle2(fname,lname,fatherfname,fatherlname) );
> ALTER TABLE
CREATE FUNCTION father_cycle3(TEXT,TEXT,TEXT,TEXT) RETURNS BOOLEAN AS $$
(SELECT EXISTS
(SELECT * FROM person p1, person p2
WHERE p1.fatherfname=$1 AND p1.fatherlname=$2
AND
p2.fatherfname=p1.fname AND p2.fatherlname=p1.lname AND
p2.fname=$3 AND p2.lname=$4) )
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT no_father_cycle3
CHECK ( NOT father_cycle3(fname,lname,fatherfname,fatherlname) );
> ALTER TABLE
Constraint
mother_is_female
CREATE FUNCTION mother_is_female(TEXT,TEXT) RETURNS BOOLEAN AS $$
SELECT NOT EXISTS (SELECT * FROM person
WHERE fname=$1 and lname=$2 AND gender='M') AND
NOT EXISTS (SELECT *
FROM person
WHERE fatherfname=$1 AND fatherlname=$2)
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT mother_is_female
CHECK ( (motherfname IS NULL AND motherlname IS NULL) OR
mother_is_female(motherfname,motherlname) );
> ALTER TABLE
CREATE FUNCTION not_father(TEXT,TEXT) RETURNS BOOLEAN AS $$
SELECT NOT EXISTS (SELECT * FROM person
WHERE fatherfname=$1 AND fatherlname=$2)
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT female_not_father
CHECK (NOT(gender='F') OR not_father(fname,lname));
--CHECK (gender='F' IMPLIES not_father(fname,lname));
> ALTER TABLE
Constraint
father_is_male
CREATE
FUNCTION father_is_male(TEXT,TEXT) RETURNS BOOLEAN AS $$
SELECT NOT EXISTS (SELECT * FROM person
WHERE fname=$1 and lname=$2 AND gender='F') AND
NOT EXISTS (SELECT *
FROM person
WHERE motherfname=$1 AND motherlname=$2)
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT father_is_male
CHECK ( (fatherfname IS NULL AND fatherlname IS NULL) OR
father_is_male(fatherfname,fatherlname) );
> ALTER TABLE
CREATE FUNCTION not_mother(TEXT,TEXT) RETURNS BOOLEAN AS $$
SELECT NOT EXISTS (SELECT * FROM person
WHERE motherfname=$1 AND motherlname=$2)
$$ LANGUAGE SQL;
> CREATE FUNCTION
ALTER TABLE person ADD CONSTRAINT male_not_mother
CHECK (NOT(gender='M') OR not_mother(fname,lname));
--CHECK (gender='M' IMPLIES not_mother(fname,lname));
> ALTER TABLE
Overview
on specified constraints
CONSTRAINT fname_lname_key
CONSTRAINT mother_fname_lname_foreign_key
CONSTRAINT father_fname_lname_foreign_key
CONSTRAINT fname_format
CONSTRAINT lname_format
CONSTRAINT gender_female_or_male
CONSTRAINT no_mother_cycle1
CONSTRAINT no_mother_cycle2
CONSTRAINT no_mother_cycle3
CONSTRAINT no_father_cycle1
CONSTRAINT no_father_cycle2
CONSTRAINT no_father_cycle3
CONSTRAINT mother_is_female
CONSTRAINT female_not_father
CONSTRAINT father_is_male
CONSTRAINT male_not_mother
Test
scenarios I
INSERT INTO person VALUES ('Ada','Black','F','Ada','Black',NULL, NULL);
> ERROR: new row for relation "person" violates check constraint
> "no_mother_cycle1"
INSERT INTO person VALUES ('Bea','Black','F',NULL, NULL,
NULL, NULL);
> INSERT 0 1
INSERT INTO person VALUES ('Cia','Black','F','Bea','Black',NULL, NULL);
> INSERT 0 1
UPDATE person SET motherfname='Cia', motherlname='Black'
WHERE fname='Bea' AND lname='Black';
> ERROR: new row for relation "person" violates check constraint
> "no_mother_cycle2"
INSERT INTO person VALUES ('Ada','Black','F',NULL, NULL, NULL, NULL);
> INSERT 0 1
UPDATE person SET motherfname='Ada', motherlname='Black'
WHERE fname='Bea' AND lname='Black';
> UPDATE 1
UPDATE person SET motherfname='Cia', motherlname='Black'
WHERE fname='Ada' AND lname='Black';
> ERROR: new row for relation "person" violates check constraint
> "no_mother_cycle3"
INSERT INTO person VALUES ('Bea','Black','F',NULL, NULL, NULL, NULL );
> ERROR: duplicate key violates unique constraint "fname_lname_key"
INSERT INTO person VALUES ('Cyd','Black','M',NULL, NULL, NULL, NULL );
> INSERT 0 1
INSERT INTO person
VALUES ('Ada','Black','F','Bea','Black','Cyd','Black');
> ERROR: new row for relation "person" violates check constraint
> "no_mother_cycle2"
INSERT INTO person VALUES ('Dan','Green','M',NULL, NULL, NULL, NULL );
> INSERT 0 1
INSERT INTO person
VALUES ('Eva','Black','F','Ada','Black','Dan','Green');
> INSERT 0 1
UPDATE person SET motherfname='Eva', motherlname='Black'
WHERE fname='Bea' AND lname='Black';
> UPDATE 1
UPDATE person SET gender='F' WHERE fname='Dan' AND lname='Green';
> ERROR: new row for relation "person" violates check constraint
> "female_not_father"
SELECT * FROM person ORDER BY 1;
fname | lname | gender | mothfname | mothlname | fathfname |
fathlname
-------+-------+--------+-----------+-----------+-----------+-----------
Ada | Black | F
|
|
| |
Bea | Black | F |
Eva | Black
| |
Cia | Black | F |
Bea | Black
| |
Cyd | Black | M
|
|
| |
Dan | Green | M
|
|
| |
Eva | Black | F |
Ada | Black
| Dan | Green
(6 rows)
Remark: Column names shortened for layout reasons
-- Ada Black Dan Green Cyd Black
--
\ /
-- Eva Black
-- |
-- Bea Black
-- |
-- Cia Black
Test
scenarios II
INSERT INTO person
VALUES ('Flo','Black','W','Bea','Black','Cyd','Black');
> ERROR: new row for relation "person" violates check constraint
> "gender_female_or_male"
INSERT INTO person
VALUES (Null, 'Black','F','Bea','Black','Cyd','Black');
> ERROR: null value in column "fname" violates not-null constraint
INSERT INTO person
VALUES ('Ada','Black','M','Bea','Black','Cyd','Black');
> ERROR: new row for relation "person" violates check constraint
> "male_not_mother"
INSERT INTO person
VALUES ('','Black','M','Bea','Black','Cyd','Black');
> ERROR: new row for relation "person" violates check constraint
> "fname_format"
INSERT INTO person
VALUES ('Eva','Black','F','Eva','Black','Cyd','Black');
> ERROR: new row for relation "person" violates check constraint
> "no_mother_cycle1"
INSERT INTO person
VALUES ('Eva','Black','F','Bea','Black','Eva','Black');
> ERROR: new row for relation "person" violates check constraint
> "father_is_male"
Overview
on occured errors (in scenarios)
ERROR: new
row for relation "person" violates check constraint
"no_mother_cycle1"
ERROR: new row for relation "person" violates check constraint
"no_mother_cycle2"
ERROR: new row for relation "person" violates check constraint
"no_mother_cycle3"
ERROR: duplicate key violates unique constraint
"fname_lname_key"
ERROR: new row for relation "person" violates check constraint
"no_mother_cycle2"
ERROR: new row for relation "person" violates check constraint
"female_not_father"
ERROR: new row for relation "person" violates check constraint
"gender_female_or_male"
ERROR: null value in column "fname" violates
not-null constraint
ERROR: new row for relation "person" violates check constraint
"male_not_mother"
ERROR: new row for relation "person" violates check constraint
"fname_format"
ERROR: new row for relation "person" violates check constraint
"no_mother_cycle1"
ERROR: new row for relation "person" violates check constraint
"father_is_male"