Database Creates -- Adresse (aid, plz, ort, strasse, nummer) CREATE TABLE Adresse ( aid SERIAL PRIMARY KEY, plz SMALLINT NOT NULL, ort VARCHAR(40) NOT NULL, strasse VARCHAR(70) NOT NULL, nummer VARCHAR(20) NOT NULL, UNIQUE (plz,ort,strasse,nummer) -- Standort(sid, bezeichnung, adresse:adresse.aid, leiter:angestellter.svnr) CREATE TABLE Standort ( sid SERIAL PRIMARY KEY, bezeichnung TEXT NOT NULL, adresse INTEGER REFERENCES Adresse(aid), leiter NUMERIC(12) --REFERENCES Angestellter(svnr) -- Angestellter(svnr, nname, vname, gebdat, gehalt, adresse:adresse.aid, arbeitet:standort.sid) CREATE TABLE Angestellter ( svnr NUMERIC(12) PRIMARY KEY, nname VARCHAR(20) NOT NULL, vname VARCHAR(20) NOT NULL, gebdat DATE NOT NULL, gehalt NUMERIC(7,2) NOT NULL, adresse INTEGER REFERENCES Adresse(aid), arbeitet INTEGER REFERENCES Standort(sid) DEFERRABLE INITIALLY DEFERRED ALTER TABLE Standort ADD CONSTRAINT standort_leiter FOREIGN KEY (leiter) REFERENCES Angestellter(svnr) DEFERRABLE INITIALLY DEFERRED; -- Manager(svnr:Angestellter.svnr, bonus) CREATE TABLE Manager ( svnr NUMERIC(12) PRIMARY KEY REFERENCES Angestellter, bonus NUMERIC(7,2) NOT NULL -- Kunde(knr, nname, vname, adresse:adresse.aid) CREATE TABLE Kunde ( knr NUMERIC(7) PRIMARY KEY, nname VARCHAR(20) NOT NULL, vname VARCHAR(20) NOT NULL, adresse INTEGER REFERENCES Adresse(aid) -- Artikel(ean, bezeichnung, typ CREATE TABLE Artikel ( ean NUMERIC(13) PRIMARY KEY, bezeichnung TEXT NOT NULL, typ TEXT NOT NULL
-- serie(vorgaenger:artikel.ean, nachfolger:artikel.ean) CREATE TABLE serie ( vorgaenger NUMERIC(13) REFERENCES Artikel(ean), nachfolger NUMERIC(13) REFERENCES Artikel(ean), PRIMARY KEY (vorgaenger, nachfolger) -- Land(code, name, kontinent) CREATE TABLE Land ( code CHAR(3) PRIMARY KEY, -- ISO 3166-1 name VARCHAR(50) NOT NULL, kontinent VARCHAR(15) NOT NULL, CHECK (kontinent IN ('Afrika', 'Asien', 'Australien', 'Europa', 'Nordamerika', 'Südamerika')) -- Preis(datum, artikel:artikel.ean, land:land.code, wert, steuer) CREATE TABLE Preis ( datum DATE, artikel NUMERIC(13) REFERENCES Artikel(ean), land CHAR(3) REFERENCES Land(code), wert NUMERIC(7,2) NOT NULL, steuer NUMERIC(3) NOT NULL, -- prozent CHECK (steuer BETWEEN 0 AND 100), PRIMARY KEY (datum, artikel, land) -- Bestellung(bid, zeitpunkt, bezahlt, datum, knr:kunde.knr) CREATE TABLE Bestellung ( bid NUMERIC(10) PRIMARY KEY, zeitpunkt TIMESTAMP NOT NULL, bezahlt VARCHAR(4) NOT NULL, datum DATE NOT NULL, knr NUMERIC(7) REFERENCES Kunde, CHECK (bezahlt IN ('JA', 'NEIN')) -- umfasst(bid:bestellung.bid, artikel:preis.artikel, land:preis.land, datum:preis.datum, anzahl) CREATE TABLE umfasst ( bid NUMERIC(10) REFERENCES Bestellung, datum DATE, artikel NUMERIC(13), land CHAR(3), anzahl SMALLINT NOT NULL, PRIMARY KEY (bid, datum, artikel, land), FOREIGN KEY (datum, artikel, land) REFERENCES Preis
-- sequences CREATE SEQUENCE kunde_knr_seq START 1000001 INCREMENT 10 MINVALUE 1000001 NO MAXVALUE; CREATE SEQUENCE bestellung_bid_seq START 1000000001 INCREMENT 10 MINVALUE 1000000001 NO MAXVALUE; -- extensions for task 3 ALTER TABLE Kunde ADD COLUMN tpunkte integer NOT NULL DEFAULT 0; CREATE TABLE lagerstand ( standort INT REFERENCES Standort(sid), artikel NUMERIC(13) REFERENCES Artikel(ean), anzahl INT NOT NULL, PRIMARY KEY (standort, artikel) Deletes DROP TABLE IF EXISTS Adresse CASCADE; DROP TABLE IF EXISTS Standort CASCADE; DROP TABLE IF EXISTS Angestellter CASCADE; DROP TABLE IF EXISTS Manager CASCADE; DROP TABLE IF EXISTS Kunde CASCADE; DROP TABLE IF EXISTS Artikel CASCADE; DROP TABLE IF EXISTS serie CASCADE; DROP TABLE IF EXISTS Land CASCADE; DROP TABLE IF EXISTS Preis CASCADE; DROP TABLE IF EXISTS Bestellung CASCADE; DROP TABLE IF EXISTS umfasst CASCADE; DROP TABLE IF EXISTS lagerstand CASCADE; DROP SEQUENCE IF EXISTS kunde_knr_seq; DROP SEQUENCE IF EXISTS bestellung_bid_seq; DROP FUNCTION IF EXISTS t_before_bestellen( DROP FUNCTION IF EXISTS f_bestellwert(numeric DROP FUNCTION IF EXISTS f_treuepunkte(numeric DROP FUNCTION IF EXISTS f_treuepunkte_eintragen(numeric DROP FUNCTION IF EXISTS f_alte_bestellungen_entfernen(
SQL Teil 1.1. (Standorte): Geben Sie alle Standorte ("sid" und "bezeichnung") zusammen mit ihren Angestellten ("svnr, "vname" und "nname") aus. Vermerken Sie in einer zusätzlichen Spalte, ob es sich bei dem Angestellten um den Leiter des Standortes handelt. Benennen Sie diese neue Spalte mit "position". Sortieren Sie die Ausgabe nach dem Standort und, falls dieser gleich ist, nach dem Namen des Angestellten. SELECT sid, bezeichnung, svnr, vname, nname (CASE WHEN leiter=svnr THEN 'Leiter' END) AS position FROM Standort, Angestellter WHERE sid=arbeitet; 1.2. (bester Kunde): Geben Sie den Kunden aus, deren Bestellungen in Summe am teuersten gekommen sind. Beachten Sie dabei, dass Sie nur Bestellungen berückstichtigen, die bereits bezahlt wurde. Die Abfrage soll neben der Kundennummer ("knr") auch den Namen des Kunden ("vname", "nname") sowie die Gesamtsumme aller bisher bestellten und bezahlten Artikel ("gesamtbestellsumme") zurückliefern. SELECT knr, vname, nname, round(total, 2) gesamtbestellwert FROM ( SELECT knr, sum(wert*(1+steuer/100)*anzahl) total FROM umfasst NATURAL JOIN Preis JOIN Bestellung USING (bid) WHERE bezahlt='ja' GROUP BY knr ) tempa JOIN Kunde USING (knr) WHERE total >= ( SELECT max(total) FROM ( SELECT sum(wert*(1+steuer/100)*anzahl) total FROM umfasst NATURAL JOIN Preis JOIN Bestellung USING (bid) WHERE bezahlt='ja' GROUP BY knr ) tempb ) ; 1.3. (Standort Ranking) Geben Sie für jeden Standort ("sid" und "bezeichnung") die Anzahl der Mitarbeiter ("mitarbeiter"), die dort arbeiten aus, und ordnen Sie die Liste absteigend nach der Anzahl der Mitarbeiter. Sollten zwei Standorte gleich viele Mitarbeiter beschäftigen, so soriteren Sie die Stanodrte alphabetisch nach ihrere Bezeichnung. SELECT sid, bezeichnung, mitarbeiter FROM ( SELECT arbeitet, count(*) AS mitarbeiter FROM Angestellter GROUP BY arbeitet ) AS anz JOIN Standort ON (sid=arbeitet) ORDER BY mitarbeiter DESC, bezeichnung ASC;
2. (Rekursion) Wählen Sie per Hand einen Artikel aus dem Sortiment aus, der Teil einer Serie ist. Schreiben Sie eine Query, die diesen Artikel ("ean") ausgibt sowie rekursiv alle Nachfolger in dieser Serie. WITH RECURSIVE serrec(vorgaenger, nachfolger) AS ( SELECT vorgaenger, nachfolger FROM serie WHERE vorgaenger='2801234123452' UNION ALL SELECT s.vorgaenger, s.nachfolger FROM serrec sr, serie s WHERE s.vorgaenger = sr.nachfolger ) SELECT * FROM serrec; 3.1. Wählen Sie per Hand einen Kunden aus und geben Sie alle Artikel ("ean", "bezeichnung") aus, die dieser Kunde bisher noch nicht bestellt hat. SELECT ean, bezeichnung FROM Artikel WHERE ean NOT IN ( SELECT DISTINCT artikel FROM bestellung JOIN umfasst USING (bid) WHERE knr=1000031 3.2. Schreiben Sie eine Query, die alle Kunden ("knr", "vname", "nname") zurückgibt, die alle erfassten Artikel mindestens einmal bestellt haben. SELECT knr, vname, nname FROM ( SELECT DISTINCT artikel, knr FROM bestellung JOIN umfasst USING(bid) ORDER BY knr ) tmp JOIN Kunde USING (knr) GROUP BY knr, vname, nname HAVING count(artikel) = (SELECT count(*) FROM Artikel 4. Schreiben Sie Befehle zum Erzeugen und Löschen einer View ("tagespreise_view"), welche zu einem Artikel alle Tagespreise und in welchen Ländern diese gelten, selektiert. Die View soll folgende Informationen enthalten: ean, bezeichnung, typ, datum, preis, steuer, code, name, kontinent. CREATE VIEW tagespreise_view AS SELECT Artikel.*, datum, wert, steuer, code, name, kontinent FROM Preis JOIN Artikel ON ean=artikel JOIN Land on code=land; DROP VIEW tagespreise_view;
PL/pgSQL Teil 5. Trigger Schreiben Sie einen BEFORE INSERT ON Trigger ("t_before_bestellen"), welcher vor einem Insert in die Tabelle "bestellen" überprüft, ob überhaupt genug Stück eines Artikels lagernd sind. Ein Datenbankeintrag soll nur erfolgen, falls in Summe an allen Standorten mindestens so viele Stück des Artikels lagernd sind wie der Kunde bestellt. Sollte der gewünschte Artikel an gar keinem Standort verfügbar sein, so soll eine Exception "artikel_ausverkauft" geworfen werden. Gibt es zu wenig Stück des Artikels, so soll die Exception "zu_wenig_stueck" geworfen werden. Verzichten Sie auf eine Fehlerbehandlung innerhalb des Triggers (da ansonsten ein fehlerhaftes INSERT-Kommand als erfolgreich angesehen würde). CREATE OR REPLACE FUNCTION t_before_bestellen() RETURNS TRIGGER AS $$ DECLARE stand INT; BEGIN SELECT sum(anzahl) INTO stand FROM Lagerstand WHERE artikel=new.artikel; IF stand = 0 THEN RAISE EXCEPTION 'artikel_ausverkauft'; ELSEIF stand IS NULL THEN RAISE EXCEPTION 'artikel_ausverkauft'; ELSEIF stand < NEW.anzahl THEN RAISE EXCEPTION 'zu_wenig_stueck'; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER t_before_bestellen BEFORE INSERT ON umfasst FOR EACH ROW EXECUTE PROCEDURE t_before_bestellen( 6. Funktionen 6.1 f_bestellwert Schreiben Sie eine Funktion ("f_bestellwert"), die für eine Bestellung den Gesamtpreis aller in dieser Bestellung zusammengefassten Waren berechnet. Die Funktion erhält als Eingabeparameter "bestellnr" ("bid" der Bestellung). Beachten Sie, dass von jedem Artikel mehrere Stück pro Bestellung bestellt werden können ("anzahl" von bestellen). CREATE OR REPLACE FUNCTION f_bestellwert(bestellnr NUMERIC(10)) RETURNS NUMERIC(7,2) AS $$ DECLARE total NUMERIC(7,2 BEGIN total = (SELECT sum(wert*(1+steuer/100)*anzahl) FROM umfasst NATURAL JOIN Preis WHERE bid = bestellnr RETURN total; EXCEPTION WHEN OTHERS THEN RAISE NOTICE '%, %', SQLERRM, SQLSTATE; RETURN -1; END; $$ LANGUAGE plpgsql;
6.2 f_treuepunkte Schreiben Sie eine Funktion ("f_treuepunkte"), die für eine übergebene Bestellung die, dem Kunden zustehenden Treuepunkte berechnet. Der Kunde erhält Pro 10,- Bestellwert einen Treuepunkt, d.h. für Bestellungen mit einem Wert ab 10,- einen Punkt, ab 20,- zwei Punkte usw. Hat ein Kunde bereits mehr als 10 bezahlte Bestellungen mit einem Wert jeweils über 50,- Euro getätigt, so erhält der Kunde dafür 5 Extrapunkte. Verwenden Sie dabei die im vorigen Punkt erstelle Funktion "f_bestellwert". Werfen Sie eine Expcetion "bestellwert_zu_gering", sollte der Gesamtwert der betrachteten Bestellung kleiner als 10,- sein. CREATE OR REPLACE FUNCTION f_treuepunkte(bestellnr NUMERIC(10)) RETURNS INTEGER AS $$ DECLARE trpnkt INTEGER; bstwrt NUMERIC(7,2 bstcnt INTEGER; bst Bestellung%ROWTYPE; kdnr Kunde.knr%TYPE; BEGIN bstwrt = f_bestellwert(bestellnr IF bstwrt < 10 THEN RAISE EXCEPTION 'bestellwert_zu_gering'; trpnkt = round(bstwrt/10 -- kundennummer holen von dem kunden der bestellung SELECT knr INTO kdnr FROM Bestellung WHERE bid=bestellnr; bstcnt = 0; -- bezahlte bestellungen des kunden durchlaufen FOR bst IN (SELECT bid FROM Bestellung WHERE knr=kdnr AND Bezahlt='JA') LOOP IF bstcnt > 10 THEN EXIT; IF f_bestellwert(bst.bid) > 50 THEN bstcnt = bstcnt + 1; END LOOP; IF bstcnt > 10 THEN trpnkt = trpnkt + 5; RETURN trpnkt; EXCEPTION WHEN OTHERS THEN RAISE NOTICE '%, %', SQLERRM, SQLSTATE; RETURN -1; END; $$ LANGUAGE plpgsql; 3. f_treuepunkte_eintragen Schreiben Sie eine Prozedur ("f_treuepunkte_eintragen"), die die für eine Bestellung anfallenden Treuepunkte einem Kunden gutschreibt. Die Prozedur hat als Eingabeparameter bestellnr ("bid" von Bestellungen) und gibt nichts zurück (Rückgabetyp void). Verwenden Sie zur Berechnung der gutzuschreibenden Treuepunkte die im vorigen Punkt erstellte Funktion "f_treuepunkte". CREATE OR REPLACE FUNCTION f_treuepunkte_eintragen(bestellnr NUMERIC(10)) RETURNS VOID AS $$ DECLARE trpnkt Kunde.tpunkte%TYPE; kdnr Kunde.knr%TYPE;
BEGIN trpnkt = f_treuepunkte(bestellnr IF trpnkt = -1 THEN RAISE EXCEPTION 'treuepunkte konnten nicht ermittelt werden'; ELSEIF trpnkt IS NULL THEN RAISE EXCEPTION 'bestellung % konnte nicht gefunden werden', bestellnr; ELSE SELECT knr INTO kdnr FROM Bestellung WHERE bid=bestellnr; IF kdnr IS NULL THEN RAISE EXCEPTION 'kunde % konnte nicht gefunden werden', kdnr; ELSE -- input okay, jetzt können wir das updaten! UPDATE Kunde SET tpunkte = tpunkte + trpnkt WHERE knr=kdnr; EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION '%, %', SQLERRM, SQLSTATE; END; $$ LANGUAGE plpgsql; 6.4 f_alte_bestellungen_entfernen Schreiben Sie eine Prozedur ("f_alte_bestellungen_entfernen), die alle Bestellungen aus der Datenbank entfernt, für die nach mehr als 14 Tagen immer noch keine Bezahlt eingegangen ist. Bedenken Sie, dass sie auch in der Tabelle "bestellen" alle Datensätze löschen müssen, die zu den betroffenen Bestellungen gehören. Die Prozedur soll am Ende einen Hinweis ausgeben, wie viele Bestellungen dabei entfernt wurden. CREATE OR REPLACE FUNCTION f_alte_bestellungen_entfernen() RETURNS VOID AS $$ DECLARE todel Bestellung.bid%TYPE; deled INTEGER = 0; BEGIN FOR todel IN SELECT bid FROM Bestellung WHERE bezahlt = 'NEIN' AND (age(zeitpunkt)) > '14 days' LOOP DELETE FROM umfasst WHERE bid=todel; DELETE FROM Bestellung WHERE bid=todel; deled = deled + 1; END LOOP; -- und alles nur fuer grammatikalisch richtigen output! IF deled = 0 THEN RAISE NOTICE 'Keine Bestellungen geloescht'; ELSEIF deled = 1 THEN RAISE NOTICE 'Eine Bestellung geloescht'; ELSE RAISE NOTICE '% Bestellungen geloescht', deled; EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION '%, %', SQLERRM, SQLSTATE; END; $$ LANGUAGE plpgsql;
Java Teil 7.1 dbconnect() Stellt eine JDBC-Verbindung zur Datenbank her und stellt AUTOCOMMIT aus. public void dbconnect() { String driver = "jdbc:postgresql"; String host = "bordo.dbai.tuwien.ac.at"; String port = "5432"; String database = "database"; String username = "username"; String password = "password"; String dbconnectionstring = driver + "://" + host + ":" + port + "/" + database; Class.forName("org.postgresql.Driver" conn = DriverManager.getConnection(dbConnectionString, username, password conn.setautocommit(false catch (Exception e) { System.out.println(e.toString() 7.2 dboutputkunden() Gibt alle Kunden (Kundennummer, Vor- und Nachname, Treuepunkte), die Anzahl aller bisher abgewickelten Bestellungen, den Gesamtwert aller Bestellungen sowie den durchschnittlichen Wert der Bestellungen aus. Berücksichtigen Sie dabei ausschließlich Bestellungen, für die bereits die Bezahltung erfolgt ist. Vergessen Sie nicht auf jene Kunden, für die noch keine derartige Bestellung erfasst ist! Verwenden Sie dabei die Funktion "f_bestellwert", die Sie in Punkt 6a erstellt haben. Die Ausgabe soll als Comma-Separated List erfolgen. public void dboutputkunden() { Statement stmt = null; ResultSet rs = null; stmt = conn.createstatement( rs = stmt.executequery("select k.knr, " + "k.vname, " + "k.nname, " + "k.tpunkte, " + "COUNT(b.bid) AS anz, " + "SUM(f_bestellwert(b.bid)) AS gesamtwert, " + "round(avg(f_bestellwert(b.bid)), 2) AS durchschn " + "FROM Kunden k " + "LEFT OUTER JOIN (Bestellungen b JOIN Bezahlung bez ON (b.bid = bez.bestellung)) ON (k.knr = b.kunde) " + "GROUP BY k.knr, k.vname, k.nname, k.tpunkte " + "ORDER BY gesamtwert DESC, k.knr" while (rs.next()) { System.out.print(rs.getString("knr") + ", " System.out.print(rs.getString("vname") + ", " System.out.print(rs.getString("nname") + ", " System.out.print(rs.getString("tpunkte") + ", " System.out.print(rs.getString("anz") + ", " System.out.print(rs.getString("gesamtwert") + ", " System.out.println(rs.getString("durchschn") rs.close( stmt.close( catch (Exception e) { System.out.println(e.toString()
if (rs!= null) rs.close( if (stmt!= null) stmt.close( catch (SQLException ex) { ex.printstacktrace( 7.3 edittreuepunkte() Soll die Funktionalität der PL/pgSQL-Prozedur aus 6.3 bereitstellen. public void edittreuepunkte(int bestellnr) { Statement stmt = null; CallableStatement cstmttreuepunkte = null; stmt = conn.createstatement( /* Kunden zur Bestellung herausfinden. */ ResultSet rs = stmt.executequery( "SELECT kunde "+ "FROM Bestellungen " + "WHERE bid = '" + bestellnr + "'" rs.next( int kunde = rs.getint("kunde" /* Zugehörige Treuepunkte berechnen. */ cstmttreuepunkte = conn.preparecall("{? = call f_treuepunkte(?) " cstmttreuepunkte.registeroutparameter(1, Types.INTEGER cstmttreuepunkte.setint(2, bestellnr cstmttreuepunkte.executequery( int treuepunkte = cstmttreuepunkte.getint(1 rs = stmt.executequery("select f_treuepunkte('" + bestellnr + "') AS tp" rs.next( int treuepunkte = rs.getint("tp" /* Treuepunkte eintragen */ stmt.executeupdate( "UPDATE Kunden "+ "SET tpunkte = tpunkte + " + treuepunkte + " " + "WHERE knr = '" + kunde + "'" conn.commit( /* Statements und ResultSets schließen */ cstmttreuepunkte.close( rs.close( stmt.close( catch (SQLException e) { e.printstacktrace( if (conn!= null) conn.rollback( if (stmt!= null) stmt.close( if (cstmttreuepunkte!= null) cstmttreuepunkte.close( catch (SQLException ex) { ex.printstacktrace( 7.4 resettreuepunkte() Setzt alle Treuepunkte der Tabelle "Kunden" auf 0.
public void resettreuepunkte() { Statement stmt = null; int updatedrows = 0; stmt = conn.createstatement( updatedrows = stmt.executeupdate( "UPDATE Kunden " + "SET tpunkte = 0 " + "WHERE tpunkte!= 0" conn.commit( stmt.close( catch (Exception e) { System.out.println(e.toString() if (conn!= null) conn.rollback( if (stmt!= null) stmt.close( catch (SQLException ex) { ex.printstacktrace( finally { System.out.println("Anzahl aktualisierter Tupel: " + updatedrows 7.5 dbdisconnect() public void dbdisconnect() { conn.close( catch (SQLException e) { //System.out.println(e.toString() while (e!= null) { System.out.print("Code: " + e.geterrorcode() System.out.print("Message: " + e.getmessage() System.out.print("State: " + e.getsqlstate() e = e.getnextexception(