PL/pgSQL
https://www.postgresql.org/docs/current/static/plpgsql.html
Prozedurale DB-Sprache für PostgreSQL
Funktionen
CREATE [OR REPLACE] FUNCTION name (arg argtype)
[RETURNS rettype | RETURNS TABLE (colname coltype)] AS $$
...
$$ LANGUAGE plpgsql;
Funktionen
SELECT sum(2,3);
CREATE FUNCTION sum (integer, integer) RETURNS integer AS $$
BEGIN
$1 + $2
END
$$ LANGUAGE plpgsql;
CREATE FUNCTION sum (IN a integer, IN b integer, OUT c integer) AS $$
BEGIN
c = a + b
END
$$ LANGUAGE plpgsql;
Eingabe
IN, OUT, INOUT, VARIADIC
(zugriff mit
$i
möglich)
Ausgabe
RETURN NEXT exp;
concatinated tupel an Ergebnis - oder auch
void
Variablen Typen
name [CONSTANT] type [NOT NULL] [{DEFAULT | =} expression];
Typ
matrknr integer;
input1 ALIAS FOR $1;
rang CONSTANT varchar(2) = 'C4';
Copying Types von anderen Variable oder Tabellenspalte
matrnr Studenten.MatrNr%TYPE;
-- MatrNr Studenten.Matrnr%TYPE; ist schlechter Stil
aktuell vorl%TYPE;
Row Types von Tabelle (nicht veränderbar)
student Studenten%ROWTYPE;
Record Types von Tabelle (veränderbar, wiederverwendbar)
ergebnis RECORD;
SELECT * INTO ergebnis FROM Studenten;
SELECT * INTO ergebnis FROM Professoren;
Befehle
Befehl ohne Ergebnis
PERFORM statement
INSERT, UPDATE, ...
brauchen kein prefix beim Aufrufen
Andere Befehle zB
SELECT
brauchen
PERFORM
davor.
Befehle mit einer Zeile als Ergebnis
SELECT expr INTO [STRICT] target FROM ...;
INSERT expr RETURNING expr INTO [STRICT] target;
INTO
lädt einzeiliges Ergebnis in Typ
RECORD
oder
ROWTYPE
.
Mit
STRICT
muss genau eine Zeile zurückgeliefert werden (ansonsten wird die erste gewählt).
String parsen und executen
EXECUTE cmdString [INTO [STRICT] var] [USING expr];
EXECUTE format('SELECT count(*) FROM %I WHERE Sem > $1', tabname) INTO c;
Kontrollstrukturen
BLOCK
[<<label>>] -- name für Block
[DECLARE declarations] -- lokale Variablen
BEGIN
statements
[EXCEPTION exceptionhandling]
END [label];
IF THEN ELSE
IF expr THEN
statements
[ELSIF expr THEN
statements]
[ELSE
statements]
END IF;
CASE
CASE note
WHEN 1,2,3,4 THEN txt = 'Positiv';
WHEN 5 THEN txt = 'Negativ';
END CASE;
CASE
WHEN note = 1,2,3,4 THEN txt = 'Positiv';
WHEN note = 5 THEN txt = 'Negativ';
END CASE;
WHILE LOOP, FOR LOOP
[<<label>>]
WHILE expr LOOP
statements
END LOOP [label];
[<<label>>]
FOR expr IN [REVERSE] expressions [BY expr] LOOP
statements
END LOOP [label];
verlassen oder skippen
EXIT/CONTINUE [label] [WHEN expr];
FOR EACH
[<<label>>]
FOR target IN query LOOP
statements
END LOOP [label];
FOR s IN SELECT * FROM Studenten LOOP
INSERT INTO hoeren VALUES (s.MatrNr, 184686)
END
CREATE OR REPLACE FUNCTION suche(matrnr numeric(10)) RETURNS void AS $$
DECLARE
name varchar(30);
semester numeric(2);
BEGIN
SELECT s.name, s.semester INTO name, semester
FROM students s WHERE s.matrnr = matrnr;
IF (name IS NULL) THEN
RAISE NOTICE 'nothing found';
ELSE
RAISE NOTICE 'Name: %, Semester: %', name, semester;
END IF;
END
$$
Exception Handling
Statements vor rollback ausgeführt - Zugriff auf lokalen Variablen möglich
BEGIN
statements
EXCEPTION
WHEN conds THEN statements
WHEN conds THEN statements ...
END;
-- Note auf 1 setzen oder neu erstellen
LOOP
-- updaten
UPDATE pruefen SET Note=1 WHERE MatrNr=mn AND VorlNr=vn AND PersNr=pn;
RETURN WHEN FOUND;
-- neues tupel erstellen
BEGIN
INSERT INTO pruefen VALUES (mn, vn, pn, 1);
RETURN;
EXCEPTION WHEN uniquie_violation THEN -- hier leer lassen
END;END LOOP;
Cursors
name refcursor;
name [[NO] SCROLL] CURSOR [(args)] FOR query;
FOR query
,
SCROLL
und
NO SCROLL
wenn nicht gebunden
-- nur deklarieren
curs1 refcursor;-- für Tabelle
curs2 CURSOR FOR SELECT * FROM Studenten;-- für Query
curs3 CURSOR (mn integer) FOR
SELECT *
FROM Studenten
WHERE MatrNr=mn;
OPEN curs1 FOR SELECT * FROM Professoren;
OPEN curs2;
OPEN curs3(42);
Befehle
OPEN
öffnen vor verwendung
FETCH
liest nächste Zeile, sonst
NULL
FOUND
sagt quasi
hasNext()
MOVE
erlaubt bewegung ohne zu lesen
CLOSE
schließen nach Verwendung
Fetchen
FETCH [direction {FROM | IN}] cursor INTO target;
FETCH curs1 INTO rowvar;
FETCH LAST FROM curs2 INTO stud;
FETCH PRIOR FROM curs2 INTO stud;
FETCH RELATIVE 2 FROM curs2 INTO stud;
FETCH curs3 INTO mn, name, sem;
Insert/Delete
Vor der Abfrage bei cursor
FOR UPDATE
dazuschreiben.
UPDATE table SET ... WHERE CURRENT OF cursor;
DELETE FROM table ... WHERE CURRENT OF cursor;
Vorlesungen(VorlNr, SWS):
INSERT INTO Vorlesungen VALUES (26120, 3), (27550, 4):CREATE FUNCTION reassign() RETURNS void AS $$
DECLARE
c CURSOR FOR SELECT * FROM Vorlesungen FOR UPDATE;
c2 refcursor;
count integer = 0;
row RECORD;
BEGIN
-- ausgeben und VorlNr mit Count ändern
FOR r IN c LOOP
RAISE NOTICE '(%, %)', r.VorlNr, r.SWS;
UPDATE Vorlesungen SET VorlNR=count WHERE CURRENT OF c
count = count+1;
END LOOP;
-- erneut ausgeben
OPEN c2 FOR SELECT * FROM Vorlesungen;
LOOP
FETCH c2 INTO row;
RAISE NOTICE '(%, %)', row.VorlNr, row.SWS;
EXIT WHEN NOT FOUND;
END LOOP;END;
$$ LANGUAGE plpsql;
Ausgabe:
(26120, 3)
,
(27550, 4)
,
(0, 3)
,
(1, 4)