Datenbanken SoSe2016

Blatt 10

Hier eine alte Vorlesung zum Thema "Relationale Algebra": Vorlesung5.pdf


Blatt 8, Aufgabe 3

Beispiellösung, Variante 1 (die aktuelle Zeile der Cursordaten wird über zwei Variablen "artikelnummer" und "bestand" abgebildet):
create or replace function nachbestellung_zusammenstellen() RETURNS int as $$
  declare curLager CURSOR FOR select a_nr, bestand from lagerbestand;
  declare artikelnummer varchar;
  declare bestand INT;
  declare preis numeric;

BEGIN
 delete from nachbestellen;
 OPEN curLager;
 LOOP  
   FETCH curLager into artikelnummer, bestand;
   EXIT when not found;
   RAISE NOTICE 'Artikel: % => Lagerbestand: %', artikelnummer, bestand;
   --neuesten Preis laden.
   preis := (select angebot.preis from angebot where angebot.a_nr = artikelnummer order by angebot.datum desc limit 1);
   RAISE NOTICE 'Preis: %', preis;
   if (not preis is null) then
     if (preis > 6) then
       if (bestand < 5) then
         RAISE NOTICE 'Nachbestellung (Preis > 6 Euro): %', (5 - bestand);
         insert into nachbestellen (a_nr, nachzubestellen) values (artikelnummer, 5 - bestand);
       end if;
     else
       if (bestand < 10) then
         RAISE NOTICE 'Nachbestellung (Preis <= 6 Euro): %', (10 - bestand);
         insert into nachbestellen (a_nr, nachzubestellen) values (artikelnummer, 10 - bestand);
       end if;     
     end if;
   end if;
 END LOOP;

 CLOSE curLager;
 RETURN 0;
end $$
LANGUAGE PLPGSQL;

Beispiellösung, Variante 2 (die aktuelle Zeile der Cursordaten wird über eine Variable vom Typ "RECORD" abgebildet):
create or replace function nachbestellung_zusammenstellen() RETURNS int as $$
  declare curLager CURSOR FOR select a_nr, bestand from lagerbestand;
  declare lagerzeile RECORD;
  declare preis numeric;

BEGIN
 delete from nachbestellen;
 OPEN curLager;
 LOOP  
   FETCH curLager into lagerzeile;
   EXIT when not found;
   RAISE NOTICE 'Artikel: % => Lagerbestand: %', lagerzeile.a_nr, lagerzeile.bestand;
   --neuesten Preis laden.
   preis := (select angebot.preis from angebot where angebot.a_nr = lagerzeile.a_nr order by angebot.datum desc limit 1);
   RAISE NOTICE 'Preis: %', preis;
   if (not preis is null) then
     if (preis > 6) then
       if (lagerzeile.bestand &lgt; 5) then
         RAISE NOTICE 'Nachbestellung (Preis > 6 Euro): %', (5 - lagerzeile.bestand);
         insert into nachbestellen (a_nr, nachzubestellen) values (lagerzeile.a_nr, 5 - lagerzeile.bestand);
       end if;
     else
       if (lagerzeile.bestand < 10) then
         RAISE NOTICE 'Nachbestellung (Preis <= 6 Euro): %', (10 - lagerzeile.bestand);
         insert into nachbestellen (a_nr, nachzubestellen) values (lagerzeile.a_nr, 10 - lagerzeile.bestand);
       end if;     
     end if;
   end if;
 END LOOP;

 CLOSE curLager;
 RETURN 0;
end $$
LANGUAGE PLPGSQL;


Zuweisungsoperator

Es ist egal, ob man ":=" oder "=" verwendet - die beiden sind identisch: https://www.postgresql.org/docs/9.4/static/plpgsql-statements.html

Es gibt aber einige wenige Spezialfälle, wo es doch wichtig ist, welchen Operator man verwendet. Einer ist die Verwendung von benannten Parametern bei Funktionsaufrufen: hier muss ":=" verwendet werden: http://stackoverflow.com/a/22001209

Blatt 7

Beispiellösung:
CREATE TABLE lagerbestand
(
  a_nr VARCHAR (13) NOT NULL,
  bestand INT NOT NULL,
  constraint lagerbestand_PK PRIMARY KEY (a_nr),
  constraint lagerbestand_FKa FOREIGN KEY (a_nr) REFERENCES medienartikel (a_nr) on delete cascade
);

--Aufgabe 7.2: Funktion verhindert das Überbuchen des Lagers bei INSERT oder UPDATE: Bestellmenge wird auf verfügbare Lagermenge reduziert.
CREATE OR REPLACE FUNCTION kontrolle() RETURNS TRIGGER AS $$
DECLARE
  lb INT ;
BEGIN
  SELECT bestand INTO lb FROM lagerbestand WHERE lagerbestand.a_nr = NEW.a_nr;
  RAISE NOTICE 'kontrolle: Lagerbestand alt = %, Bestellmenge: %', lb, NEW.Anzahl;
  if tg_op = 'INSERT' then 
    NEW.anzahl :=
      CASE WHEN (NEW.anzahl <= lb) THEN
        NEW.anzahl
      ELSE 
        lb
      END;
  else 
    lb:= lb + OLD.anzahl; 
    NEW.anzahl :=
      CASE WHEN (NEW.anzahl <= lb) THEN
        NEW.anzahl
      ELSE 
        lb 
      END;  
  END if;
  RAISE NOTICE 'kontrolle: neue Bestellmenge: %', NEW.Anzahl;
  RETURN NEW;
END$$
LANGUAGE PLPGSQL;

CREATE TRIGGER trg_bestellungsbegrenzer
  BEFORE UPDATE OR INSERT ON bestellposition
  FOR EACH ROW EXECUTE PROCEDURE kontrolle();


--Aufgabe 7.2: Funktion passt bei Eintragen/Ändern/Löschen einer Bestellposition den Lagerbestand an.
CREATE OR REPLACE FUNCTION updatelagerbestand() RETURNS TRIGGER AS $$
DECLARE
  lagerbestandOriginal INT ;
  lagerbestandNeu INT;
BEGIN
if (TG_OP = 'INSERT') then
  SELECT bestand INTO lagerbestandOriginal FROM lagerbestand WHERE lagerbestand.a_nr = NEW.a_nr;
  lagerbestandNeu := lagerbestandOriginal - NEW.anzahl;
  RAISE NOTICE 'INSERT Lagerbestand alt: %, neue Bestellmenge: %, Lagerbestand neu: %', lagerbestandOriginal, NEW.anzahl, lagerbestandNeu;
  UPDATE lagerbestand SET bestand = lagerbestandNeu WHERE lagerbestand.a_nr = NEW.a_nr;
elsif (TG_OP = 'UPDATE')  then
  SELECT bestand INTO lagerbestandOriginal FROM lagerbestand WHERE lagerbestand.a_nr = NEW.a_nr;
  --Man kommt doch beim Update mit einem einzigen Fall aus: auf vorherigen Lagerbestand wird alte Bestellmenge addiert, davon wird die neue Bestellmenge abgezogen.
  lagerbestandNeu := lagerbestandOriginal + OLD.anzahl - NEW.anzahl;
  RAISE NOTICE 'UPDATE Lagerbestand alt: %, vorherige Bestellmenge: %, neue Bestellmenge: %, Lagerbestand neu: %', lagerbestandOriginal, OLD.anzahl, NEW.anzahl, lagerbestandNeu;
  UPDATE lagerbestand SET bestand = lagerbestandNeu WHERE lagerbestand.a_nr = NEW.a_nr;
elsif (TG_OP = 'DELETE') then
   SELECT bestand INTO lagerbestandOriginal FROM lagerbestand WHERE lagerbestand.a_nr = OLD.a_nr;
   lagerbestandNeu := lagerbestandOriginal + OLD.anzahl;
   RAISE NOTICE 'delete Lagerbestand alt: %, gelöschte Bestellmenge: %, Lagerbestand neu: %', lagerbestandOriginal, OLD.anzahl, lagerbestandNeu;
   UPDATE lagerbestand SET bestand = lagerbestandNeu WHERE lagerbestand.a_nr = OLD.a_nr;
end if;

RETURN NEW;
END$$
LANGUAGE PLPGSQL;

CREATE TRIGGER trg_updatelagerbestand
  AFTER INSERT or update or delete ON bestellposition
  FOR EACH ROW EXECUTE PROCEDURE updatelagerbestand();


--*********************************************************************************
--Testfall 1:
DELETE FROM bestellposition WHERE a_nr='0017-V' ;
update lagerbestand set bestand = 50 where a_nr = '0017-V';

--Eine Überbuchung einfügen: wird automatisch auf 50 abgeschnitten.
INSERT INTO bestellposition VALUES ('0017-V', 'BE-0010', '101', '14.90');

select * from bestellposition where a_nr = '0017-V';
select * from lagerbestand where a_nr = '0017-V';

--Buchung löschen:
DELETE FROM bestellposition WHERE a_nr='0017-V' ;

select * from bestellposition where a_nr = '0017-V';
select * from lagerbestand where a_nr = '0017-V';

--*********************************************************************************
--Testfall 2:
DELETE FROM bestellposition WHERE a_nr='0017-V' ;
update lagerbestand set bestand = 50 where a_nr = '0017-V';

--Eine normale Buchung einfügen:
INSERT INTO bestellposition VALUES ('0017-V', 'BE-0010', '10', '14.90');


select * from bestellposition where a_nr = '0017-V';  --10
select * from lagerbestand where a_nr = '0017-V';  --40

--Weitere 10 Artikel buchen:
update bestellposition set anzahl = 20 where a_nr = '0017-V' and be_nr = 'BE-0010';

select * from bestellposition where a_nr = '0017-V';  --20
select * from lagerbestand where a_nr = '0017-V';  --30

--Fünf Artikel zurücknehmen:
update bestellposition set anzahl = 15 where a_nr = '0017-V' and be_nr = 'BE-0010';

select * from bestellposition where a_nr = '0017-V';  --15
select * from lagerbestand where a_nr = '0017-V';  --35

--Und zuviel aufbuchen:
update bestellposition set anzahl = 55 where a_nr = '0017-V' and be_nr = 'BE-0010';

select * from bestellposition where a_nr = '0017-V';  --50
select * from lagerbestand where a_nr = '0017-V';  --0

--Wieder etwas davon wegnehmen:
update bestellposition set anzahl = 45 where a_nr = '0017-V' and be_nr = 'BE-0010';

select * from bestellposition where a_nr = '0017-V';  --45
select * from lagerbestand where a_nr = '0017-V';  --5

--Buchung löschen:
DELETE FROM bestellposition WHERE a_nr='0017-V' ;

select * from bestellposition where a_nr = '0017-V';  --kein treffer
select * from lagerbestand where a_nr = '0017-V'; --50


drop trigger trg_updatelagerbestand on bestellposition;
drop function updatelagerbestand();

drop trigger trg_bestellungsbegrenzer on bestellposition;
drop function kontrolle();


Stand 22.06.2016
Historie:
22.06.2016: Link zu alter Vorlesung zur relationalen Algebra.
15.06.2016: Blatt 8
12.06.2016: ":=" vs "="
08.06.2016: Erstellt