Objektrelationale, erweiterbare Datenbanken WS 04/05 Übung 8

Werbung
Institut für Informationssysteme
Dr. C. Türker
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of Technology Zurich
END;
/
Objektrelationale, erweiterbare Datenbanken WS 04/05
Übung 8
Beispiellösung
Aufgabe 1: Objektrelationales Oracle-SQL
Setzen Sie die Klassenhierarchie Artikel (Klassen: Artikel, ArtikelSet, Lebensmittel, Haushaltswaren) und die Klasse Katalog aus dem UML-Diagramm von Übung 1 mit Hilfe der objektrelationalen Erweiterungen von Oracle um. Verwenden Sie dabei Objekttypen-/tabellen.
Beispiellösung: Oracle unterstützt keine Subtabellen. Daher muss die UML-Klassenhierarchie
auf einem anderen Weg auf Oracle-SQL abgebildet werden. Die folgende Lösung nutzt die
Konzepte der Objekttypen, Subtypbildung und Substituierbarkeit, um die komplette Klassenhierarchie auf eine Objekttabelle abzubilden, wobei die einzelnen Klassen durch Objektsichten
repräsentiert werden.
Bei den Aggregationsbeziehungen wurden die Kardinalitäten vernachlässigt.
CREATE TYPE BildKollektionsTyp AS TABLE OF BLOB;
/
CREATE TYPE ArtikelTyp AS OBJECT (
Nummer
CHAR(12),
Bezeichnung VARCHAR(40),
Preis
DECIMAL(12,2),
Beschreibung VARCHAR(2000),
Bilder
BildKollektionsTyp
) NOT FINAL;
/
CREATE TYPE LebensmittelTyp UNDER ArtikelTyp (
Gewicht DECIMAL(10),
Zusammensetzung VARCHAR(400),
MEMBER FUNCTION GewichtInKilo RETURN INTEGER
) FINAL;
/
CREATE TYPE BODY LebensmittelTyp AS
MEMBER FUNCTION GewichtInKilo RETURN INTEGER AS
BEGIN
RETURN SELF.Gewicht / 1000;
END;
END;
/
CREATE TYPE HaushaltswarenTyp UNDER ArtikelTyp (
Garantie DATE,
Farbe
VARCHAR(30)
) FINAL;
/
CREATE TYPE KatalogTyp AS OBJECT (
Titel
VARCHAR(30),
Jahr
DECIMAL(4),
Sommer CHAR(1),
Artikel ArtikelKollektionsTyp,
MEMBER FUNCTION AnzahlArtikel RETURN INTEGER
) NOT FINAL;
/
CREATE TYPE ArtikelKollektionsTyp AS TABLE OF REF ArtikelTyp;
/
CREATE TYPE ArtikelSetTyp UNDER ArtikelTyp (
Artikel
ArtikelKollektionsTyp,
MEMBER FUNCTION AnzahlArtikel RETURN INTEGER
) FINAL;
/
CREATE TYPE BODY KatalogTyp AS
MEMBER FUNCTION AnzahlArtikel RETURN INTEGER AS
BEGIN
RETURN SELF.Artikel.COUNT;
END;
END;
/
CREATE TYPE BODY ArtikelSetTyp AS
MEMBER FUNCTION AnzahlArtikel RETURN INTEGER AS
BEGIN
RETURN SELF.Artikel.COUNT;
END;
CREATE TABLE Katalog OF KatalogTyp (
PRIMARY KEY(Jahr, Sommer),
Titel NOT NULL
)
NESTED TABLE Artikel STORE AS KatalogArtikel;
1
2
CREATE TABLE Artikel OF ArtikelTyp (
Nummer
PRIMARY KEY,
Bezeichnung NOT NULL
)
NESTED TABLE Bilder STORE AS ArtikelBilder;
CREATE VIEW ArtikelSet OF ArtikelSetTyp
AS SELECT TREAT(VALUE(a) AS ArtikelSetTyp)
FROM Artikel a
WHERE VALUE(a) IS OF (ONLY ArtikelSetTyp);
CREATE VIEW AllgemeineArtikel OF ArtikelTyp
AS SELECT TREAT(VALUE(a) AS ArtikelTyp)
FROM Artikel a
WHERE VALUE(a) IS OF (ONLY ArtikelTyp);
CREATE VIEW Lebensmittel OF LebensmittelTyp
AS SELECT TREAT(VALUE(a) AS LebensmittelTyp)
FROM Artikel a
WHERE VALUE(a) IS OF (ONLY LebensmittelTyp);
’Auf den Bergen von ...’,
NULL, 700, ’Keine Ahnung’));
INSERT INTO Artikel VALUES
(HaushaltswarenTyp(’343-1197-71’, ’Mixer’, 59.99,
’Super leicht ...’,
NULL, CURRENT_DATE, ’Weiss’));
SELECT Nummer, Bezeichnung, Preis, Beschreibung
FROM Artikel;
SELECT Nummer, Bezeichnung, Preis, Beschreibung
FROM AllgemeineArtikel;
SELECT Nummer, Bezeichnung, Preis, Beschreibung,
Artikel, a.AnzahlArtikel()
FROM ArtikelSet a;
SELECT Nummer, Bezeichnung, Preis, Beschreibung,
Gewicht, Zusammensetzung
FROM Lebensmittel;
CREATE VIEW Haushaltswaren OF HaushaltswarenTyp
AS SELECT TREAT(VALUE(a) AS HaushaltswarenTyp)
FROM Artikel a
WHERE VALUE(a) IS OF (ONLY HaushaltswarenTyp);
SELECT Nummer, Bezeichnung, Preis, Beschreibung,
Garantie, Farbe
FROM Haushaltswaren;
INSERT INTO Artikel VALUES
(ArtikelTyp(’123-4567-98’, ’DVD’, 24.99,
’Goldeneye ...’, NULL));
Aufgabe 2: Entwurf einer Funktion in Oracle-PL/SQL
INSERT INTO Artikel VALUES
(ArtikelTyp(’123-4567-99’, ’DVD’, 24.99,
’Goldfinger ...’, NULL));
Schreiben Sie eine Funktion, die als Parameter einen Primärschlüssel und das monatliche Einkommen eines Kunden erhält, dessen Kreditlimit errechnet werden soll. Dieses Limit beträgt
normalerweise das dreifache seines Gehalts, minimal aber 500 und maximal 5000 Franken. Der
so ermittelte Wert soll zurückgegeben werden.
Aufgabe 3: Entwurf einer Prozedur in Oracle-PL/SQL
INSERT INTO Artikel VALUES
(ArtikelSetTyp(’989-4567-98’, ’DVD-Box’, 559.99,
’Die Box enthält ...’, NULL,
CAST(MULTISET(SELECT REF(a)
FROM Artikel a
WHERE Bezeichnung = ’DVD’
AND Beschreibung LIKE ’Gold%’)
AS ArtikelKollektionsTyp)));
INSERT INTO Artikel VALUES
(LebensmittelTyp(’481-4427-15’, ’Wein’, 34.99,
3
Entwerfen Sie eine Prozedur, die wiederum den Primärschlüssel eines Kunden und eine neue
Telefonnummer übergeben bekommt. Diese Telefonnummer soll nun zusätzlich zu den bisher
vorhandenen gespeichert werden.
Beispiellösung für Aufgabe 1 und 2:
Bevor wir die Funktion bzw. Prozedur umsetzen, wollen wir die zugrunde liegende Tabellen erzeugen, damit wir die Routinen später auch testen können. In Übung 8 hatten wir
bereits einen Teil des Anwendungsszenarios aus Übung 1 umgesetzt. Darauf aufbauend
implementieren wir zunächst die restlichen Klassen und danach die geforderten Routinen.
4
CREATE TYPE TelefonTabellenTyp AS TABLE OF VARCHAR(20); /
CREATE TYPE AdresseTyp AS OBJECT (
Strasse VARCHAR(30),
Nr
DECIMAL(4),
PLZ
DECIMAL(5),
Ort
VARCHAR(40),
Land
VARCHAR(25)
) NOT FINAL; /
CHECK(Status IN (’InBearbeitung’, ’Eingegangen’,
’Geliefert’, ’Bezahlt’))
) NESTED TABLE Positionen STORE AS Positionen;
CREATE TABLE Kunde OF KundeTyp (
PRIMARY KEY(Kundennummer),
Name
NOT NULL,
Anschrift
NOT NULL,
CHECK(Anschrift.PLZ IS NOT NULL),
CHECK(Kreditlimit <= 5000)
) NESTED TABLE Bestellungen STORE AS Kundenbestellungen NESTED TABLE
Telefone STORE AS Kundentelefone;
CREATE TYPE PositionTyp AS OBJECT (
Artikel REF ArtikelTyp,
Anzahl INTEGER
); /
CREATE TYPE PositionTabellenTyp AS TABLE OF PositionTyp; /
CREATE TYPE BestellungsTyp AS OBJECT (
Bestellnummer INTEGER,
Eingangsdatum DATE,
Status VARCHAR(15),
Positionen PositionTabellenTyp
); /
CREATE TYPE BestellungsTabellenTyp AS TABLE OF REF BestellungsTyp; /
CREATE TYPE PersonTyp AS OBJECT (
Name
VARCHAR(30),
Anschrift
AdresseTyp,
Telefone
TelefonTabellenTyp,
Email
VARCHAR(25)
) NOT FINAL NOT INSTANTIABLE; /
CREATE TYPE KundeTyp UNDER PersonTyp (
Kundennummer INTEGER,
Bestellungen BestellungsTabellenTyp,
Kreditlimit DECIMAL(12,2)
) FINAL; /
CREATE TYPE AngestellterTyp UNDER PersonTyp (
Personalnummer INTEGER,
Gehalt
DECIMAL(12,2)
) FINAL; /
CREATE TABLE Bestellung OF BestellungsTyp (
PRIMARY KEY(Bestellnummer),
5
CREATE TABLE Angestellte OF AngestellterTyp (
PRIMARY KEY(Personalnummer),
Name
NOT NULL,
Anschrift
NOT NULL,
CHECK(Anschrift.PLZ IS NOT NULL),
CHECK(Gehalt >= 2000)
) NESTED TABLE Telefone STORE AS Angestelltentelefone;
---------------------------------------------------- Funktion Kundenkredit berechnet das Kreditlimit
-- basierend auf dem Gehalt eines Kunden
--------------------------------------------------CREATE FUNCTION Kundenkredit(gehalt DECIMAL) RETURN DECIMAL AS
limit DECIMAL;
BEGIN
limit := 3 * gehalt;
IF limit < 500.0 THEN limit:= 500;
ELSE IF limit > 5000.0 THEN limit:= 5000; END IF;
END IF;
RETURN limit;
END; /
---------------------------------------------------- PROCEDURE NeueNummer fügt eine (neue)
-- Telefonnummer in die tabellenwertige Spalte
-- Telefone eines bestimmten Kunden ein
--------------------------------------------------CREATE PROCEDURE NeueNummer(knr INTEGER, nn VARCHAR) AS BEGIN
INSERT INTO TABLE(SELECT Telefone
FROM Kunde
WHERE Kundennummer = knr)
VALUES(nn);
END; /
6
---------------------------------------------------- Basierend auf den Artikel-Daten aus Uebung 8
-- erzeugen wir nun Daten für Bestellungen und
-- Kunden, um die neue Routinen zu testen
--------------------------------------------------INSERT INTO Bestellung VALUES (7989,
CURRENT_DATE,
’Eingegangen’,
PositionTabellenTyp(
PositionTyp((SELECT REF(a)
FROM Artikel a
WHERE Nummer = ’123-4567-98’),
1)));
INSERT INTO Bestellung VALUES (6134,
CURRENT_DATE,
’InBearbeitung’,
PositionTabellenTyp(
PositionTyp((SELECT REF(a)
FROM Artikel a
WHERE Nummer = ’989-4567-98’),
1),
PositionTyp((SELECT REF(a)
FROM Artikel a
WHERE Nummer = ’481-4427-15’),
12)));
---------------------------------------------------- Funktion Kundenkredit wird beim Erzeugen
-- eines Kunden verwendet
--------------------------------------------------INSERT INTO Kunde VALUES(’James’,
AdresseTyp(’Haldeneggsteig’, 4, 8092, ’Zürich’, ’CH’),
TelefonTabellenTyp(’0041-1-9876543’,
’0049-611-6733219’),
’[email protected]’,
1704,
CAST(MULTISET(SELECT REF(b)
FROM Bestellung b
WHERE Bestellnummer = 7989)
AS BestellungsTabellenTyp),
Kundenkredit(1000.0));
INSERT INTO Kunde VALUES(’John’,
AdresseTyp(’Seestrasse’, 13, 8008, ’Zürich’, ’CH’),
7
TelefonTabellenTyp(’0041-1-2612345’),
’[email protected]’,
6970,
NULL,
Kundenkredit(3000.0));
---------------------------------------------------- Aufruf der Prozedur NeueNummer
--------------------------------------------------CALL NeueNummer(1704, ’0041-1-45678912’);
---------------------------------------------------- Es ist auch möglich, die folgende Funktion zu
-- definieren
--------------------------------------------------CREATE FUNCTION Kundenlimit(knr INTEGER,
gehalt DECIMAL)
RETURN DECIMAL AS
limit DECIMAL;
BEGIN
limit := 3 * gehalt;
IF limit < 500.0 THEN limit:= 500;
ELSE IF limit > 5000.0 THEN limit:= 5000; END IF;
END IF;
UPDATE Kunde
SET Kreditlimit = limit
WHERE Kundennummer = knr;
RETURN limit;
END; /
---------------------------------------------------- Ein Aufruf dieser Funktion ist innerhalb einer
-- Anfrage nicht erlaubt
--------------------------------------------------SELECT Kundenlimit(Kundennummer, 1500)
FROM Kunde
WHERE Kundennummer = 6970;
---------------------------------------------------- Oracle gibt die folgende Fehlermeldung aus:
---------------------------------------------------- FEHLER in Zeile 1:
-- ORA-14551: DML-Vorgang kann innerhalb einer
-Abfrage nicht ausgeführt werden
-- ORA-06512: in "SYSTEM.KUNDENLIMIT", Zeile 10
8
---------------------------------------------------- Ebenso ist ein Aufruf ein solchen Funktion
-- nicht in einer DML-Anweisung gestattet
--------------------------------------------------UPDATE Kunde
SET Kreditlimit = Kundenlimit(Kundennummer, 1500)
WHERE Kundennummer = 6970;
---------------------------------------------------- Oracle gibt die folgende Fehlermeldung aus:
---------------------------------------------------- FEHLER in Zeile 2:
-- ORA-04091: Tabelle SYSTEM.KUNDE wird gerade
-geändert, Trigger/Funktion sieht
-dies
möglicherweise nicht
-- ORA-06512: in "SYSTEM.KUNDENLIMIT", Zeile 10
---------------------------------------------------- Folgender Aufruf in einem PL/SQL-Block ist
-- dagegen möglich
--------------------------------------------------DECLARE
test DECIMAL;
Begin test := Kundenlimit(6970, 1500); END; /
9
Institut für Informationssysteme
Dr. C. Türker
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of Technology Zurich
Objektrelationale, erweiterbare Datenbanken WS 04/05
Übung 8 (Extra)
Beispiellösung
Aufgabe 1: Rekursion
Gegeben sei folgende Definition einer Tabelle, die Verwandtschaftsbeziehungen verwaltet:
CREATE TABLE Stammbaum
(
MenschID
INTEGER PRIMARY KEY,
Name
VARCHAR(30) NOT NULL,
GebDatum
DATE,
Mutter
INTEGER REFERENCES Stammbaum,
Vater
INTEGER REFERENCES Stammbaum
);
1. Ermitteln Sie mittels SQL:2003 alle Vorfahren von ’Jim’.
WITH RECURSIVE Vorfahren(MenschID, Vorfahr) AS
(
SELECT MenschID, Mutter
FROM Stammbaum
WHERE Name = ’Jim’
UNION
SELECT MenschID, Vater
FROM Stammbaum
WHERE Name = ’Jim’
UNION
SELECT v.MenschID, s.Mutter
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
UNION
SELECT v.MenschID, s.Vater
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
)
SELECT *
FROM Vorfahren
WHERE Vorfahr IS NOT NULL
ORDER BY MenschID;
2. Ermitteln Sie mittels SQL:2003 alle Nachkommen von ’Bob’.
1
WITH RECURSIVE Nachkommen(MenschID, Nachkomme) AS
(
SELECT Mutter, MenschID
FROM Stammbaum
UNION
SELECT Vater, MenschID
FROM Stammbaum
UNION
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Mutter
UNION
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Vater
)
SELECT *
FROM Nachkommen
WHERE MenschID IN (SELECT MenschID
FROM Stammbaum
WHERE Name = ’Bob’)
ORDER BY MenschID;
3. Setzen Sie die obige Tabelle und die rekursiven Anfragen in DB2 um.
DROP TABLE Stammbaum $ CREATE TABLE Stammbaum (
MenschID
INTEGER NOT NULL PRIMARY KEY,
Name
VARCHAR(30) NOT NULL,
GebDatum
DATE,
Mutter
INTEGER REFERENCES Stammbaum,
Vater
INTEGER REFERENCES Stammbaum
) $
INSERT INTO Stammbaum VALUES (1, ’Bob’, ’11-29-1927’, NULL, NULL) $
INSERT INTO Stammbaum VALUES (2, ’Joe’, ’10-23-1952’, NULL, 1) $
INSERT INTO Stammbaum VALUES (7, ’Mark’, ’08-30-1957’, NULL, NULL) $
INSERT INTO Stammbaum VALUES (3, ’Joanna’, ’09-12-1954’, NULL, NULL)
$ INSERT INTO Stammbaum VALUES (4, ’Kim’, ’03-15-1997’, 3, 2) $
INSERT INTO Stammbaum VALUES (5, ’Jim’, ’12-24-1995’, 3, 2) $ INSERT
INTO Stammbaum VALUES (6, ’Cloe’, ’05-07-1999’, 3, 7) $
WITH Vorfahren(MenschID, Vorfahr) AS (
SELECT MenschID, Mutter
FROM Stammbaum
WHERE Name = ’Jim’
UNION ALL
SELECT MenschID, Vater
2
FROM Stammbaum
WHERE Name = ’Jim’
UNION ALL
SELECT v.MenschID, s.Mutter
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
UNION ALL
SELECT v.MenschID, s.Vater
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
)
SELECT *
FROM Vorfahren
WHERE Vorfahr IS NOT NULL
ORDER BY MenschID $
WITH Nachkommen(MenschID, Nachkomme) AS (
SELECT Mutter, MenschID
FROM Stammbaum
UNION ALL
SELECT Vater, MenschID
FROM Stammbaum
UNION ALL
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Mutter
UNION ALL
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Vater
)
SELECT *
FROM Nachkommen
WHERE MenschID IN (SELECT MenschID
FROM Stammbaum
WHERE Name = ’Bob’)
ORDER BY MenschID $
3
Institut für Informationssysteme
Dr. C. Türker
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of Technology Zurich
Objektrelationale, erweiterbare Datenbanken WS 04/05
Übung 8 (Extra)
Beispiellösung
Aufgabe 1: Rekursion
Gegeben sei folgende Definition einer Tabelle, die Verwandtschaftsbeziehungen verwaltet:
CREATE TABLE Stammbaum
(
MenschID
INTEGER PRIMARY KEY,
Name
VARCHAR(30) NOT NULL,
GebDatum
DATE,
Mutter
INTEGER REFERENCES Stammbaum,
Vater
INTEGER REFERENCES Stammbaum
);
1. Ermitteln Sie mittels SQL:2003 alle Vorfahren von ’Jim’.
WITH RECURSIVE Vorfahren(MenschID, Vorfahr) AS
(
SELECT MenschID, Mutter
FROM Stammbaum
WHERE Name = ’Jim’
UNION
SELECT MenschID, Vater
FROM Stammbaum
WHERE Name = ’Jim’
UNION
SELECT v.MenschID, s.Mutter
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
UNION
SELECT v.MenschID, s.Vater
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
)
SELECT *
FROM Vorfahren
WHERE Vorfahr IS NOT NULL
ORDER BY MenschID;
2. Ermitteln Sie mittels SQL:2003 alle Nachkommen von ’Bob’.
1
WITH RECURSIVE Nachkommen(MenschID, Nachkomme) AS
(
SELECT Mutter, MenschID
FROM Stammbaum
UNION
SELECT Vater, MenschID
FROM Stammbaum
UNION
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Mutter
UNION
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Vater
)
SELECT *
FROM Nachkommen
WHERE MenschID IN (SELECT MenschID
FROM Stammbaum
WHERE Name = ’Bob’)
ORDER BY MenschID;
3. Setzen Sie die obige Tabelle und die rekursiven Anfragen in DB2 um.
DROP TABLE Stammbaum $ CREATE TABLE Stammbaum (
MenschID
INTEGER NOT NULL PRIMARY KEY,
Name
VARCHAR(30) NOT NULL,
GebDatum
DATE,
Mutter
INTEGER REFERENCES Stammbaum,
Vater
INTEGER REFERENCES Stammbaum
) $
INSERT INTO Stammbaum VALUES (1, ’Bob’, ’11-29-1927’, NULL, NULL) $
INSERT INTO Stammbaum VALUES (2, ’Joe’, ’10-23-1952’, NULL, 1) $
INSERT INTO Stammbaum VALUES (7, ’Mark’, ’08-30-1957’, NULL, NULL) $
INSERT INTO Stammbaum VALUES (3, ’Joanna’, ’09-12-1954’, NULL, NULL)
$ INSERT INTO Stammbaum VALUES (4, ’Kim’, ’03-15-1997’, 3, 2) $
INSERT INTO Stammbaum VALUES (5, ’Jim’, ’12-24-1995’, 3, 2) $ INSERT
INTO Stammbaum VALUES (6, ’Cloe’, ’05-07-1999’, 3, 7) $
WITH Vorfahren(MenschID, Vorfahr) AS (
SELECT MenschID, Mutter
FROM Stammbaum
WHERE Name = ’Jim’
UNION ALL
SELECT MenschID, Vater
2
FROM Stammbaum
WHERE Name = ’Jim’
UNION ALL
SELECT v.MenschID, s.Mutter
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
UNION ALL
SELECT v.MenschID, s.Vater
FROM Vorfahren v, Stammbaum s
WHERE v.Vorfahr = s.MenschID
)
SELECT *
FROM Vorfahren
WHERE Vorfahr IS NOT NULL
ORDER BY MenschID $
WITH Nachkommen(MenschID, Nachkomme) AS (
SELECT Mutter, MenschID
FROM Stammbaum
UNION ALL
SELECT Vater, MenschID
FROM Stammbaum
UNION ALL
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Mutter
UNION ALL
SELECT v.MenschID, s.MenschID
FROM Nachkommen v, Stammbaum s
WHERE v.Nachkomme = s.Vater
)
SELECT *
FROM Nachkommen
WHERE MenschID IN (SELECT MenschID
FROM Stammbaum
WHERE Name = ’Bob’)
ORDER BY MenschID $
3
Herunterladen