Kapitel 2 Simulation von Allquantoren in SQL: Erinnerung "Welcher Fluss fließt durch alle Bundesländer ?" ∀ x: F ( SELECT FL1.Fluss FROM fluss_durch_land AS FL1 WHERE NOT EXISTS ( SELECT * FROM land AS L WHERE L. Kurzform NOT IN ( SELECT FL2.Land FROM fluss_durch_land AS FL2 WHERE FL1.Fluss = FL2.Fluss ))) ⇔ ¬ ∃ x: ¬ F © 2011 Prof. Dr. Rainer Manthey Informationssysteme 1 Kapitel 2 RA: Division (1) • Für solche Fälle, in denen die Logik ∀ braucht, kennt die RA eine spezielle Algebraoperation, die Division. • Notation: R ÷ S • in unserem Beispiel: Welche Flüsse fließen durch alle Bundesländer ? fluss_durch_land ÷ ρ Land ← Name(πName(land)) • Darstellung mit Division: • Auch die Division ist eigentlich nicht nötig, sondern dient dem „Komfort“. • Darstellung ohne Division: πFluss (R) − πFluss ((πFluss (R) × S) − R) mit R := fluss_durch_land S := ρLand ← Name(πName(land)) • Voraussetzung: Alle Attribute von S sind auch Attribute von R. • Schema der Ergebnisrelation: attr(R) − attr(S) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 2 Kapitel 2 RA: Division (2) • Prinzip der Division am abstrakten Beispiel: R • • A B a a a b b 1 2 3 2 3 S R ÷ S B 1 2 A a Nur 'a' kommt kombiniert mit allen S-Tupeln in R vor ! präzise Definition der Division: R ÷ S := = π attr(R) − attr(S) (R) − π attr(R) − π attr(R) − attr(S) (R) × attr(S) ((π S) − R) Idee dabei: • Nenne die Projektion von R auf alle "Nicht-S-Attribute" R' ! • • • • R'' sei das Produkt von R' und S: R'' enthält alle möglichen Kombinationen aller R'-Tupel mit allen S-Tupeln und hat dasselbe Schema wie R ! Ziehe von R'' wieder R ab, um damit alle R-Tupel zu bestimmen, deren R'-Anteil nicht mit allen S-Tupeln kombiniert ist ! Nenne das Resultat R'''! Projiziere R''' auf die "Nicht-S-Attribute" und ziehe nochmal R' davon ab! Übrig bleiben die Nicht-S-Werte derjenigen R'-Tupel, die mit allen S-Tupeln kombiniert in R vorkommen. © 2011 Prof. Dr. Rainer Manthey Informationssysteme 3 Kapitel 2 RA: Division (3) Anwendung der Definition auf das Beispiel: R A B a a a b b 1 2 3 2 3 S R ÷ S B A 1 2 a R'' R' R' R ÷ S := = π attr(R) − R' − π attr(R) − π attr(R) − attr(S) (R) × attr(S) ((π S) − R) A a b S attr(S) (R) R''' B 1 2 © 2011 Prof. Dr. Rainer Manthey R'' A A B a a b b 1 2 1 2 Informationssysteme a R''' A B b 1 4 Kapitel 2 Division in SQL (1) In diesem Beispiel ist attr(R) − attr(S) = {A}, also wäre in SQL zu formulieren: R ÷ S := = π A (R) (SELECT A FROM R) − π A ((π π A (R) × S) − R) EXCEPT (SELECT A FROM ( (SELECT * FROM (SELECT A FROM R), S) EXCEPT (TABLE R) ) ) Das ist korrekt, aber sehr unhandlich und überhaupt nicht intuitiv! © 2011 Prof. Dr. Rainer Manthey Informationssysteme 5 Kapitel 2 Division in SQL (2) Beide Formulierungen der Division sind aufwändig und komplex, egal ob man den Weg über die RA oder die Logik nimmt! (SELECT A FROM R) EXCEPT (SELECT A FROM ( (SELECT * FROM (SELECT A FROM R), S) EXCEPT (TABLE R) ) ) ( SELECT r.A FROM R AS r WHERE NOT EXISTS ( SELECT * FROM S AS s WHERE NOT EXISTS ( SELECT * FROM R AS r' WHERE r'.B = s.B AND r'.A = r.A) ) ) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 6 Kapitel 2 Existenzbedingungen: Resümee Warum diese ganze "Wirbelei" mit Quantoren in SQL ???? • Nur sehr einfache Anfragen lassen sich ohne die genauere Kenntnis von Existenzbedingungen formulieren. • Sowie das Wörtchen "alle" bzw. "jeder" ins Spiel kommt, wird es schwierig und Quantoren sind unvermeidbar! • Auch "genau ein"-Anfragen haben es in sich, sind aber in der Praxis nicht gerade selten. Ohne gute Grundkenntnisse der Prädikatenlogik (insbesondere der Quantoren) kann man niemals „vernünftig“ SQL anwenden ! © 2011 Prof. Dr. Rainer Manthey Informationssysteme 7 Kapitel 2 Quantorensimulation durch Abzählen: Idee letzte Woche behauptet: Idee: Ohne gute Grundkenntnisse der Prädikatenlogik (insbesondere der Quantoren) kann man niemals „vernünftig“ SQL anwenden ! Ganz so kategorisch stimmt das nicht, denn wenn man in SQL zählen kann, dann lassen sich Quantoren ganz vermeiden! • Statt „Gibt es ein Objekt mit Eigenschaft E?“ kann man fragen „Ist die Anzahl der Objekte mit Eigenschaft E > 0?“. • Statt „Gibt es genau ein Objekt mit Eigenschaft E?“ kann man fragen „Ist die Anzahl der Objekte mit Eigenschaft E = 1?“. • Statt „Haben alle Objekte die Eigenschaft E?“ kann man fragen „Gibt es kein Objekt ohne Eigenschaft E?“, was wiederum bedeutet „Ist die Anzahl der Objekte ohne Eigenschaft E = 0?“. Der Operator zum Abzählen der Zeilen in einer Tabelle (bzw. Anfrageantwort) heißt ja in SQL sinnigerweise COUNT und gehört zur Klasse der Aggregatfunktionen, mit denen wir uns schon kurz beschäftigt hatten – jetzt betrachten wir sie noch mal systematisch. © 2011 Prof. Dr. Rainer Manthey Informationssysteme 8 Kapitel 2 Aggregatfunktionen (Wdhl.) • wichtige Klasse von „built-in“-Funktionen in SQL: COUNT SUM AVG MAX MIN • Aggregatfunktionen Anzahl Summe Durchschnitt (engl.: "average") Maximum Minimum Aggregatfunktionen berechnen einen skalaren Wert aus einer Menge von skalaren Werten („Aggregat“), die aus einer Spalte einer Tabelle stammen: Tabelle Aggregat Funktionswert © 2011 Prof. Dr. Rainer Manthey Informationssysteme 9 Kapitel 2 Aggregatfunktionen (Bsp. 1) Tabelle SELECT COUNT(*) FROM städte_in_D WHERE Einwohner >= 100 Aggregat Funktionswert Wieviele Städte mit mehr als 100.000 Einw. gibt es in D. ? © 2011 Prof. Dr. Rainer Manthey Informationssysteme 10 Aggregatfunktionen (Bsp. 2) Kapitel 2 Tabelle Aggregat Funktionswert Wie groß ist die größte Stadt in NW ? SELECT MAX (Einwohner) FROM städte_in_D WHERE Land = ′NW′ © 2011 Prof. Dr. Rainer Manthey Informationssysteme 11 Kapitel 2 Aggregatfunktionen: Nicht ganz so einfach wie gedacht! SELECT MAX (Einwohner) FROM städte_in_D WHERE Land = ′NW′ Wie groß ist die größte Stadt in NRW ? Wie heißt die größte Stadt in NRW (und wie groß ist sie)? unzulässig ! SELECT Stadt, MAX (Einwohner) FROM städte_in_D WHERE Land = ′NW′ Bei Aggregatanfragen: SELECT-Teil muss garantiert eine eindeutige Antwort liefern (es könnte aber mehrere gleich große größte Städte geben!) Wie heißt die größte Stadt in NRW ? unzulässig ! SELECT Stadt FROM städte_in_D WHERE Land = ′NW′ AND Einwohner = MAX (Einwohner) Aggregatfunktionen dürfen nicht direkt im WHERE-Teil vorkommen, da der WHERE-Teil nur zum Bilden des Aggregats vor der Aggregation dient! © 2011 Prof. Dr. Rainer Manthey Informationssysteme 12 Aggregatfunktionen: Nicht ganz so einfach wie gedacht! (2) Wie heißt die größte Stadt in NRW ? unzulässig ! Kapitel 2 SELECT Stadt FROM städte_in_D WHERE Land = ′NW′ AND Einwohner = MAX (Einwohner) Erst wenn man den Vergleich auf einen verschachtelten inneren Block anwendet, der genau ein eindeutig bestimmtes Resultat liefert, lässt sich diese Anfrage formulieren: Wie heißt die größte Stadt in NRW ? © 2011 Prof. Dr. Rainer Manthey SELECT Stadt FROM städte_in_D WHERE Land = ′NW′ AND Einwohner = (SELECT MAX (Einwohner) FROM städte_in_D WHERE Land=‚NW‘) Informationssysteme 13 Aggregatfunktionen: Nicht ganz so einfach wie gedacht! (3) Kapitel 2 Will man jetzt auch dazu noch die Größe der größten Stadt sehen, ist dies im äußeren Block möglich, da die Aggregierung ganz im inneren Block stattfindet (und dort die Eindeutigkeit der Antwort gewahrt bleibt): Wie heißt die größte Stadt in NRW und wie groß ist sie ? © 2011 Prof. Dr. Rainer Manthey SELECT Stadt, Einwohner FROM städte_in_D WHERE Land = ′NW′ AND Einwohner = (SELECT MAX (Einwohner) FROM städte_in_D WHERE Land=‚NW‘) Informationssysteme 14 Kapitel 2 Aggregatfunktionen mit Gruppierung: Bsp. Tabelle Aggregat(e) Wie heißt die größte Stadt pro Land und wie groß ist sie ? Funktionswert pro Gruppe © 2011 Prof. Dr. Rainer Manthey Informationssysteme 15 Kapitel 2 Aggregatfunktionen mit Gruppierung: Prinzip • oft verwendet im Zusammenhang mit Aggregatfunktionen: erweiterte SELECT-Blöcke mit Unterteilung der Resultattabelle in Gruppen • dazu vorgesehen: GROUP BY- und (ggf.) HAVING-Teile in SELECT-Blöcken • Grundidee: Resultat der Auswertung von SELECT-FROM-WHERE (Tabelle) unterteilt in „Untertabellen“ (Gruppen) mit identischen Werten für bestimmte Gruppierungsspalten (festgelegt im GROUP BY-Teil) • optional: Gruppen, die bestimmte Bedingung nicht erfüllen (HAVING-Teil), werden eliminiert. Im HAVING-Teil dürfen Aggregatfunktionen vorkommen. • Aggregatfunktionen werden auf Gruppen (als Aggregate) angewendet, wenn GROUP BY spezifiziert ist, z.B.: Wie groß ist die größte Stadt pro Land (für Länder mit mehr als zwei Städten) ? © 2011 Prof. Dr. Rainer Manthey SELECT Land, MAX(Einwohner) FROM städte_in_D GROUP BY Land HAVING COUNT(*) > 2 Informationssysteme 16 Kapitel 2 GROUP BY und HAVING am Bsp. SELECT Land, MAX (Einwohner) FROM städte_in_D GROUP BY Land HAVING COUNT(*) > 2 Gruppen als Aggregate eliminierte Gruppe © 2011 Prof. Dr. Rainer Manthey Informationssysteme 17 GROUP BY und HAVING: SELECT nie ohne Gruppierungsattribute! Kapitel 2 SELECT Land, MAX (Einwohner) FROM städte_in_D GROUP BY Land HAVING COUNT(*) > 2 Wenn gruppiert wird, müssen immer die Gruppierungsattribute in der SELECT-Klausel mit vorkommen – sonst versteht man nicht, worauf sich die Aggregatwerte beziehen! © 2011 Prof. Dr. Rainer Manthey Informationssysteme 18 Kapitel 2 Bsp.anfrage mit Gruppierung und zusätzlichem Attribut SELECT FROM Stadt AS Name, Einwohner, Land städte_in_D INNER JOIN Wie heißt die größte Stadt pro Land und wie groß ist sie ? (SELECT MAX(Einwohner) AS MaxEw, Land FROM städte_in_D GROUP BY Land) AS GrS ON (Einwohner = GrS.MaxEw AND Land = GrS.Land) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 19 Kapitel 2 Abarbeitungsreihenfolge bei Aggregatanfragen SELECT FROM WHERE GROUP BY HAVING Abarbeitungsreihenfolge für Aggregatanfragen: 2. Grad Alter Jim John Ken Lisa Tom Eva C4 C3 C4 C4 C2 C3 WHERE 43 33 57 39 32 36 1. © 2011 Prof. Dr. Rainer Manthey COUNT(*) > 1 GROUP BY Name Name P.Grad, AVG( P.alter ) AS Avgalter professoren AS P P.Name <> ‚Ken‘ P.Grad Grad Alter Jim Lisa C4 C4 43 39 John Eva C3 C3 33 36 Tom C2 32 3. 4. AVG Grad Avgalter C3 C4 34.5 41.0 HAVING Informationssysteme 20 Kapitel 2 Redundantes GROUP BY • Auch das GROUP BY-Konstrukt ist eigentlich redundant: SELECT FROM WHERE GROUP BY • Grad, MAX (Alter) professoren Grad > ‚C2‘ Grad Äquivalente Formulierung ohne Verwendung von GROUP BY: SELECT DISTINCT P.Grad, ( SELECT MAX (P1.Alter) FROM professoren AS P1 WHERE P.Grad = P1.Grad ) FROM professoren AS P WHERE P.Grad > ‚C2‘ © 2011 Prof. Dr. Rainer Manthey Informationssysteme 21 Kapitel 2 Redundantes HAVING • Auch der HAVING-Teil ist im Prinzip überflüssig (aber oft komfortabler): Welche Vorlesungen werden von mehr als einem Professor angeboten?“ SELECT FROM GROUP BY HAVING • Titel vorlesungen Titel COUNT(*) > 1 äquivalent ohne GROUP BY und HAVING: SELECT DISTINCT V.Titel FROM vorlesungen V WHERE (SELECT COUNT(*) FROM vorlesungen V1 WHERE V.Titel = V1.Titel ) > 1 © 2011 Prof. Dr. Rainer Manthey Informationssysteme 22 Nullwerte und Aggregatfunktionen • Kapitel 2 im Prinzip: Alle Aggregatfunktionen ignorieren NULL ! Name Alter person Jim Tom 33 NULL SUM (alter): 33 COUNT(alter): 1 AVG (alter): 33 • Ausnahme: COUNT(*) ignoriert NULL-Tupel nicht! • weitere Ergänzung zu Aggregatfunktionen: je zwei Versionen • Duplikate werden mitgezählt: ALL • Duplikate werden ignoriert: DISTINCT • z.B.: COUNT ALL, AVG DISTINCT • Voreinstellung: ALL • Beispiel für Verwendung: SELECT COUNT (DISTINCT Alter), AVG (ALL Alter) FROM .... © 2011 Prof. Dr. Rainer Manthey Informationssysteme 23 Kapitel 2 Sortierung von Antworttabellen • Fragen nach größtem oder kleinstem Element lassen sich auch dadurch (indirekt) beantworten, dass man die jeweilige Antworttabelle vor interaktiver Ausgabe sortieren lässt, z.B.: Position kann nicht in anderen Anfragen verwendet werden, nur „gesehen“! SELECT * FROM städte_in_D ORDER BY Einwohner DESC • Sortierungsrichtung: ASC (engl.: ascending, dt. aufsteigend; Defaultwert) DESC (engl.: descending, dt. absteigend) • Reihenfolge der Spalten wird beim Sortieren berücksichtigt: mehrfache Sortierkriterien • ORDER BY-Klausel steht immer am Ende (noch hinter HAVING) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 24 Kapitel 2 Leere Tabellen • Wie sieht in SQL eine leere Tabelle aus ? • „Leer“ bedeutet in der Mengenlehre „ohne Elemente“. Eine leere Tabelle enthält also kein Tupel. • Nicht zu verwechseln mit einer Tabelle, die ein Tupel enthält, dessen Felder nur aus Nullwerten bestehen – eine solche Tabelle ist nicht (wirklich) leer! • In der Datenblattansicht von Access ist der Unterschied z.B. deutlich sichtbar: stadt_an_fluss Stadt stadt_an_fluss Stadt © 2011 Prof. Dr. Rainer Manthey Fluss Fluss nichtleere Tabelle, die nur ein „NULL-Tupel“ enthält leere Tabelle, die kein Tupel enthält Informationssysteme 25 Kapitel 2 Boolesche Anfragen in SQL • Wie "simuliert" man in SQL Ja/Nein-Anfragen ? z.B.: Gibt es eine Stadt mit mehr als 4 Millionen Einwohnern ? • Mit Tabellenanfragen ist nur indirekt eine Antwort möglich: Eine leere Antworttabelle wird als „Nein“ interpretiert. SELECT Name FROM stadt WHERE Einwohner > 4000 ; • Name Paris London wenn ja: nichtleere Antworttabelle Name wenn nein: leere Tabelle Sinnvoll, aber leider (noch?) nicht im Standard vorgesehen wäre eine direkte Formulierungsmöglichkeit, etwa: CHECK EXISTS (SELECT Name FROM stadt WHERE Einwohner > 4000 ) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 26 Kapitel 2 Zugriffsarten in SQL • abschließend zum Thema "Anfragen" in SQL: Diskussion der beiden Zugriffsarten, die der SQL-Standard vorsieht • prinzipiell: zwei "Betriebsarten" für SQL direct SQL • interaktiv, direkte Ein-/Ausgabe: • SQL-Code eingebettet in Anwendungsprogramme: embedded SQL • bisher in den Übungen (PostgreSQL) kennengelernt: direct SQL-Modus ! • in „direct SQL“: • „embedded SQL“: Verschiedene Formen der „Koexistenz“ von SQL und der jeweiligen Programmiersprache, aus der heraus Abfragen abgesetzt werden, abhängig u.a. von Verbindungsprotokollen (wie etwa ODBC, JDBC) • Hauptproblem dabei: In üblichen Programmiersprachen fehlen geeignete Datentypen zur Aufnahme von Mengen von Tupeln („impedance mismatch“). © 2011 Prof. Dr. Rainer Manthey Beliebige Tabellenausdrücke sind als Anfragen absetzbar. Informationssysteme 27 Kapitel 2 Zugriffsarten (2) • in "embedded SQL": Unterscheidung zwischen zwei Formen des Zugriffs • Wenn SELECT garantiert nur eine einzeilige Tabelle liefert . . . . . . direkte Verwendung als Anfrage möglich. single-row retrieval • Wenn eine mehrzeilige Tabelle als Antwort denkbar ist . . . . . . Zugriff nur in Verbindung mit einem sogenannten Cursor multi-row retrieval • Beispiel für eine „single-row“-Anfrage: SELECT FROM WHERE © 2011 Prof. Dr. Rainer Manthey P.Grad professoren P P.Name = ‚Manthey‘ Informationssysteme (vorausgesetzt ‚Name‘ ist Schlüsselkandidat) 28 Kapitel 2 Zugriffsarten (3) • "Cursor": Zeiger (wörtlich: "Läufer") auf jeweils eine Zeile der zu lesenden Ergebnistabelle Vorwärtsund RückwärtsBewegung möglich • diverse Cursoroperationen verfügbar: DECLARE: Einführen eines Cursors, Namensvereinbarung und Binden an einen Tabellenausdruck OPEN: Öffnen eines vorher eingeführten Cursors und Positionieren vor der ersten Zeile der Ergebnistabelle FETCH NEXT: Vorrücken des Cursors auf nächste Zeile (bzw. hinter letzte Zeile) FETCH PRIOR: Vorrücken des Cursors auf vorige Zeile (bzw. vor erste Zeile) CLOSE: Schliessen eines Cursors © 2011 Prof. Dr. Rainer Manthey Informationssysteme 29 Kapitel 2 Zugriffsarten (4) • Syntax von Cursordeklarationen: DECLARE <cursor-name> [ INSENSITIVE ] [ SCROLL ] CURSOR FOR <cursor-specification> • INSENSITIVE-Option: Separate Kopie der Ergebnistabelle wird erzeugt. (wichtig vor allem bei Änderungen mittels Cursor: Originaltabelle bleibt unverändert) • SCROLL-Option: Alle Formen der FETCH-Operation möglich (weitere Varianten im Date-Buch); fehlt SCROLL, ist nur FETCH NEXT erlaubt ! • Syntax von „cursor specifications“: <table-expression> [ ORDER BY < list of order-items> ] • „order items“: Spaltennamen oder Integers (relative Position von links nach rechts); optional mit ASC (ascending/aufsteigend) oder DESC (descending/ absteigend) verbunden; Defaultwert für Sortierung ist ASC. ⇒ Cursor operieren stets auf sortierten Tabellen ! © 2011 Prof. Dr. Rainer Manthey Informationssysteme 30 Kapitel 2 Änderungsoperationen: Übersicht • In Kapitel 1 schon kurz erwähnt: Auch Änderungsanweisungen gehören zum DMLAnteil einer Datenbanksprache wie SQL • SQL bietet drei Grundoperationen zum Ändern von Daten: • INSERT Einfügen von Zeilen • UPDATE Abändern von Spaltenwerten • DELETE Löschen von Zeilen • Gefahr eines Terminologiekonflikts: • „Update“: im DB-Jargon Bezeichnung für jede Art von Änderungsoperation • UPDATE in SQL: nur für Spaltenmodifikationen reserviert • auch für Änderungsoperationen: Verwendung mit und ohne Cursor möglich • allerdings: Alle Änderungsoperationen sind (im Prinzip) „multi-row“-Operationen. • im folgenden: Zunächst werden nur Änderungen von Basistabellen betrachtet. © 2011 Prof. Dr. Rainer Manthey Informationssysteme 31 Kapitel 2 INSERT-Operation • Syntaxformat für Einfügungen: INSERT INTO <table-name> [ ( <list-of-columns> ) ] <table-expression> • zwei Grundformen üblich: • direkte Angabe einer oder mehrerer Zeilen, z.B. Schlüsselwort für direkte Angabe von Zeilen Tupelnotation INSERT INTO professoren (Name, Grad, Abteilung) VALUES ( ‚Cremers‘, ‚C4‘, ‚III‘) • indirekte Angabe der einzufügenden Zeilen über eine Anfrage, z.B. INSERT INTO professoren (SELECT * FROM Mitarbeiter M WHERE M.Qualifikation = ‚Habilitation‘) © 2011 Prof. Dr. Rainer Manthey Informationssysteme 32 Kapitel 2 UPDATE-Operation • Syntaxformat für Modifikationen: UPDATE <table-name> SET <list-of-assignments> [ WHERE <conditional-expression> ] • Modifiziert alle Zeilen von "table name" , die den WHERE-Teil erfüllen, gemäß den Einzelspaltenänderungen (assignments), die durch Gleichungen im SETTeil definiert werden; Syntaxformat der einzelnen Zuweisungen: <column-name> = { <scalar-expression> | DEFAULT | NULL } • Beispiele: UPDATE professoren SET Name = ‚N.N.‘ WHERE Abteilung = ‚II‘ UPDATE SET © 2011 Prof. Dr. Rainer Manthey professoren Grad = NULL Informationssysteme Zuweisung (Aktion) Bedingung (Test im "alten" Zustand) Vorbereitung der nächsten Besoldungsreform ! 33 Kapitel 2 DELETE-Operation und CURSOR-Versionen DELETE FROM <table-name> [ WHERE <conditional-expression> ] • Syntaxformat für Löschungen: • Bedeutung und Beispiele: offensichtlich ! • Löschung einzelner Zeilen nur durch Angabe einer exakt identifizierenden Bedingung (kein direktes Gegenstück zu INSERT . . . . . VALUES . . . . ). • für UPDATE und DELETE: • spezielle Version im CURSOR-Betrieb • positioned UPDATE/DELETE im Gegensatz zu searched U./D. • Modifikation/Löschung erfolgt an aktueller Cursorposition: . . . . WHERE CURRENT OF <cursor-name> • besonders zu beachten bei positioned-Änderungen: • Cursor kann mit Änderungsbeschränkungen deklariert worden sein: <cursor specification> enthält "FOR UPDATE OF <list-of-columns> • Spalten, auf denen der Cursor sortiert ist, dürfen nicht modifiziert werden. © 2011 Prof. Dr. Rainer Manthey Informationssysteme 34