Rheinische Friedrich-Wilhelm-Universität Bonn Institut für Informatik III Prof. Dr. Rainer Manthey Diplomarbeit Entwurf und Realisierung eines Tools zur Spezialisierung von SQL-Sichten Olga Berman 06.06.2005 Danksagung Hiermit möchte ich mich bei Prof. Dr. Rainer Manthey und den Herren Oliver Speidel für die viele Zeit, die sie der Betreuung dieser Arbeit gewidmet haben, bedanken. Ich bedanke mich auch bei meinen Eltern und Freunden für die Unterstützung. Erklärung Hiermit erkläre ich, Olga Berman, dass ich diese Arbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt sowie Zitate kenntlich gemacht habe. Bonn, den 06. Juni 2005 iii Inhaltsverzeichnis Inhaltsverzeichnis 1 Einführung ........................................................................ 1 2 Relationale Datenbanken ................................................. 3 2.1 2.2 Grundlagen relationaler Datenbanken........................................................................ 3 SQL ............................................................................................................................ 6 2.2.1 DDL.................................................................................................................... 6 2.2.2 DML ................................................................................................................... 7 2.2.3 Sichten.............................................................................................................. 11 3 MS ACCESS - ein DBMS und JET-SQL ..................... 13 3.1 3.2 3.3 3.4 3.5 3.6 Komponenten einer Datenbankanwendung ............................................................. 13 Beziehungen zwischen Tabellen .............................................................................. 15 Datenzugriffsschnittstellen....................................................................................... 17 Datenzugriff mit DAO-Objekten ............................................................................. 17 Jet-SQL..................................................................................................................... 19 VBA (Visual Basic for Applications) ...................................................................... 22 3.6.1 Visual Basic-Entwicklungsumgebung ............................................................. 22 3.6.2 Objekte, Methoden, Eigenschaften .................................................................. 22 3.6.3 VBA-Grundlagen ............................................................................................. 23 4 Deduktive Datenbanken ................................................. 25 4.1 4.2 Deduktive Datenbanken ........................................................................................... 25 Datalog ..................................................................................................................... 26 4.2.1 Grundlagen ....................................................................................................... 26 4.2.2 Auswertung von Datalog-Regeln ..................................................................... 28 4.3 Änderungspropagierung ........................................................................................... 30 4.3.1 Motivation ........................................................................................................ 30 4.3.2 Delta-Regeln..................................................................................................... 31 4.3.3 Transitionsregeln .............................................................................................. 33 5 Änderungspropagierung in SQL................................... 37 5.1 5.2 5.3 5.4 5.5 5.6 Das Prinzip ............................................................................................................... 37 Effektivitätstest......................................................................................................... 41 Besonderheiten, Schwierigkeiten ............................................................................. 43 Duplikate .................................................................................................................. 50 Aggregatfunktionen.................................................................................................. 51 Algorithmen ............................................................................................................. 53 iv Inhaltsverzeichnis 6 Implementierung............................................................. 57 6.1 Compiler................................................................................................................... 57 Parser................................................................................................................ 58 Analyser ........................................................................................................... 60 Code Generator ................................................................................................ 61 6.2 Einbettung in MS Access ......................................................................................... 66 6.3 Compiler-Testbeispiele ............................................................................................ 72 6.3.1 Test 1 ................................................................................................................ 72 6.3.2 Test 2 ................................................................................................................ 75 6.4 Schlussfolgerung ...................................................................................................... 80 6.1.1 6.1.2 6.1.3 7 Zusammenfassung und Perspektiven............................ 81 Literaturverzeichnis............................................................. 83 v Kapitel 1 1 Einführung Durch Änderungsoperationen in einer Datenbank, genauer gesagt durch Tabellenänderungen, können sich auch die Einträge einer Sicht ändern. Eine naive Möglichkeit der Aktualisierung ist die Rematerialisierung der Sichten. Dabei wird der gesamte Inhalt der Sicht neu berechnet. Wenn sich nur ein kleiner Teil der Daten geändert hat, so ist die Rematerialisierung sehr ineffizient. Wenn die Änderungen im Verhältnis zu den enthaltenen Daten insgesamt relativ gering sind, so ist die inkrementelle Anpassung hingegen viel effizienter. Bei dieser wird zunächst festgestellt, wie sich die Änderungen von Basisdaten auf die Sichten auswirken, und nur noch die induzierten Änderungen der Sichten berechnet. Es gibt viele inkrementelle Verfahren zur effizienten Sichtenaktualisierung in Datalog. In relationalen Datenbanksystemen mit der Anfragesprache SQL sind diese Konzepte jedoch noch nicht implementiert worden. Ziel dieser Arbeit ist es, einen Algorithmus zu entwicklen und zu implementieren, der das Konzept der inkrementellen Sichtanpassung in einer relationalen Datenbank realisiert. MS Access 2002 ist ein kommerzielles relationales, SQL-basiertes DB-System, das von der Firma Microsoft herstellt und vertrieben wird. In dieser Arbeit wird eine Erweiterung für MS Access 2002 geschrieben, deren Hauptteil ein Compiler ist. Der Compiler übersetzt die SQL-Sichten in so genannte Delta-Sichten, die nur die echten Faktenänderungen jener Sichten enthalten, die durch die Basisfaktenänderungen verursacht wurden. Um die induzierten Faktenänderungen einer Sicht zu ermitteln, müssen nur ihre Delta-Sichten ausgewertet werden. Die Sicht selbst muss dabei nicht neu berechnet werden. Bei dieser Arbeit wird nicht eine Teil-Sprache von SQL betrachtet, sondern es wird in erster Linie darauf geachtet, dass die von dem Compiler bearbeiteten Sichten die Möglichkeiten von Access-SQL zum größten Teil ausnutzen können. Abbildung 1-1 zeigt eine grobe Skizze des hier zu entwickelnden Compilers, der eine Access Erweiterung darstellt. Die Diplomarbeit ist wie folgt aufgebaut: Im ersten Teil der Ausarbeitung werden die erforderlichen Grundlagen definiert. SQL wird in Abschnitt 2 vorgestellt. In Kapitel 3 werden die Grundlagen der MS Access Datenbank und der darin integrierten Programmiersprache VBA erläutert. Hierbei werden die Unterschiede in der Syntax zwischen dem ANSI-SQL Standard und Jet-SQL (MS Access SQL) aufgezeigt. In Abschnitt 4 werden relationale, deduktive Datenbanken und deren Anfragesprache 1 Einführung Datalog sowie das Konzept der inkrementellen Sichtenanpassung, soweit dies für die Entwicklung eines Compilers erforderlich ist, vorgestellt. Die Darstellung orientiert sich dabei an der Vorlesung „Deduktive Datendanken“ von Professor Dr. Rainer Manthey [Man00]. In Abschnitt 5 wird dann ein Algorithmus entwickelt, der die Delta-Sichten für die SQLSichten erstellt, ohne dass diese in eine datalogähnliche Form überführt werden müssen. Abbildung 1-1 Der praktische Teil dieser Arbeit beginnt mit Abschnitt 6. Dort werden die Implementierung des Compilers in VBA sowie das Programm, in welches der Compiler integriert ist, vorgestellt. Das Programm erlaubt es dabei dem Benutzer, auf bequeme Weise für die ausgewählten Sichten die Delta-Sichten zu generieren, Basisänderungen einzugeben, die induzierten Änderungen anzusehen und gegebenenfalls die Änderungen in der Datenbank zu speichern. Die Korrektheit des Compilers wird anhand von zwei Beispielsichten in Abschnitt 6.3 demonstriert. 2 Kapitel 2 2 Relationale Datenbanken Eine Datenbank ist eine Sammlung von Daten, die aus der Sicht des Benutzers zusammengehören, wie z.B. bei einer Personaldatenbank oder einer Lagerinventardatenbank. Es gibt hierarchische, relationale, multidimensionale und objektorientierte Datenbanken. Die meisten der heute kommerziell hergestellten Datenbanken basieren auf dem relationalen Datenbankmodell. Dieses Kapitel gibt eine allgemeine Einführung in das Thema relationale Datenbanken ([Man02],[KE01]). In Abschnitt 2.2 wird die Anfragesprache SQL mit DDL-Befehlen (Data-Definition-Language) und DML-Befehlen (Data-Manipulation-Language) vorgestellt. Außerdem wird in Abschnitt 2.2.3 das für die Problematik der Änderungspropagierung grundlegende Element − das Konzept der Sichten − erläutert. 2.1 Grundlagen relationaler Datenbanken In der Fachliteratur wird zwischen den Begriffen "Datenbank" (DB; Engl.: Database), "Datenbank-Management-System" (DBMS; Engl.: Database Management System) und "Datenbanksystem" (DBS; Engl.: Database System) unterschieden ([Man02], [KE01]). Unter einem DBMS versteht man ein Softwarepaket, welches in der Lage ist, unabhängig vom Anwendungssystem, (Anwender-)Daten in einem Computersystem zu verwalten, sprich: aus einer Datenbank zu lesen oder etwas hineinzuschreiben. Das DBMS stellt dabei Funktionalitäten zur Datenverwaltung und Bearbeitung (Lesen, Ändern, Einfügen oder Löschen von Daten) zur Verfügung. Ein DBS ermöglicht es mehreren Benutzern, gleichzeitig mit demselben physischen Datenbestand zu arbeiten. Benutzer greifen dabei nicht direkt auf den Datenbestand zu, sondern die Datenbanksoftware erledigt die lesenden und schreibenden Zugriffe auf die Daten. Der Begriff Datenbanksystem beschreibt den Verbund einer nicht leeren Menge von Datenbanken mit einem DatenbankManagement-System: DBS = DB + DBMS. Das Relationenmodell wurde im Jahre 1970 von Dr. Edgar F. Codd [Cod70] formuliert und dient seitdem als Grundlage für die meisten Datenbanksysteme. Die Basis dafür bildet die mathematische Relationentheorie. Die Relationen (hier im Sinne einer zweidimensionalen Tabelle) sind die einzigen Strukturelemente, die man dazu braucht. 3 Relationale Datenbanken _ Relationale Datenbanken, wie etwa MS-Access, werden heutzutage in den meisten Fällen bevorzugt, und zwar besonders dort, wo auf schnelle und einfache Bedienung Wert gelegt wird. Zur Beschreibung von Daten verwenden relationale Datenbanken das relationale Datenmodell. Hierbei werden Datenobjekte zu einer Tabelle zusammengefasst. Eine solche Tabelle besteht aus einem Tabellenkopf und einem Tabellenrumpf. Der Tabellenkopf wiederum setzt sich aus einer festen Anzahl von Spalten zusammen, die man im Allgemeinen Attribute nennt. Der Wertebereich eines Attributs heißt Domäne; eine einzelne Zeile hingegen nennt man ein Tupel. Attribute kann man auch als Spalten einer Tabelle bezeichnen, welche die Eigenschaften der Objekte beschreiben. Die Werte des Tabellenrumpfes stellen die Attributwerte der zu beschreibenden Objekte dar. Der Tabellenrumpf enthält eine variable Anzahl von Tupeln, welche Datenobjekte darstellen. Die Gesamtheit aller Tupeln einer Tabelle, also die Daten, werden als Relation bezeichnet. Relationen sind Mengen. Diese Mengen können als zweidimensionale Tabellen dargestellt werden. Die Anzahl der Attribute im Relationskopf gibt den Grad einer Relation an, während die aktuelle Anzahl der Tupeln im Rumpf die Kardinalität definiert. Da eine Datenbankrelation keine doppelten Elemente enthalten darf, gibt es auch keine identischen Tupeln. Daher wird jedem Tupel ein individueller Schlüssel zugewiesen. Durch diesen Schlüssel kann ein Tupel eindeutig identifiziert werden. Normalerweise verwendet man hierfür eine minimale Menge an Attributen, deren Werte sich zusammengenommen von den Werten anderer Tupel definitiv unterscheiden. Aus Performancegründen und zur besseren Handhabung sollte ein Schlüssel möglichst wenig Attribute beinhalten und auch nur solche, bei deren Wegnahme die Schlüsseleigenschaft verloren geht. Jedes Attribut, das zu einem Schlüssel gehört, heißt Schlüsselattribut; alle anderen Attribute sind Nichtschlüsselattribute. Häufig existieren zu einem Relationstyp mehrere mögliche Schlüssel. Diese werden dann als Schlüsselkandidaten bezeichnet. Ein Schlüssel, der aus mehreren Attributen besteht (Attributkombination), heißt zusammengesetzter Schlüssel. Die Festlegung eines (Primär-)Schlüssels ist eine Designentscheidung. Bei mehreren Schlüsselkandidaten wird oft der kürzeste als Primärschlüssel ausgewählt. Im Falle eines zusammengesetzten Schlüssels sollte über die Einführung eines künstlichen Attributs zur Indentifizierung nachgedacht werden. Manchmal geht dies auf Kosten des Speicherbedarfs, bringt aber auch mehr Klarheit mit sich. Dabei ist jeder Primärschlüssel ein Schlüsselkandidat, aber nur ein einziger Schlüsselkandidat ist ein Primärschlüssel. Primärschlüssel sind deshalb von großer Bedeutung, weil sie das einzige Mittel zur Identifizierung eines gegebenen Tupels einer Relation sind. Die Kombination aus dem Namen einer Relation und ihrem Primärschlüssel reicht aus, um den Zugriff auf ein bestimmtes Tupel innerhalb einer Relation zu garantieren. Da Primärschlüssel für Relationen eine besondere Bedeutung haben, müssen sie sich von anderen Schlüsseln durch die Operationen unterscheiden, die man an ihnen ausführen kann. So sollte der Primärschlüssel z.B. niemals den Nullwert annehmen. Der Nullwert ist ein Platzhalter für einen unbekannten oder nicht anwendbaren Wert. Er entspricht nicht der numerischen Null oder einer leeren Zeichenkette, welche oft gültige Attributwerte sein können. Er stellt vielmehr ein Hilfsmittel zur Wahrung der Integrität dar. Neben Relationen, die den Einheiten der realen Welt entsprechen, enthalten Datenbanken in der Regel auch Relationen über Relationen (Beziehungsrelationen). Besteht eine Datenbank aus mehreren Relationen, dann werden die Beziehungen zwischen den 4 Kapitel 2 einzelnen Relationen dadurch hergestellt, dass der Primärschlüssel einer Relation in die Attributmenge einer anderen Relation aufgenommen wird. Der Schlüssel dient dort der Identifizierung von Tupeln einer „fremden“ Relation und wird als Fremdschlüssel (Engl.: Foreign Key) bezeichnet. Im Allgemeinen dient ein Fremdschlüssel in einer Relation als Mechanismus, um auf eine andere Relation zu verweisen. Die Fremdschlüssel einer Relation zeigen also an, dass diese Relation mit einer anderen Relation über den Inhalt ihrer Fremdschlüssel verbunden ist. Ein Beispiel: Relation Mann Name Alter Prinz 300 Tony 30 Relation Frau Name Schneewittchen Angela Beziehungsrelation Ehe Ehemannname Ehefrauname Schneewittchen Prinz Tony Angela Alter 400 30 Ehedauer 200 2 Unter dem Begriff der Integrität bzw. Konsistenz versteht man die Widerspruchsfreiheit von Datenbanken. Eine Datenbank ist integer bzw. konsistent, wenn die gespeicherten Daten fehlerfrei erfasst sind und den gewünschten Informationsgehalt korrekt wiedergeben. Relationale Integritätsregeln dienen der Gewährleistung der Integrität und werden durch das Datenbankschema umgesetzt. Bei relationalen Datenbanken existieren zwei Integritätsregeln bzw. Bedingungen: Die Entity-Integrität und die Referentielle Integrität. Jede Relation besitzt einen Primärschlüssel (Attribut oder Attributskombination), der jedes Tupel der Relation eindeutig bestimmt. Für jede Relation wird ein Schlüssel zwingend verlangt. Falls mehrere Schlüsselkandidaten vorkommen, muss einer davon als Primärschlüssel definiert werden. Die Eindeutigkeit von Primärschlüsselinhalten wird vom Datenbanksystem geprüft. Diesen Sachverhalt nennt man Entity-Integrität (Eindeutigkeit). Die Attribute einer Relation können nur Werte aus einem vordefinierten Wertebereich annehmen. Das nennt man Wertebereichsbedingung. Dies kann nicht durch das Datenbanksystem selbst gewährleistet werden, die Validierung bleibt weitgehend dem Anwender überlassen. Eine Unterstützungsmöglichkeit bilden Eingabemasken, welche bei der Datenerfassung dem Anwender fest definierte Attributwerte für die Attribute vorgeben (Listenfelder, Radio-Buttons etc.). Jeder Wert eines Fremdschlüssels muss als Schlüsselwert in der referenzierten Relation existieren. Die Einhaltung dieser Bedingung nennt man referentielle Integrität. Werden Daten verändert (Ändern, Löschen oder Einfügen), können die Integritätsbedingungen verletzt werden. In diesem Fall spricht man von Datenbankanomalien, da die Daten in sich widersprüchlich sind. Man unterscheidet drei Arten von Anomalien: Einfügeanomalie, Änderungsanomalie und Löschanomalie. Manchmal kann ein Sachverhalt nur dann eingefügt werden, wenn ein anderer Sachverhalt ebenfalls mit aufgenommen wird. Es könnte jedoch sein, dass zum zweiten Sachverhalt entweder noch keine Daten zur Verfügung stehen, oder die Erfassung nicht gewünscht wird. Normalerweise überwacht das Datenbankmanagementsystem die Abläufe und hindert den Benutzer daran, eine Einfügeanomalie zu verursachen. Daher kommt sie in der Praxis selten vor. Die 5 Relationale Datenbanken _ Änderungsanomalie hingegen entsteht dann, wenn durch den Austausch eines einzigen Sachverhaltes die Relation an mehreren Stellen angepasst werden muss, dies aber nicht geschieht. Eine solche unvollständige Anpassung wiederum führt zu inkonsistenten Datenbeständen. Eine Löschanomalie schließlich entsteht, wenn ein Sachverhalt ungewollt verloren geht. 2.2 SQL Nach der Einführung des relationalen Modells Anfang der 70er Jahre wurde von IBM ein DBMS-Prototyp namens "System R" entwickelt. Die Anfragesprache, die System R benutzte, wurde "SEQUEL" (Structured English Query Language) genannt und später in SQL umbenannt. Im Laufe der Jahre hat sich SQL als Abfragesprache für relationale Datenbanken wie Oracle, MS-Access, MySQL, PostgreSQL oder MaxDB etabliert, die von den größten kommerziellen Datenbankherstellern wie Oracle oder IBM angeboten werden. Die erste SQL-Norm wurde 1986 von der ANSI-Kommision (American National Standards Institute) verabschiedet. 1989 wurde der Standard das erste Mal revidiert und 1992 entstand der stark erweiterte SQL-92, auch SQL 2 genannt. Der heute gültige Standard ist SQL:1999 (SQL-3) ([ANSI99I], [ANSI99II]). Er wurde erst im Dezember 1999 veröffentlicht. Die Anfragesprache beinhaltet die Werkzeuge, mit deren Hilfe man Informationen über den Inhalt einer Datenbank erfragen, Daten manipulieren und definieren kann. SQL zählt zu den deklarativen Anfragesprachen. D.h. die Benutzer geben nur an, welche Daten sie interessieren, und nicht, wie die Auswertung der Daten vorgenommen werden soll. Bei SQL unterscheidet man: DDL-Befehle (Data-Definition-Language): Mit den Befehlen dieser Klasse werden Datenstrukturen (Tabellen, Indices,...) erzeugt, verändert oder gelöscht. DML-Befehle (Data-Manipulation-Language): Mit diesen Befehlen werden die Abfragen und Änderungen der Datenbank durchgeführt. 2.2.1 DDL Die zu einer Datenbank gehörenden Tabellendefinitionen werden als Schema der Datenbank bezeichnet. Sie werden automatisch im Datenwörterbuch gespeichert. Das Datenwörterbuch beschreibt den Zustand der Datenbank, enthält also Metadaten. DDL CREATE TABLE (Tabelle erzeugen) DROP TABLE (Tabelle löschen) ALTER TABLE (Tabelle modifizieren) CREATE INDEX (Index für Tabellen anlegen) CREATE VIEW (Erzeugen einer Sicht) RENAME (Tabellen, Spalten umbenennen) ADD/DROP COLUMN (Spalte einfügen/ löschen) 6 Kapitel 2 So wird eine neue Tabelle mit dem Befehl CREATE TABLE erzeugt. Dem Namen der Tabelle folgt in Klammern eine Liste der Attribute und ihrer Typen, durch Komma(ta) getrennt. Nach einer Typeneingabe kann zusätzlich noch die Einschränkung NOT NULL folgen. In SQL gibt es einen speziellen Wert mit dem Namen NULL, der in jedem Datentyp vorhanden ist. Ein NULL -Wert wird z.B. dann gespeichert, wenn der korrekte Wert nicht bekannt ist. Dadurch wird erzwungen, dass alle in die Tabelle eingetragenen Tupel an dieser Stelle einen definierten Wert haben. In Beispiel 2.1 ist es also nicht möglich, Autoren ohne Namen einzutragen. Die Spezifikation NOT NULL ist eine Integritätsbedingung und sollte für alle Primärschlüsselattribute angegeben werden. Beispiel 2.1 CREATE TABLE Autoren ( Name CHARAKTER (50) NOT NULL, Land CHARAKTER (50) ); CREATE TABLE Bücher ( Autor CHARAKTER(50) NOT NULL, Titel CHARAKTER (100) NOT NULL, Art CHARAKTER (50) ); CREATE TABLE Fachliteratur ( Autor CHARAKTER (50) NOT NULL, Titel CHARAKTER (100) NOT NULL, ); 2.2.2 DML Die Datenmanipulationssprache DML dient zur Anfrage, zum Einfügen, Ändern und Löschen von Datensätzen. DML SELECT (Tabellen abfragen) INSERT (Tabellenzeilen hinzufügen) UPDATE (Daten einer Tabelle verändern) DELETE (Tabellenzeilen löschen) Seien A1 bis An Attribute der R1 bis Rk Relationen. SELECT A1, ..., An FROM R1, ..., Rk WHERE P; 7 Relationale Datenbanken _ Hinter FROM steht der Name der Tabelle. Durch die WHERE-Klausel wird bestimmt, welche Datensätze (Zeilen einer Tabelle) ausgewählt werden sollen (Bedingungsprüfung). Formal ausgedrückt, entspricht das Ergebnis des FROM - Teils logisch dem kartesischen Produkt R1 * ... * Rk der beteiligten Relationen. Der WHERE -Teil entspricht der Selektion der relationalen Algebra. Er kann auch fehlen, dann wird als Bedingung aber implizit "True" eingesetzt und jedes Tupel des Kreuzproduktes in das Ergebnis aufgenommen. Der SELECT-Teil projiziert schließlich auf die eingegebenen Attribute A1, ... , An. Ein äquivalenter Relationenalgebra-Ausdruck sieht folgendermaßen aus: ∏A1,…,An( σP(R1 × … × Rk)) Die folgende SQL-Anfrage liefert alle Autoren, die in Deutschland geboren wurden: SELECT Autoren.Autor FROM Autoren WHERE Autoren.Land="Deutschland"; In SQL-92 kann man den JOIN-Operator benutzen. Er stellt eine schnelle Technik zum gleichzeitigen Zugriff auf zwei Tabellen zur Verfügung. Dort können im FROM-Teil die folgenden Schlüsselwörter angegeben werden: CROSS JOIN: NATURAL JOIN: JOIN oder INNER JOIN: LEFT, RIGHT oder FULL OUTER JOIN: Kreuzprodukt, natürlicher JOIN, THETA - JOIN äußerer JOIN. Auf diese Weise kann z.B. eine Anfrage der Art: SELECT * FROM R1,R2 WHERE R1.A=R2.b; explizit als JOIN formuliert werden: SELECT * FROM R1 JOIN R2 ON R1.A=R2.B; Hinter dem JOIN wird mit ON die Joinbedingung explizit angegeben. Die folgende Abfrage liefert eine virtuelle Tabelle, in der die Inhalte der beiden Tabellen Bücher und Autoren miteinander kombiniert werden. SELECT Bücher.Autor, Bücher.Titel, Bücher.Art, Autoren.Land FROM Bücher INNER JOIN Autoren ON Bücher.Autor=Autoren.Name; 8 Kapitel 2 SQL kennt die üblichen Vergleichsoperatoren: Vergleichsoperatoren Operatoren Beschreibung = gleich <> ungleich > größer < kleiner >= größer gleich <= kleiner gleich – sowie einige zusätzliche wie BETWEEN, IN, LIKE, AND, OR oder NOT. Bei dem Operator BETWEEN Wert1 AND Wert2 liegt der Vergleichswert zwischen Wert1 und Wert2. Bei der IN Werteliste ist der Vergleichswert in der angegebenen Werteliste enthalten. Sehr nützlich ist der Vergleich von Zeichenketten in Hinblick auf Ähnlichkeit mit dem Operator LIKE. Der Ausdruck LIKE Zeichenfolge bedeutet, dass die Vergleichszeichen der Zeichenfolge entsprechen. Wenn eine Zeichenkette nicht genau bekannt ist, so können "%" und "_" als Platzhalter für unbekannte Teile verwendet werden. "%" steht dabei für beliebig viele und "_" für genau ein unbekanntes Zeichen. Vergleichsoperatoren können mit den logischen Operatoren AND, OR und NOT verknüpft werden. Eine SQL-Anfrage mit zusätzlichen Operatoren: SELECT [DISTINCT] {Feldname, Feldname,..*} FROM Tabelle [, Tabelle, Tabelle....] [WHERE {Bedingung}] [ORDER BY Feldname [ASC|DESC]...] [GROUP BY Feldname [HAVING {Bedingung}]] Werden alle Attribute benötigt, so kann zur Abkürzung einfach ein "*" anstelle der Attributsnamen angegeben werden. Da die Eliminierung der Duplikate aus Effizienzgründen in einer Tabelle nicht automatisch vorgenommen wird, kann dies mit dem Schlüsselwort DISTINKT erzwungen werden. Mit Hilfe von ORDER BY werden Daten nach einem oder mehreren Feldnamen sortiert ausgegeben. Die vorgegebene Sortierreihenfolge ist aufsteigend ASC (ascending). Soll absteigend sortiert werden, so muss DESC (descending) eingegeben werden. Mit der GROUP BY -Klausel kann man die Zeilen einer Tabelle nach bestimmten Feldern gruppieren. Die HAVING - Klausel ist der WHERE - Klausel sehr ähnlich. Bei der HAVING - Klausel werden bestimmte Gruppen aus einer Gruppenmenge ausgewählt. Somit kann die HAVING - Klausel nur in Zusammenhang mit der GROUP BY - Klausel angewendet werden. Aggregatfunktionen führen Operationen auf den Tupelmengen durch und komprimieren eine Menge von Werten zu einem einzigen Wert. AVG bestimmt den Durchschnitt, MAX und MIN bestimmen das größte bzw. kleinste Element und SUM bildet die Summe einer Menge von Zahlen. COUNT zählt die Anzahl der Zeilen in der Tabelle. Häufig werden die Aggregatfunktionen zusammen mit dem Opertor GROUP BY kombiniert. Folgende 9 Relationale Datenbanken _ Anfrage liefert alle Autoren, die, der Tabelle Bücher nach, mehr als 3 Bücher geschrieben haben: SELECT Bücher.Autor, COUNT(*) FROM Bücher GROUP BY Autor HAVING COUNT(*) > 3 ORDER BY Autor; In SQL können SELECT-Anweisungen geschachtelt werden. Hier ein Beispiel für eine geschachtelte Abfrage: SELECT deutscher_Bücher.Autor, deutscher_Bücher.Titel, deutscher_Bücher.Art FROM deutscher_Bücher WHERE NOT EXISTS ( SELECT * FROM Fachliteratur WHERE Fachliteratur.Autor=deutscher_Bücher.Autor AND Fachliteratur.Titel=deutscher_Bücher.Titel); Der EXIST -Operator liefert TRUE, falls die Unterabfrage mindestens ein Ergebnistupel zurückliefert, und ansonsten FALSE Der Operator IN testet auf Mengenmitgliedschaft: SELECT * FROM Autoren WHERE Name IN ( SELECT * FROM deutscher_Bücher WHERE Autoren.Name=deutscher_Bücher); Die Abfrage liefert alle Autoren, die in der Sicht deutscher_Bücher enthalten sind, also alle deutschen Autoren. Wie schon gesagt wurde, gibt es in SQL einen speziellen Wert mit dem Namen NULL, der in jedem Datentyp vorhanden ist. Ein NULL-Wert wird z.B. dann gespeichert, wenn der korrekte Wert nicht bekannt ist. Bei der Abfragebearbeitung können NULL-Werte als Ergebnis von Operationen entstehen - selbst wenn die zugrunde liegenden Operationen keine NULL-Werte enthalten. Die Aggregatfunktion MAX, angewendet auf eine leere Tabelle, liefert zum Beispiel einen NULL-Wert zurück. IS NULL(Feld) liefert TRUE falls das Vergleichsfeld einen NULL-Wert hat. SQL enthält eine dreiwertige Logik, d.h. SQL kennt nicht nur TRUE und FALSE, sondern auch UNKNOWN. Diesen Wert liefert der Vergleich zweier Argumente zurück, wenn mindestens einer von ihnen NULL ist. Die Operationen der Mengenlehre – Vereinigung, Durchschnitt und Differenz – heißen in SQL UNION, INTERSECT und EXCEPT. Da das Ergebnis einer Abfrage wieder eine sinnvolle Tabelle darstellen soll, müssen die Ergebnistypen der Teilanfragen übereinstimmen. IN ist äquivalent zum Ausdruck =ANY. Es gibt folgende Ausdrücke in 10 Kapitel 2 SQL: Vergleichsoperatoren (=, >, <, ...) sowie ALL oder ANY. ANY prüft, ob es mindestens ein Element im Ergebnis der Unterabfrage gibt, für das der Vergleich mit dem linken Argument des Operators erfüllt wird. ALL testet, ob alle Ergebnisse der Unterabfrage den Vergleich erfüllen. Die Studenten mit der größten Semesterzahl können mit folgendem Ausdruck gefunden werden: SELECT Name FROM Studenten WHERE Semester>=all (SELECT Semester FROM Studenten); Mit Hilfe von SQL kann man die Daten auch ändern, modifizieren oder löschen. Mit dem INSERT - Befehl ist es möglich, der Datenbank Daten hinzuzufügen. Ein Beispiel: INSERT INTO Fachliteratur SELECT FROM Bücher WHERE ART="Fachliteratur"; Zum Löschen wird der DELETE-Befehl verwendet. Es werden nur Einträge gelöscht, die der Bedingung des WHERE-Teils genügen: DELETE FROM Bücher WHERE Art="Fachliteratur"; Bestehende Zeilen können mit dem UPDATE- Befehl verändert werden: UPDATE Bücher SET Art="Belletristik"; 2.2.3 Sichten Die Möglichkeit, Basisrelationen mit dem CREATE TABLE Befehl zu erzeugen, wurde bereits erwähnt. Neben den so gebildeten Basisrelationen existieren noch weitere Relationen, die so genannten Sichten (im Englischen: Views). Dies sind von Basisrelationen abgeleitete Relationen, wobei in der Datenbank nicht deren Inhalt, sondern nur die Ableitungsregeln, also Sichtdefinitionen, abgespeichert werden. Somit werden die Sichten bei jedem Zugriff neu berechnet Der Sinn von Sichten liegt darin, dem Benutzer einen Datenausschnitt zu zeigen, ohne dass die Daten in dieser Konstellation in der Datenbank tatsächlich gespeichert sind. Auch im Falle von Zugriffsschutz gibt es wichtige Anwendungen, wenn zum Beispiel nur auf einen Teil einer Relation durch Dritte zugegriffen werden darf. Sichten können sich auf Datenbankrelationen und/ oder auf andere Sichten stützen. Beispiel 2.2 (Sichten, die auf den Tabellen aus dem Beispiel 2.1 aufbauen) CREATE VIEW deutscher_Bücher AS ( SELECT Bücher.Autor, Bücher.Titel, Bücher.Art 11 Relationale Datenbanken _ FROM Bücher, Autoren WHERE Bücher.Autor=Autoren.Name AND Autoren.Land="Deutschland"; ); und CREATE VIEW deutsche_Belletristik AS ( SELECT deutscher_Bücher.Autor, deutscher_Bücher.Titel, deutscher_Bücher.Art FROM deutscher_Bücher WHERE NOT EXISTS ( SELECT * FROM Fachliteratur WHERE Fachliteratur.Autor=deutscher_Bücher.Autor AND Fachliteratur.Titel=deutscher_Bücher.Titel) ); Der neueste SQL Standard SQL:1999 erlaubt die Definition rekursiver Sichten, aber nur mit der Beschränkung, dass die Relation in der rekursiven Unterabfrage im FROM-Teil nur einmal auftreten darf. Mehr Information über Rekursion in SQL können dem [ANSI99II] entnommen werden. 12 Kapitel 3 3 MS ACCESS - ein DBMS und JET-SQL Microsoft Access ist ein Datenbanksystem, welches die Verwaltung von Daten und die Entwicklung von Datenbankanwendungen ermöglicht. Access ist ein relationales Datenbank-Management-System (RDBMS) ([HS02], [Bro99]). Es wird zusammen mit dem Microsoft Office Professional-Paket vertrieben. Die bisher aktuellste Version ist MS Access 2003. In dieser Arbeit wird allerdings MS Access XP verwendet. Im Gegensatz zu den anderen Office-Programmen von Microsoft läuft Access nur unter Windows. Dieses Programm speichert die Daten in einem so genannten *.mdb-Format ab. Ferner kann auf Access-Datenbanken auch mit anderen Programmiersprachen wie z.B. Delphi oder Visual Basic zugegriffen werden. Die Schreibzugriffe erfolgen in einer Access-Datenbank immer am Dateiende. Dieses Vorgehen ist nötig, um einen Mehrbenutzerzugriff zu ermöglichen. Gelöschte oder abgeänderte Elemente bleiben als "Löcher" in der Datei vorhanden, bis die Access-Datei als Ganzes komprimiert wird. Mit VBA (Visual Basic for Applications) kann man benutzerdefinierte Erweiterungen für MS Access[Sta99] erstellen. Der in dieser Arbeit entwickelte Compiler stellt eine solche Erweiterung dar. Dieses Kapitel gibt eine allgemeine Einführung in das relationale DBMS MS Access. In Abschnitt 3.1 werden kurz Komponenten wie z.B. Formulare, Berichte oder Abfragen vorgestellt. Abschnitt 3.2 gibt eine kurze Anleitung dazu, wie man in Access referentielle Integrität definieren kann. In den Abschnitten 3.3 und 3.4 werden die für die Compilerimplimentierung interessanten Datenzugriffsmodelle DAO (Data AccessObjects) erläutert. Abschnitt 3.5 vergleicht Jet-SQL mit der Standard-SQL. Abschnitt 3.6 gibt eine allgemeine Einführung in VBA. 3.1 Komponenten einer Datenbankanwendung Nach dem Start einer neuen MS Access-Anwendung wird das Datenbankfenster (Abbildung 3.1) eingeblendet. Es ist das Hauptfenster dieses Systems,, von dem aus man mit Tabellen, Abfragen, Formularen, Berichten, Datenzugriffsseiten, Makros und Modulen arbeiten kann. Im Folgenden werden diese Komponenten nur in einfacher Form vorgestellt, da eine detaillierte Erläuterung den Rahmen dieser Arbeit sprengen würde. Die Daten einer Access-Datenbank werden in Tabellen gespeichert. Bei Access können die Daten sehr unterschiedlicher Art sein, so etwa als Texte, Zahlen, Wahrheitswerte, 13 MS ACCESS - ein DBMS und JET-SQL _ Hyperlinks, Bilder etc. Tabellen und Abfragen stellen die Daten in tabellarischer, standardisierter Form dar. Der Benutzer hat dabei kaum Möglichkeiten, die Art der Darstellung zu beeinflussen. In Formularen kann der Benutzer dagegen selber bestimmen, wie die Daten auf dem Bildschirm dargestellt werden. Dies geschieht durch die Anordnung und Gestaltung von Eingabefeldern (bei Access heißen sie Textfelder) und ihren Bezeichnungen, Auswahlfeldern, Unterformularen, grafischen Elementen etc. Die Formulare erlauben dabei eine derartige Gestaltung der Datenbank, dass diese auch von Anwendern ohne Access-Kenntnisse bearbeitet werden kann, und zwar lesend wie schreibend. Die Berichte haben eine große Ähnlichkeit mit den Formularen. Sie unterscheiden sich aber von den Formularen vor allem in zwei Punkten: 1. 2. Berichte sind nicht für den Bildschirm, sondern für die Druckausgabe gedacht. In Berichten können Daten in gruppierter Weise ausgegeben und berechnet werden. In Formularen ist es dies hingegen nicht möglich. Berichte sind besonders nützlich, wenn eine große Datenmenge in übersichtlicher Form ausgedruckt werden soll. Abbildung 3.1 Datenbankfenster Stellen Tabellen die Datenbasis einer Datenbank dar, so sind Abfragen das wichtigste Instrument der Datenverwaltung. Mit einer Abfrage können aus der Gesamtheit der Daten die Informationen zusammengestellt werden, die für einen bestimmten Zweck benötigt werden. Eine Sicht kann man in Access als eine Abfrage definieren. Man kann Abfragen annähernd so einsetzen wie Tabellen. Zum Beispiel kann ein Formular oder ein Bericht ebenso gut auf einer Abfrage basieren wie auf einer Tabelle. Abfragen können in der Datenbank gespeichert werden. Normalerweise enthält eine 14 Kapitel 3 Datenbank eine Vielzahl von gespeicherten Abfragen, die aufgerufen oder als Datenquelle für Formulare oder Berichte dienen können. Wenn Daten in einem Formular, einer Tabelle oder im Datenblatt einer Abfrage geändert oder eingeben werden, speichert Access die Veränderung automatisch und ohne Meldung, sobald es sie als abgeschlossen erkennt. Diese Verfahrensweise hat den großen Vorteil, dass der Benutzer sich um das Speichern von Daten im Allgemeinen überhaupt nicht zu kümmern braucht. In einer Mehrbenutzerumgebung sind dadurch veränderte Datensätze sofort für andere Benutzer aktualisiert. Außerdem trägt diese Tatsache zur Datensicherheit bei Systemabstürzen bei, da ungesicherte Datenänderungen praktisch nicht vorkommen. Mit Makros werden die wichtigsten Access-Aktionen, die sonst manuell mittels Menübefehlen oder durch Anklicken von Symbolschaltflächen veranlasst werden, automatisch ausgeführt. Makros werden auf bequeme Weise in tabellarisch angeordneten Makrofenstern erstellt. So können die Programmabläufe automatisiert werden, ohne dass man sie dafür programmieren muss. Zur Vereinfachung häufig vorkommender komplexer Aufgaben (Erstellen einer Tabelle, Abfrage etc.) stellt Access mehrere Assistenten zur Verfügung. Diese Assistenten führen den Anwender Schritt für Schritt weiter und bieten dabei geeignete Auswahlalternativen und Hinweise an. Auf Module gehen wir später in Kapitel 3.6.2 ein. 3.2 Beziehungen zwischen Tabellen Da diese Arbeit sich mit dem Problem der Sicht-/ Tabellenänderungen beschäftigt, wird das Thema der referentiellen Integrität nicht umgangen werden können. Bei Tabellen, die in Beziehung zu anderen Tabellen stehen, können einige Daten nicht gelöscht und andere wiederum nicht eingefügt werden. Daher ist es von enormer Wichtigkeit, sich jetzt schon ein Bild davon zu machen, wie dies funktioniert. Microsoft Access bietet mit dem Beziehungsfenster die Möglichkeit, Beziehungen zwischen einzelnen Tabellen anzugeben. Das Beziehungsfenster (Abbildung 3.2) kann von der Menüleiste aus geöffnet werden. Um eine Beziehung aufzubauen, klickt man mit der Maus auf das gewünschte Feld der einen Tabelle und zieht bei gedrückter Maustaste zum gewünschten Feld der anderen Tabelle (Abbildung 3.3). Beziehungen zwischen einzelnen Tabellen werden dadurch hergestellt, dass der Primärschlüssel einer Tabelle in die Attributmenge einer anderen Tabelle aufgenommen wird. Der Schlüssel dient dort der Identifizierung von Datensätzen einer „fremden“ Tabelle und wird als Fremdschlüssel bezeichnet. Access legt die Beziehungen automatisch fest: Bei 1:N-Beziehungen wird der Tabelle mit dem Primärschlüssel der “1-Teil“ zugewiesen (Mastertabelle), der Tabelle mit dem Fremdschlüssel der „N-Teil“ (Detailtabelle). 15 MS ACCESS - ein DBMS und JET-SQL _ Abbildung 3.2 Beziehungsfenster 1:1-Beziehungen (sehr selten) werden dadurch hergestellt, dass eine Beziehung zwischen zwei Primärschlüsseln definiert wird. M:N-Beziehungen können in Access nicht definiert werden. Abbildung 3.3 - Beziehungen zwischen Tabellen Wenn zwischen zwei Tabellen die „referentielle Integrität“ aktiviert wird, ändert sich das Verhalten der Tabelle beim Löschen und Ändern von Datensätzen. Bei der referentiellen Integrität können keine Datensätze in die Detailtabelle eingegeben werden, wenn kein entsprechender Datensatz in der Mastertabelle existiert. Ebenso darf das Feld in der 16 Kapitel 3 Mastertabelle nicht verändert werden, wenn dadurch in der Detailtabelle “verwaiste“ Datensätze entstehen. Falls die Aktualisierungsweitergabe aktiviert wird, wird eine Änderung des verknüpften Feldes in der Mastertabelle automatisch an alle verknüpften Datensätze der Detailtabelle weitergegeben. Bei aktivierter Löschweitergabe werden alle verknüpften Datensätze der Detailtabelle gelöscht, wenn ein Datensatz in der Mastertabelle gelöscht wird. 3.3 Datenzugriffsschnittstellen Datenzugriffsschnittstellen werden für den Zugriff auf Tabellen und Abfragen benötigt. In diesem Abschnitt wird eine Übersicht über die verschiedenen Schnittstellen von MS Microsoft Access gegeben. Formulare, Berichte und Steuerelemente, Tabellen und Abfragen können als Objekte betrachtet werden, auf die mit Hilfe einer Objektvariablen zugegriffen werden kann. Access XP unterstützt zwei verschiedene Datenzugriffsmodelle: DAO (Data AccessObjects) und ADO (Active(X) Data Objects). Ursprünglich gab es nur die DAO-Bibliothek. Sie wurde entwickelt, um einen möglichst schnellen und sicheren Zugriff auf Access-Datenbanken zu ermöglichen. Die DAOBibliothek stellt alles zur Verfügung, was zur Manipulation der Datenbanken benötigt wird. Access 2000 erhielt zusätzlich die zuvor mit dem Internet Explorer 4 eingeführte ADO-Bibliothek. Dabei handelte es sich um eine neue Bibliothek, die mit DAO zwar stark verwandt war, aber vor einem völlig anderen Hintergrund entwickelt wurde. ADO wurde nicht gezielt für Access entworfen (wie etwa DAO), weil DAO etwa irgendwelche Nachteile gehabt hätte und nun durch eine neue Bibliothek ersetzt werden sollte. Stattdessen ist ADO ein von Access unabhängiger Bestandteil von Microsofts Datenbankstrategie. Dabei wird das Ziel verfolgt, eine einheitliche Schnittstelle zu unterschiedlichen Datenbanken zu schaffen (wie z.B. Access-, Oracle- oder MS SQLDatenbanken), die mit beliebigen Programmiersprachen (wie etwa Java, VB oder C++) auf die immer gleiche Art und Weise zum Datenzugriff verwendet werden könnte. Diese Schnittstelle sollte auch den Zugriff auf Datenbanken an unterschiedlichsten Orten (auf lokale PC-Datenbanken genauso wie auf weit entfernte Datenbanken auf Unix- oder anderen Serversystemen im Netzwerk) ermöglichen. Aufgrund seiner Vielseitigkeit ist ADO für den Umgang mit Access-Datenbanken allerdings unnötig kompliziert und schwierig. Außerdem fehlen ADO sogar Features, die DAO zum Umgang mit AccessDatenbanken besitzt. Um lokale Access-Datenbanken zu erzeugen und zu programmieren, sollte man daher das hierfür besser geeignete DAO verwenden. Da es sich bei der im Rahmen dieser Diplomarbeit geleisteten Implementierung um eine Access-Datenbankerweiterung handelt, haben wir ebenfalls die DAO-Objekte benutzt. Daher gehen wir hier ein wenig gründlicher auf sie ein. 3.4 Datenzugriff mit DAO-Objekten Abbildung 3.4-1 enthält die Objekte, die für einen DAO-Datenzugriff mit Access relevant sind. 17 MS ACCESS - ein DBMS und JET-SQL _ An oberster Stelle befindet sich das DBEngine-Objekt. Dieses Objekt ist keine Auflistung, sondern ein von Access fest vorgegebenes Objekt. Alle anderen Objekte repräsentieren Auflistungen und sind in Abbildung 3.4 deshalb grau gefärbt. Das DBEngine-Objekt repräsentiert die JET-Datenbank-Engine (JET = Joint Engine Technologie) von Access, die für die Verwaltung aller Daten zuständig ist. Die dritte Ebene stellen die Database-Objekte dar, die jeweils eine bestimmte Datenbank repräsentieren. Da es mit VBA möglich ist, mehrere Datenbanken gleichzeitig zu öffnen, können sich in der dazugehörigen Databases-Auflistung auch mehrere Database-Objekte befinden. An erster Position (Positionsnummer 0) befindet sich immer die aktuelle Datenbank, mit der gerade in Access gearbeitet wird. Dasselbe gilt für die aktuelle Arbeitsumgebung (Workspace-Objekt). Die aktuelle Arbeitsumgebung befindet sich in der Workspaces-Auflistung ebenfalls an Positionsnummer 0. Um auf die aktuelle Benutzerdatenbank zuzugreifen, kann der folgende Ausdruck verwendet werden: Dbengine.Workspaces(0).Databases(0). oder kürzer: DBEngine(0)(0). Noch mehr verkürzt wird der Zugriff auf die aktuelle Datenbank durch den Einsatz der Funktion Currentdb. Auch in dieser Arbeit wird diese Funktion verwendet. Dies ist auch die offizielle Empfehlung von Microsoft. Mit der Currentdb-Methode kann auf Daten von verschiedenen Tabellen zugegriffen werden, ohne Tabellen oder Formulare sichtbar öffnen zu müssen. Nur die Datenbank selbst muss geöffnet sein. Die Methode gibt ein DataBase-Objekt zurück, auf das sich unter anderem die folgenden Eigenschaften und Methoden anwenden lassen: Eigenschaften und Methoden Funktion Beschreibung CreateQueryDef erzeugt eine neue Abfrage. CreateTableDef erzeugt eine neue Tabelle. Execute führt eine Abfrage durch, soweit es sich nicht um eine Auswahlabfrage handelt. OpenRecordSet erzeugt eine neue Datensatzgruppe. QueryDefs liefert die Auflistung aller Abfragen der aktuellen Datenbank. TableDefs liefert die Auflistung aller Tabellen der aktuellen Datenbank. Im Folgenden werden die Methoden genauer vorgestellt, die für das in dieser Arbeit entwickelte Programm benötigt werden .Mit den Eigenschaften Querydefs und Tabledefs erhält man die Auflistungen der in der aktuellen Datenbank enthaltenen Abfragen und Tabellen. Für die Definition einer Abfrage ist ein SQL_String erforderlich. Der String wird dann als zweites Argument der CreateQueryDef-Methode übergeben. Im ersten Argument befindet sich der Name der Abfrage. Diese Methode ist sehr wichtig, da mit ihrer Hilfe Delta- und Transitionssichten angelegt werden (siehe Kapitel 5). 18 Kapitel 3 Abbildung 3.4 - Die DAO-Objekthierarchie[HS02] 3.5 Jet-SQL Die verschiedenen Datenbankhersteller haben die SQL-Normen meist nur teilweise in ihren Produkten umgesetzt. Fast alle haben dabei ihre Implementierung von SQL durch eigene Erweiterungen ergänzt, sodass sich die SQL-Varianten der einzelnen Produkte stark voneinander unterscheiden. Microsoft Access 2002 unterstützt ANSI-89 SQL sowie ANSI-92 SQL [KE01] und arbeitet standardmäßig mit Jet-SQL, also ANSI-89 SQL. Die Datenzugriffsschnittstelle DAO verwendet ANSI-89 SQL, ADO benutzt hingegen ANSI-92 SQL. In Access 2002 ist es auch möglich, den ANSI SQL-Abfrage-Modus zu wechseln und auf ANSI-92-SQL umzuschalten. Dazu muss man im Menü EXTRAS das Dialogfeld Optionen öffnen und 19 MS ACCESS - ein DBMS und JET-SQL _ auf dem Registerblatt „Tabellen/Abfragen“ unter „SQL-Server kompatible Syntax“ ein Häkchen vor „In dieser Datenbank benutzen“ setzen. Es ist aber nicht empfehlenswert, mit in unterschiedlichen Abfragemodi erstellten Abfragen gleichzeitig zu arbeiten, da hierdurch Laufzeitfehler oder unerwartete Ergebnisse auftreten können. In der Datenbank, die für diese Arbeit erstellt wurde, wird der Standardmodus ANSI-89 SQL belassen und auf die Datenbank mit Hilfe von DAO-Objekten zugegriffen. Im Folgenden wird auf die Anfragen/ Sichtdefinitionen in Jet-SQL genauer eingegangen, da sie für diese Arbeit von großer Bedeutung sind. Der grundlegendste und am häufigsten verwendete SQL-Ausdruck ist das SELECT-Statement. Das SELECT-Statement wird verwendet, um Daten aus einer Datenbank zu lesen. Das Resultat erscheint üblicherweise als eine Menge von Einträgen (Zeilen) mit einer variablen Anzahl von Spalten. Die Basisstruktur eines SELECT-Statements ist uns schon aus Kapitel 2.2.2 bekannt: SELECT [DISTINCT/DISTINCTROW/TOP n] {Feldname, Feldname,..*} FROM Tabelle [, Tabelle, Tabelle....] [WHERE {Bedingung}] [ORDER BY Feldname [ASC|DESC]...] [GROUP BY Feldname [HAVING {Bedingung}]]; In Access ist es möglich, Datensätze einer Abfrage, auch wenn diese auf mehreren Tabellen oder anderen Abfragen basiert, zu verändern. In vielen anderen SQLDatenbanken können immer nur Tabellen oder Abfragen bearbeitet werden, die auf nur einer Tabelle aufbauen. Microsoft nennt diese bearbeitbaren Abfragen Dynasets. Das Access-eigene Prädikat DISTINCTROW hat eine ähnliche Wirkung wie DISTINCT Der Unterschied zu DISTINCT besteht aber darin, dass mit DISTINCTROW immer ganze Datensätze verglichen werden. Mit dem DISTINCTROW-Prädikat werden Wiederholungen in Abfragen mit 1:N-Verknüpfungen unterdrückt. DISTINCTROW liefert im Gegensatz zu DISTINCT eine bearbeitbare Abfrage zurück, also ein Dynaset. Access-SQL verwendet das Schlüsselwort TOP, um die Anzahl der Ergebniszeilen zu beschränken. Standard-SQL verwendet hier im Gegensatz zu Access-SQL den Befehl LIMIT TO N ROWS. SQL kennt die logischen Operatoren AND (UND), OR (ODER) und NOT (NICHT), die in WHERE-Bedingungen verwendet werden können. Access-SQL erlaubt bis zu 40 ANDs in einer WHERE-Klausel. Um einen Mustervergleich auszuführen, benutzt man den Operator LIKE. Die von JetSQL verwendeten Platzhalterzeichen entsprechen nicht dem ANSI-SQL-Standard. Man kann zusätzlich anstelle von „%“ das Zeichen „*“, beziehungsweise „?“ für „_“ verwenden. Dies gilt aber nur, wenn man in ANSI-89 SQL arbeitet. Access besitzt außer den Standard-Aggregatfunktionen SUM, AVG, MIN, MAX und COUNT 20 Kapitel 3 noch folgende weitere: Aggregatfunktionen Funktion Beschreibung Spaltenwert der ersten Zeile des Ergebnisses, kann Null sein First([Spalte]) Spaltenwert der letzten Zeile des Ergebnisses Last([Spalte]) Stdev([Spalte]) Standardabweichung einer Stichprobe der Spaltenwerte Stdevp([Spalte]) Standardabweichung der Grundgesamtheit der Spaltenwerte Varianz der Stichprobe der Spaltenwerte Var([Spalte]) Varianz der Grundgesamtheit der Spaltenwerte Varp([Spalte]) JET-SQL kennt INNER und RIGHT /LEFT (OUTER) JOIN. Man kann bis zu 32 Tabellen mit Hilfe von JOINs miteinander verknüpfen. Dabei sollte man beachten, dass bei der Verknüpfung mehrerer Tabellen INNER JOINs und LEFT bzw. RIGHT JOINs nicht beliebig verschachtelt werden können. Es ist zwar möglich, einen LEFT oder RIGHT JOIN innerhalb eines INNER JOINs zu verwenden, aber nicht umgekehrt. Die klassischen Operatoren der Mengenlehre wie Durchschnitt oder Differenz kennt JetSQL nicht. Jet-SQL unterstütz nur UNION als einzigen Mengenoperator. Außerdem können Access-Abfragen Parameter enthalten. Parameter ermöglichen die Eingabe von Werten während der Auswertung einer SQL-Abfrage, ohne die Abfragedefinitionen zu verändern. Parameter können sowohl in der WHERE-Bedingung als auch in der HAVING-Klausel eingesetzt werden. SELECT A.name FROM a WHERE Parameter=a.name; Wird die Abfrage ausgeführt, erscheint das folgende Dialogfeld (Abbildung 3.5), um einen Wert für den Parameter entgegenzunehmen. Abbildung 3.5 Eine in Abfragen sehr hilfreiche Funktion ist IIF(). Diese Funktion besitzt drei Argumente: IIF(Bedingung, Wahr, Falsch). Falls die Bedingung erfüllt ist, wird der Wahr-Teil ausgeführt, ansonsten der Falsch-Teil. Darüber hinaus kennt Jet-SQL weder materialisierte noch rekursive Sichten. Ein Jet-SQLString darf nicht größer als 64 KB sein. Access erlaubt es, die SELECTs bis zu 50 Ebenen tief zu schachteln. In JET-SQL fehlt die Anweisung CREATE VIEW, das SELECT21 MS ACCESS - ein DBMS und JET-SQL _ Statement wird einfach in das Auswahlabfragefenster der entsprechenden Sicht geschrieben. 3.6 VBA (Visual Basic for Applications) VBA (Visual Basic for Applications) [Sta99] ist die objektorientierte Programmiersprache von Microsoft, die nicht nur in Access, sondern in allen Office-Anwendungen dem Anwender zur Verfügung steht. VBA erlaubt es dem Anwender nicht nur, Funktionen zu programmieren oder Berechnungen durchführen, sondern sogar Tabellen und Datenbanken anzulegen und zu manipulieren. Allerdings steht in Access kein MakroRecorder wie z.B. in Excel oder Word zur Verfügung, der Aktionen von Access aufzeichnen und damit das Entwerfen von Programmcode erleichtern würde. 3.6.1 Visual Basic-Entwicklungsumgebung Projekte, Formulare und Module Die Organisierung von VBA ist mit Access so geändert worden, dass sie derjenigen in Visual Basic als Einzelversion und in den anderen Office-Anwendungen gleicht. Das heißt: VBA ist in Projekten organisiert. Jedes Projekt enthält die Module und Klassenobjekte (Code-Module von Formularen und Berichten) der geöffneten Datenbank. VBA-Code wird als Text geschrieben und in Modulen abgelegt. Es gibt drei Arten von Modulen: Formular- und Berichtsmodule: Sie werden im Visual Basic-Editor als Klassenobjekte angezeigt. Alle Ereignisprozeduren eines bestimmten Formulars oder Berichts werden in einem Modul festgehalten, welches in das Formular bzw. den Bericht integriert ist. Prozeduren dieser Module können nicht von anderen Access-Objekten aus aufgerufen werden. Globale Standardmodule sind nicht an ein Formular oder einen Bericht gebunden. Globale Module werden zusammen mit der Datenbank gespeichert. Man kann alle benötigten Funktionen und Sub-Prozeduren in einem globalen Standardmodul hinterlegen. Davon wird aber abgeraten. Die Prozeduren eines Modul, die nicht explizit als privat deklariert sind, können von allen Prozeduren aus aufgerufen werden, egal in welchen Modulen sie sich befinden. Ein globales Standardmodul wird im Datenbankfenster durch das links daneben stehende Symbol gekennzeichnet. Globale Klassenmodule: In einem Klassenmodul werden benutzerdefinierte Objekte erstellt. Die Prozeduren des Klassenmoduls werden dabei zu Methoden und Eigenschaften des Objekts. Ein globales Klassenmodul wird im Datenbankfenster durch das links daneben stehende Symbol gekennzeichnet. 3.6.2 Objekte, Methoden, Eigenschaften Die meisten Access-Elemente können als Objekte betrachtet und angesprochen werden. Ein Beispiel für ein Objekt ist Form (Formular). Objekte besitzen Eigenschaften und Methoden, über die sie gesteuert werden. Beispielsweise hat ein Objekt Form die Eigenschaft Visible, welche wiederum verschiedene Einstellungen besitzen kann. Wird 22 Kapitel 3 diese Eigenschaft eines bestimmten Formulars auf den Wert False gesetzt, so wird das Formular ausgeblendet; durch die Eigenschaftseinstellung True wird es hingegen angezeigt. Die meisten Eigenschaften von Objekten lassen sich mit VBA verändern. Bestimmte Eigenschaften können aber nur gelesen werden. Objekte besitzen außer Eigenschaften auch Methoden. Die Methode eines Objekts führt im Allgemeinen eine Aktion aus. Beispielsweise besitzt das Objekt Form die Methode Requery. Diese Methode aktualisiert die zugrunde liegenden Daten des angegebenen Formulars durch erneutes Abfragen der Datenherkunft des Formulars. Das Objektmodell stellt gleiche Objekte als ein Objekt dar. Dieses Objekt wird Auflistungs-Objekt bezeichnet. So werden etwa alle geöffneten Berichte im Objekt Reports gesammelt (siehe Abbildung 3.4-1). Um auf ein bestimmtes Objekt des Auflistungs-Objekts zuzugreifen muss es hinter dem Namen des Auflistungs-Objekts angeben werden. Dabei kann dieses bestimmte Objekt dann durch seinen Namen oder über seine fortlaufende Nummer im Auflistungs-Objekt identifiziert werden. Es ist hier zu beachten, dass die fortlaufende Nummerierung der Objekte in einem AuflistungsObjekt mit 0 beginnt. Will man alle Objekte eines Auflistungs-Objekts ansprechen, so sollte man am besten eine For Each ... Next-Schleife verwenden. 3.6.3 VBA-Grundlagen Variablen können implizit oder explizit deklariert werden. Bei expliziter Deklaration kann der Datentyp, der Gültigkeitsbereich sowie die Lebensdauer der Variablen festgelegt werden. Variablen werden explizit mit einer der Anweisungen Dim, Public, Private oder Static deklariert. Alle implizit oder explizit in einer Prozedur deklarierten Variablen gelten nur für diese Prozedur – es sind lokale Variablen. Damit eine Variable für alle Prozeduren desselben Moduls (aber nicht für andere Module) sichtbar ist, muss sie im Deklarationsteil des Moduls mit der Anweisung Dim deklariert werden. Auf Variablen kann von allen Prozeduren und allen Modulen aus zugegriffen werden, wenn sie im Deklarationsteil eines Moduls mit der Anweisung Public deklariert werden. Der Gültigkeitsbereich hat Einfluss auf die Lebensdauer. Lokale Variablen verlieren ihre Werte, sterben also, wenn die Prozedur, in der sie deklariert sind, beendet ist. Variablen mit einem modularen oder öffentlichen Gültigkeitsbereich bleiben dagegen erhalten, bis die Datenbank geschlossen wird oder ein Laufzeitfehler auftritt. Man kann die Lebensdauer einer lokalen Variablen verlängern, indem man sie als Static deklariert. Die Variable wird so lange gültig bleiben, wie die Datenbank geöffnet ist. Den Konstanten wird bei der Deklaration ein Wert zugewiesen. Sie werden durch das Schlüsselwort Const als Konstante deklariert. Programme in VBA werden entweder als Funktionen oder als Sub-Prozeduren geschrieben. Die Syntax der Sub- und Function-Anweisungen lautet: [Public | Private] [Static] Sub Prozedurname [(Argumentliste)] [Public | Private] [Static] Function Prozedurname [(Argumentliste)] As Typ 23 MS ACCESS - ein DBMS und JET-SQL _ Soll eine Prozedur von allen Prozeduren aus aufgerufen werden können – egal in welchen Modulen sie sich befinden –, so muss man sie als Public deklarieren. Prozeduren werden aber auch implizit als öffentlich deklariert. Eine Ausnahme bilden die Ereignisprozeduren, die automatisch als privat deklariert werden. Als Private deklarierte Prozeduren lassen sich nur aus Prozeduren des gleichen Moduls heraus aufrufen. Unabhängig davon, ob eine Prozedur als Public oder als Private deklariert ist, darf sie in jedem Fall zusätzlich auch als Static deklariert werden. Dies hat zur Folge, dass die Inhalte aller lokalen Prozedurvariablen bis zum nächsten Prozeduraufruf erhalten bleiben und nicht wie sonst nach dem Beenden der Prozedur gelöscht werden. Funktionen und Prozeduren unterscheiden sich in den folgenden Punkten: • • Funktionen können einen Wert ausgeben, Prozeduren dagegen nicht. Funktionen können auf allen Access-Ebenen, etwa in Formularen, aufgerufen werden, Prozeduren dagegen nur von VBA-Prozeduren. VBA kennt Schleifen und Verzweigungen. Die Schleifen werden durch Do ... Loop, While ... Wend und For [Each] ... Next realisiert. Für die Verzweigungen existieren If... Then und Select Case ... End Select. 24 Kapitel 4 4 Deduktive Datenbanken Dieses Kapitel gibt eine allgemeine Einführung in das Thema Deduktive Datenbanken mit der Anfragesprache Datalog und geht detailliert auf die Problematik der Änderungspropagierung ein. Die Darstellung orientiert dabei sich an der Vorlesung „Deduktive Datendanken“ von Professor Dr. Rainer Manthey [Man01]. Hierbei werden Beispiele aus einer speziell für diese Diplomarbeit erstellten Datenbank überall dort eingesetzt, wo dies für das Verständnis und die Veranschaulichung der wichtigsten Vorgänge erforderlich ist. 4.1 Deduktive Datenbanken Bei deduktiven Datenbankensystemen ([Man00], [KE01]) Erweiterung eines im Prinzip beliebigen Datenmodells um deduktive und normative Regeln. Deduktive Regeln Sichtdefinitionen, normative Regeln (Integritätsbedingungen) SQL. handelt es sich um eine ein Regelkonzept. Es gibt entsprechen dabei den hingegen den Assertions in In dieser Arbeit versuche ich, einige der Datalog-Konzepte in eine MS Access Datenbank zu implementieren. Da Access eine relationale Datenbank ist, die das Assertions-Konzept nicht unterstützt, werden hier nur deduktive relationale Datenbanken und keine normativen Datalog-Regeln betrachtet. Eine deduktive Regel wird durch einen deklarativen Ausdruck spezifiziert und beschreibt durch eine allgemeine Gesetzmäßigkeit eine Menge von impliziten (intensionalen, ableitbaren) Daten (IDB), welche aus einer Menge expliziter (extensionaler, Basis-) Daten (EDB) herleitbar sind. Die Anfragesprache für deduktive Datenbanken heißt Datalog – abgeleitet vom Wort "Data" sowie dem Namen der Logikprogrammiersprache Prolog. Es gibt jedoch auch bei herkömmlichen relationalen Datenbanken mit der Abfragesprache SQL eine Analogie zu EDB und IDB: • • Die Basisrelationen entsprechen der EDB. Die in SQL mittels des CREATE VIEW-Konstruktes definierten Sichten entsprechen der IDB. 25 Deduktive Datenbanken _ 4.2 Datalog Datalog basiert auf einem relationalen Bereichskalkül (DRC) [KE01]. Die Syntax von Datalog-Regeln ist stark von der logischen Programmiersprache Prolog beeinflusst. Obwohl die Semantik von Prolog instanzenorientiert ist, verfügt sie jedoch auch wie andere DB-Sprachen über eine mengenorientierte Semantik. 4.2.1 Grundlagen Die Grundbausteine der deduktiven Regeln sind atomare Formeln (auch Literale genannt), die folgende Gestalt haben: q(A1, ..., Am). q ist entweder der Name einer Basisrelation (EDB-Relation), der Name einer abgeleiteten (IDB-)Relation oder ein eingebautes Prädikat (≠, >, <, <= usw.), auch Vergleichsliteral genannt [Man00]. Bei den eingebauten Vergleichsprädikaten benutzt man häufig die gebräuchlichere Infixnotation, also X<Y anstatt <(X,Y). Bei Ai(1<=i <=m) handelt es sich entweder um Variablen oder Konstanten. Variablen sind Buchstaben oder Zeichenreihen, die in Anlehnung an die PrologKonventionen mit einem Großbuchstaben beginnen. Konstanten sind Ziffern, Kleinbuchstaben oder Zeichenreihen, die mit Kleinbuchstaben oder Ziffern beginnen. Relationsnamen sind Zeichenreihen, die mit Kleinbuchstaben beginnen. Fakten werden als atomare Formeln dargestellt, deren Parameter lauter Konstanten sind. Eine Datalog-Regel hat folgende Form: p(X1,…,Xm) ← q1(A11,…,A1m1),..,qn(An1,..,Anmn). Es muss hierbei gelten: • Jedes qj(...) ist eine atomare Formel. Die qj werden oft auch als Subgoals bezeichnet. • X1,…,Xm sind Variablen, die mindestens einmal auch auf der rechten Seite des Zeichens ← vorkommen müssen. Den linken Teil der Regel, also p(...), bezeichnet man als den Kopf (Engl. head) und den rechten Teil als den Rumpf (Engl. body). Diese Form von Regeln bezeichnet man auch als Horn-Klausel. Die Bedeutung dieser Regel ist: Wenn q1(...) und q2(...) und ... und qn(...) wahr sind, dann ist auch p(...) wahr. Man kann die oben vorgestellte Formel auch wie folgt schreiben: p(...) ∨ ¬ q1( A11,…A1m1 ) ∨ ... ∨ ¬ qn( An1,..Anmn ). 26 Kapitel 4 Eine IDB-Relation p wird im Algemeinen durch mehrere Regeln mit dem Kopf p(...)←... definiert. Die EDB-Relation kommt dabei nicht auf der linken Seite einer Regel, sondern nur als Subgoal auf der rechten Seite vor, welche die IDB-Relation definiert. IDBRelationen können aber durchaus – sogar rekursiv – aufeinander aufbauen, so dass sie auch als Subgoals auf der rechten Seite einer Regel auftreten können. Mit Hilfe des so genannten Abhängigkeitsgraphen kann man feststellen, ob eine DatalogRegel rekursiv ist. Hierfür wird für jede in der Datalog-Regel vorkommende Relation ein Knoten des Graphen gebildet. Die Vergleichsprädikate (wie ≠, >, <, <= usw.) können dabei vernachlässigt werden. Für jede Regel der Form p(...)← q1(...),....,qn(...) werden n Kanten der Form q1→ p ,..., qn→ p eingefügt – falls sie nicht schon vorhanden waren. Eine Datalog-Regel ist genau dann rekursiv, wenn der Abhängigkeitsgraph zyklisch ist – falls also mindestens ein Zyklus vorhanden ist. Um das Pendant zur Mengendifferenz in der Relationenalgebra in Datalog ausdrücken zu können, benötigt man die Negation der Prädikate im Regelrumpf. Eine Regel mit einem negierten Prädikat im Rumpf ist: p(...) ←q1(...),...,not qi(...),...,qn(...). Dabei geht man von der Annahme aus, dass ein Fakt, der weder in der Datenbank gespeichert noch durch die Regeln von den Basisfakten abgeleitet werden kann, den Wahrheitswert false hat. Alle ableitbaren sowie in der Datenbank gespeicherten Fakten haben hingegen den Wahrheitswert true. Diese Annahme wird „close world assumption“ (CWA) genannt. Das dazugehörige Auswertungsprinzip für negative Literale heißt „Negation as failure“. Das heißt, ein negierter Fakt hat nur dann den Wahrheitswert true, wenn der entsprechende positive Fakt weder in der IDB noch in der EDB enthalten ist. Ein weiterer wichtiger Aspekt, den man hier unbedingt erwähnen sollte, ist die Sicherheit von Datalog-Regeln. Es gibt unsichere Regeln, wie z.B.: ungleich(X, Y) ← X ≠ Y, die unendliche Relationen definieren. Es gibt unendlich viele Paare, die ungleich sind. Eine Datalog-Regel ist nur dann sicher, wenn alle Variablen eingeschränkt sind. Eine Variable X ist in einer bestimmten Regel eingeschränkt, falls • die Variable im Rumpf der Regel in mindestens einem positiven Literal – also nicht nur in eingebauten Vergleichsprädikaten – vorkommt, • ein Prädikat der Form X = c mit einer Konstante c im Rumpf der Regel existiert; • ein Prädikat der Form X = Y im Rumpf vorkommt, und man bereits nachgewiesen hat, dass Y eingeschränkt ist. Aus der oben gegebenen Definition einer sicheren Regel folgt auch, dass eine Regel mit Negation nur dann sicher ist, wenn alle Variablen aus einem negierten Literal auch in mindestens einem positiven Literal vorkommen 27 Deduktive Datenbanken _ 4.2.2 Auswertung von Datalog-Regeln Auswertung von nicht-rekursiven Datalog-Programmen Wie geht man systematisch vor, um IDB-Relationen herzuleiten? Die Grundidee besteht darin, für den zyklenfreien Abhängigkeitsgraphen eine topologische Sortierung der Knoten zu finden, so dass in der Sortierreihenfolge der Knoten q vor einem Knoten p steht, falls es im Abhängigkeitsgraphen eine (gerichtete) Kante von q nach p (also q→p) gibt. IBD-Relationen werden dann in der Reihenfolge der topologischen Sortierung hergeleitet (materialisiert). Auswertung rekursiver Regeln Die Grundidee bei der Auswertung von rekursiven Regeln besteht darin, die IDBRelationen schrittweise zu bestimmen. Man fängt dabei mit leeren IDB-Relationen an und generiert sukzessive neue Tupel dafür. Bei der Herleitung neuer Tupel werden Tupel verwendet, die in vorhergehenden Schritten generiert wurden. Der Vorgang wird abgebrochen, sobald sich keine neuen Tupel mehr generieren lassen. Formal gesehen wird bei dieser Vorgehensweise auf der Basis gegebener EDB-Relationen der kleinste Fixpunkt für die IDB-Relationen bestimmt. Eine Regel mit einem negierten Prädikat im Rumpf, wie z.B.: r ≡ p(...) ←q1(...),...,not qi(...),...,qn(...). kann nur dann sinnvoll ausgewertet werden, wenn alle Regeln mit dem Kopf qi(...)←... schon vorher ausgewertet wurden. Dies ist aber nur möglich, wenn qi nicht abhängig vom Prädikat p ist und der Abhängigkeitsgraph keinen Pfad von p nach qi enthält. Wenn dies für alle Regeln und alle negierten Subgoals (Literale im Rumpf) gewährleistet ist, so ist die gegebene Regelmenge stratifizierbar. Die Bezeichnung "stratifizieren" entstammt dem lateinischen Wort strata (Schicht). Demnach werden die Regeln bei dem Vorgang in verschiedene Schichten unterteilt, sodass die Negation sich stets auf die Relation der tieferen Ebene bezieht. Jetzt lässt sich die iterierte Fixpunktberechnung für jede Schicht einzeln durchführen. Die Schichten werden dabei von unten nach oben abgearbeitet. Hier werden nun formale Definitionen für eine deduktive Datenbank und deren Semantik gegeben [Man00]: Definition 4.1( Deduktive Datenbank) Eine deduktive Datenbank ist ein Tupel D = <F,R> mit F – eine endliche Menge der Basisfakten und R – eine endliche Regelmenge. Definition 4.2 (Ableitungsoperator) Sei D = <F,R> eine deduktive Datenbank mit F – eine endliche Faktenmenge und R – eine endliche, nichtleere Regelmenge mit 28 Kapitel 4 Ri ≡ A ← B1,…,Bn, not C1,…,not Cm. Dann ist die Menge der durch R aus F ableitbaren Fakten durch den Ableitungsoperator T gegeben: T[Ri](F ) := { Aσ | σ ist eine konsistente Variablensubstitution, so dass ∀ 1 ≤ j ≤ n: Bj σ ∈ F und ∀ 1 ≤ k ≤ m: Ck σ ∈ F gilt }, wobei F eine Faktenmenge ist mit F ≠ F. 2. T[R](F ) := ∪ T[Ri](F ) ist das Resultat der Anwendung aller Regeln der 1. Ri∈R Regelmenge R auf dieselbe Menge F. 3. T*[R] (F ) := T[R](F ) ∪ F ist das Resultat der Anwendung mehrstufiger Ableitungsregeln, wobei die jeweilige Inputmenge auch ein Teil der Inputmenge der nächsten Sicht ist. Unter einer konsistenten Variablensubstitution versteht man die Ersetzungen aller Variablen durch Konstanten, so dass jedes Vorkommen derselben Variablen durch dieselbe Konstante ersetzt wird. Definition 4.3 ( Stratifikation) Sei D = <F,R> eine deduktive Datenbank mit F – eine endliche Faktenmenge und R – eine endliche, nichtleere Regelmenge . Unter einer Stratifikation λ bezüglich der Regelmenge R versteht man eine Abbildung der Menge der Relationsnamen der Datenbank D RelD auf die Menge der natürlichen Zahlen (Schichtnummern) IN0 : λ: RelD→ IN0. Dabei muss gelten: p eine Basisrelation ⇔ λ(p) = 0 p eine abgeleitete Relation ⇔ λ(p) ≥ 1 p,q ∈ RelD und p hängt positiv von q ab ⇔ λ(q) ≤ λ(p) p,q ∈ RelD und p hängt negativ von q ab ⇔ λ(q) < λ(p). Eine deduktive Datenbank heißt stratifizierbar, wenn mindestens eine Stratifikation von Relationen der Datenbank existiert. Eine deduktive Datenbank heißt nichtstratifizierbar, wenn keine Stratifikation von Relationen der Datenbank existiert. Definition 4.4 (Fixpunksemantik deduktiver Datenbanken) • Ist = <F,R> eine (semi-) positive Datenbank. Dann ist die Bedeutung von D der kleinste Fixpunkt F* := lfp(T*[R] (F) ) • Ist D = <F,R> eine stratifizierbare Datenbank und {R1,…,Rm} eine Stratifikation von D. Dann ist die Bedeutung von D der kleinste Fixpunkt F* := Fm, wobei gilt: F0:= F Fi := lfp(T*[Ri] (Fi-1) ) mit 1 ≤ i ≤ m 29 Deduktive Datenbanken _ Wobei Lfp(F, M) („least fix point“) für den kleinsten Fixpunkt einer Funktion f steht, der Argument M enthält. Bottom-Up oder Top-Down Auswertung Die beschriebene Auswertungsmethode für Datalog-Programme wird auch Bottom-Up Auswertung genannt, weil man, ausgehend von den EDB-Relationen (also der Faktenbasis), die gesamte intensionale Datenbasis in einem Stück berechnet. Diese Methode der Auswertung hat allerdings einen Nachteil: Wenn man nur an einem kleinen bestimmten Teil der IDB-Relation(en) interessiert ist, wird bei der Bottom-Up Auswertung trotzdem und unnötigerweise die gesamte intensionale Datenbasis berechnet und erst danach der relevante Teil aus der IDB extrahiert. Dieser Nachteil wird bei der so genannten Top-Down Auswertung vermieden. Um das Ziel zu beweisen, geht man dabei von der Anfrage aus und geht rückwärts zu den Fakten der EDB. Eine nicht weniger effektive Vorgehensweise für die Datalog-Anfragenbearbeitung bietet die Magic Sets Methode [BMS86] an. Ein nicht zu verkennender Punkt ist die Ausdruckskraft des Datalogs. Die Sprache Datalog, beschränkt auf nicht-rekursive Programme, dafür aber erweitert um die Negation, hat die gleiche Ausdruckskraft wie die relationale Algebra. Mit Negation und Rekursion geht Datalog sogar über die Ausdruckskraft der relationalen Algebra hinaus. Beispiel 4.1 So sehen die Sichten aus dem Beispiel 2.2 deutscher_Bücher und deutsche_Belletristik aus, niedergeschrieben in Form einer Datalog-Regel: deutscher_Bücher(Autor,Titel,Art) ← bücher(Autor,Titel,Art), autor(Autor,“Deutschland“). deutsche_Belletristik[Autor,Titel,Art]← deutscher_Bücher[Autor,Titel,Art], not fachliteratur[Autor,Titel]. 4.3 Änderungspropagierung Eine deduktive Datenbank enthält virtuelle und materialisierte (also gespeicherte) Sichten. Virtuelle Sichten werden für jeden Zugriff neu berechnet. Bei materialisierten Sichten wird der Inhalt in der Datenbank gespeichert. Diese Sichten werden bei Erstellung einmal berechnet und gespeichert. Auf sie kann man jederzeit zugreifen, ohne sie jedes Mal neu zu berechnen. Zugriffe auf materialisierte Sichten sind somit, aufgrund der ausbleibenden Neuberechnung, effizienter als Zugriffe auf virtuelle Sichten. 4.3.1 Motivation Durch Änderungsoperationen auf der Datenbank können sich die Einträge einer Sicht ändern. Im Falle von virtuellen Sichten stellt dies kein Problem dar, da die Sichten bei jedem Zugriff neu berechnet werden. Bei den materialisierten Sichten ergibt sich jedoch 30 Kapitel 4 manchmal die Schwierigkeit, da die gespeicherten Daten nicht mehr der aktuellen Datenbasis entsprechen und deshalb aktualisiert werden müssen. Eine naive Möglichkeit der Aktualisierung ist die Rematerialisierung der Sichten. Dabei wird der gesamte Inhalt der materialisierten Sicht gelöscht und dann neu berechnet. Wenn sich nur ein kleiner Teil der Daten geändert hat, dann ist die Rematerialisierung sehr ineffizient. Wenn die Änderungen im Verhältnis zu den enthaltenen Daten insgesamt relativ gering ausfallen, so ist die inkrementelle Anpassung hingegen viel effizienter. Bei dieser wird zunächst festgestellt, wie sich die Änderungen von Basisdaten auf die materialisierten Sichten auswirken und nur noch die induzierten Änderungen der materialisierten Sichten berechnet. Änderungspropagierung ist somit ein Prozess der „Fortschreibung“ von Änderungen an Basisrelationen auf die davon abhängigen abgeleiteten Relationen. 4.3.2 Delta-Regeln Unabhängig von den gegebenen Basisfakten und deren Änderungen werden die Propagierungsregeln (auch Delta-Regeln genannt) aus den gegebenen deduktiven Regeln erstellt. Wie schon in Abschnitt 3.1 erwähnt, werden in dieser Arbeit nur deduktive Regeln betrachtet. Die erstellten Propagierungsregeln sind die so genannten „Delta-Relationen". Die durch Delta-Regeln definierten Relationen (Delta-Relationen) stellen die echten Faktenänderungen der abgeleiteten Relationen, die durch die Basisfaktenänderungen verursacht wurden. Ein Fakt wird als echte Einfügung betrachtet, wenn er im neuen, aber nicht im alten Datenbankzustand ableitbar ist. Analog dazu wird ein Fakt echt aus der aktuellen Datenbank gelöscht, wenn er im alten Zustand ableitbar war, jedoch im jetzigen, neuen Zustand nicht mehr ableitbar ist. Definition 4.5(Propagierungsregeln für echte Änderungen) Sei R eine stratifizierbare deduktive Regelmenge. Für jede Regel A ← L1, ... , Ln aus R und für jedes Rumpfliteral Li mit i in [1,...,n] haben die positiven und negativen Propagierungsregeln folgende Form: A+ ← L+i, new(L1,.., Li-1, Li+1,.., Ln), not Aold mit Li≡L A+ ← L-i, new(L1,.., Li-1, Li+1,.., Ln), not Aold mit Li≡not L A- ← L-i, old(L1,.., Li-1, Li+1,.., Ln), not Anew mit Li≡L A- ← L+i, old(L1,.., Li-1, Li+1,.., Ln), not Anew mit Li≡not L Aus der Tatsache, dass für jedes Rumpfliteral eine Delta-Plus-Regel und eine DeltaMinus-Regel erstellt wird, ergibt sich der Fakt, dass für eine Regel mit n Rumpfliteralen insgesamt 2n Delta-Regeln existieren. 31 Deduktive Datenbanken _ Es kann passieren, dass durch die Delta-Regeln einige schon bekannte Fakten noch einmal hergeleitet werden. Dabei versteckt sich die Quelle der Fehler stets in einer Konstellation, bei der Duplikate bei den Ableitungen entstehen und damit Mehrfachableitungen ein und desselben Fakts möglich werden. Um solche Mehrfachableitungen, die schon vor der Einfügung existiert haben können, zu entdecken und damit eine fehlerhafte Herleitung von Deltafakten zu vermeiden, ist es erforderlich, in die Deltaregeln zusätzlich einen Test auf Ableitbarkeit einzubauen. Der Effektivitätstest auf Aold bzw. Anew prüft nach, ob der durch die Delta-Regeln gewonnene Fakt nicht im alten bzw. neuen Datenbankzustand alternativ herleitbar bzw. überhaupt noch herleitbar ist. Diese Maßnahme ist aber nur dann erforderlich, wenn die jeweilige Relation durch mehrere Regeln definiert ist (Vereinigung) oder wenn sie definierende Regeln lokaler Variablen enthält (Projektion). Um unseren Algorithmus für die Herleitung der Delta-Regeln zu vereinfachen, wird ein Effektivitätstest in jede Delta-Regel eingebaut. Beispiel 4.2- (Kodierung eines Propagierungsschritts mittels Delta-Regeln, Delta-Regeln für Relationen aus dem Beispiel 4.1) deutscher_Bücher+[Autor,Titel,Art] ← bücher+[Autor,Titel,Art], autorennew[Autor,“Deutschland“], not deutscher_Bücherold[Autor,Titel,Art] deutscher_Bücher+[Autor,Titel,Art]← büchernew[Autor,Titel,Art], autoren+[Autor,“Deutschland“], not deutscher_Bücherold[Autor,Titel,Art] deutscher_Bücher-[Autor,Titel,Art]← bücher -[Autor,Titel,Art], autorenold[Autor,“Deutschland“], not deutscher_Büchernew[Autor,Titel,Art] deutscher_Bücher-[Autor,Titel,Art]← bücherold[Autor,Titel,Art], autoren-[Autor,“Deutschland“], not deutscher_Büchernew[Autor,Titel,Art] deutsche_Belletristik +[Autor,Titel,Art]← deutscher_Bücher+[Autor,Titel,Art], not fachliteraturnew[Autor,Titel], not deutsche_Belletristikold[Autor,Titel,Art] deutsche_Belletristik +[Autor,Titel,Art]← deutscher_Büchernew[Autor,Titel,Art], fachliteratur-[Autor,Titel], not deutsche_Belletristikold[Autor,Titel,Art] deutsche_Belletristik -[Autor,Titel,Art]← deutscher_Bücher -[Autor,Titel,Art], 32 Kapitel 4 not fachliteraturold[Autor,Titel], not deutsche_Belletristiknew[Autor,Titel,Art] deutsche_Belletristik -[Autor,Titel,Art]← deutscher_Bücherold[Autor,Titel,Art], fachliteratur+[Autor,Titel], not deutsche_Belletristiknew[Autor,Titel,Art] 4.3.3 Transitionsregeln Die im vorigen Abschnitt behandelten Propagierungsregeln für echte Änderungen haben den Nachteil, dass für die Herleitung der Delta-Relationen sowohl der alte als auch der neue Datenbankzustand notwendigerweise vorhanden sein muss. Bei Einfügungen werden Effektivitätstests im "alten" Zustand der DB ausgeführt (vor der Ausführung der jeweiligen Änderungen). Die Effektivitätstests bei Löschungen werden hingegen im "neuen" Zustand ausgeführt (nach den Änderungen). Sollte man beides vorhaben, also Einfügungen und Löschungen gleichermaßen benötigen, so müssten beide Zustände gleichzeitig zur Verfügung stehen. Dieses Problem löst man, indem man sich dazu entscheidet, einen der benötigen Datenbankzustände zu simulieren. "Simulieren" bedeutet dabei, dass zunächst für jede Basisrelation der neue Zustand durch eine abgeleitete Hilfsrelation definiert wird. Der Vorteil einer solchen „Simulation" ist, dass der aktuelle Zustand dann vollkommen ausreicht. Die deduktiven Regeln, welche für diese Simulation von Zuständen gebildet werden, nennt man Transitionsregeln. Die Entscheidung darüber, welcher Zustand simuliert wird, ist eine Designfrage und hängt von der gegebenen Datenbank und ihren Restriktionen in Form von Integritätsbedingungen ab. Die Simulation des alten Zustands bezeichnet man als „optimistischen" Ansatz. Der optimistische Ansatz trägt die gewünschten Basisdatenänderungen direkt in die Datenbank ein. Dabei nimmt man an, dass die danach berechneten Folgeänderungen keine Integritätsbedingungen der Datenbank verletzen. Im Falle von Verletzungen muss ein Rollback durchgeführt werden. Die Simulation des neuen Zustands wird dagegen als „pessimistischer" Ansatz bezeichnet. Der pessimistische Ansatz trägt die gewünschten Basisdatenänderungen nicht in die Datenbank ein. Die Änderungen der Basisdaten und abgeleiteten Relationen werden erst dann in die Datenbank übertragen, wenn man sicher ist, dass sie keine der vorhandenen Integritätsbedingungen verletzen. Im Falle von Verletzungen werden die berechneten Änderungen dann verworfen. Da in dieser Arbeit versucht wird, die Änderungspropagierung in den SQL-Kontext am Beispiel der MS Access Datenbank zu übertragen, und Access solche Restriktionen wie die referentielle Integrität unterstützt, werden dort die möglichen Integritätsverletzungen bei den Datenänderungen erwartet. Somit wird schon an dieser Stelle entschieden, den neuen Zustand zu simulieren. Sei U eine Transaktion, dann erhält man Fnew (neuer Datenbankzustand) aus Fold (alter Datenbankzustand), auf die man die Transaktion U angewendet hat (Abbildung 4.1). 33 Deduktive Datenbanken _ Abbildung 4.1 [Man00] Dabei enthält F+ alle Einfügungen durch U, während sich in F- alle Löschungen mittels U befinden, und es gilt: Alle Änderungen sind echt: F+ ∩ Fold = ∅ F- ∩ Fold = FNettoeffekte sind bereits bestimmt: = ∅ F+ ∩ FDann besteht folgender Zusammenhang: Fnew = (Fold –F-) U F+. Diese Gleichung auf der Faktenebene für die Basisrelation Autoren (Autor, Land) aus Beispiel 4.2 kann man wie folgt darstellen: autorennew (Autor, Land) ← autorenold (Autor, Land), not autoren- (Autor, Land). autorennew (Autor, Land) ← autoren+ (Autor, Land) Diese beiden Transitionsregeln in Bezug auf die ursprüngliche Basisrelation nennt man auch inkrementelle Transitionsregeln. Der neue Zustand Autorennew (Autor, Land) wird aus dem alten Zustand Autorenold (Autor, Land) gebildet, indem die zu löschenden Fakten Autoren- (Autor, Land) vom alten Zustand abgezogen werden. Zusätzlich sind durch die Vereinigung im neuen Zustand die einzufügenden Fakten aus Autoren+ (Autor, Land) enthalten. Basierend auf den Annahmen, dass erstens immer der neue Zustand der Datenbank simuliert wird, und zweitens der aktuelle materialisierte Zustand der Datenbank der alte ist, werden im Folgenden die Kennzeichnungen der alten Zustände in den Prädikatssymbolen weggelassen. Verwendet man die inkrementellen Transitionsregeln für die abgeleiteten Relationen, so sieht es für die Relation deutsche_Belletristik[Autor,Titel,Art] samt den Delta-Regeln folgendermaßen aus: 34 Kapitel 4 deutsche_Belletristiknew[Autor,Titel,Art] ← deutsche_Belletristik[Autor,Titel,Art], not deutsche_Belletristik - [Autor,Titel,Art] deutsche_Belletristiknew[Autor,Titel,Art]← deutsche_Belletristik+[Autor,Titel,Art] deutsche_Belletristik +[Autor,Titel,Art]← deutscher_Bücher+[Autor,Titel,Art], not fachliteraturnew[Autor,Titel], not deutsche_Belletristik[Autor,Titel,Art] deutsche_Belletristik +[Autor,Titel,Art]← deutscher_Büchernew[Autor,Titel,Art], fachliteratur -[Autor,Titel], not deutsche_Belletristik[Autor,Titel,Art] deutsche_Belletristik -[Autor,Titel,Art]← deutscher_Bücher -[Autor,Titel,Art], not fachliteratur[Autor,Titel], not deutsche_Belletristiknew[Autor,Titel,Art] deutsche_Belletristik -[Autor,Titel,Art]← deutscher_Bücher [Autor,Titel,Art], fachliteratur+[Autor,Titel], not deutsche_Belletristiknew[Autor,Titel,Art] In diesem Beispiel ist zu erkennen, dass die alleinige Verwendung von inkrementellen Transitionsregeln zu resultierenden, nichtstratifizierbaren Regeln führt: deutsche_Belletristik -[Autor,Titel,Art]← …, not deutsche_Belletristiknew[Autor,Titel,Art] deutsche_Belletristiknew[Autor,Titel,Art] ← …, not deutsche_Belletristik[Autor,Titel,Art] Die Relation deutsche_Belletristik- ist, über den negativ referenzierten Effektivitätstest not deutsche_Belletristiknew und die inkrementelle Transitionsregel, von sich selbst negativ abhängig. Glücklicherweise gibt es auch noch einen anderen Weg, Transitionsregeln für abgeleitete Relationen zu definieren – der neue Zustand des Kopfliterals ergibt sich dabei einfach aus den neuen Zuständen der Rumpfliterale. 35 Deduktive Datenbanken _ Definition 4.6 (Transitionsregeln für echte Änderungen) Sei R eine stratifizierbare deduktive Regelmenge, dann haben die Transitionsregeln für echte Änderungen bezüglich der Simulation des neuen Zustands folgende Form: 1.(inkrementelle Transitionsregeln) Transitionsregeln der Form: Für jede Basisrelation A gibt es zwei Anew ← A, not A –. Anew ← A +. 2. (naive Transitionsregel) Für jede abgeleitete Regel A ← L1, ... , Ln ∈ R gibt es eine Transitionsregel der Form: Anew ← Lnew1, ... , L new n Naive Transitionsregeln für die Relationen aus dem Beispiel 4.2: deutscher_Büchernew[Autor,Titel,Art]← büchernew [Autor,Titel,Art], autorennew [Autor,“Deutschland“] deutsche_Belletristiknew[Autor,Titel,Art]← deutscher_Büchernew[Autor,Titel,Art], not fachliteraturnew[Autor,Titel] Als Letztes sei noch gesagt, dass es mehrere Möglichkeiten gibt, Propagierungsregeln auszuwerten. Eine sehr effiziente Vorgehensweise stellt dabei die „Magic Updates"Methode [Man00] dar. 36 Kapitel 5 5 Änderungspropagierung in SQL Ähnlich wie in Datalog, führen auch Basisfaktenänderungen in SQL zu induzierten Sichtänderungen. In kommerziellen Datenbanken wie MS Access muss man normalerweise die Sicht komplett neu berechnen, um die induzierten Änderungen festzustellen. Die SQL-Sichtdefinitionen weisen eine gewisse Äquivalenz zu den Datalogregeln auf. Mit diesem Kapitel wird der Übergang von der Theorie zur Praxis vollzogen und gezeigt, wie mithilfe der Kenntnisse über Datalog-Propagierungsregeln eine Propagierungssicht in SQL erstellt werden kann. Die Hauptaufgabe dieser Arbeit ist die Konzeption und Programmierung einer Access-Datenbankerweiterung, welche die Sichtenänderungen zu bestimmen vermag, ohne die Sicht komplett ausrechnen zu müssen. Eine Vorgehensweise, bei der die SQL-Sichten erst in Datalog umgewandelt und dann, ausgestattet mit den Propagierungsregeln, in die SQL-Umgebung zurückgeholt werden, ist aber mühsam und uneffektiv. Daher wäre es nützlich, einen Algorithmus zu entwickeln, der die Delta-Sichten für die SQL-Sichten erstellt, ohne dass diese in eine datalogähnliche Form überführt werden müssen. Der Ausdruck "Delta-Sicht" im Kontext mit sichtbasierter Änderungspropagierung entspricht in dieser Arbeit dem Begriff "Deltaregel" im Datalogkontext. In diesem Kapitel wird die Theorie erklärt, die hinter dem Compiler steht. Abschnitt 5.1 stellt das Prinzip der Änderungspropagierung in einer SQL-Umgebung vor. In den Abschnitten 5.2 und 5.3 werden die Schwierigkeiten aufgeführt, die bei einer Änderungspropagierung in SQL auftreten können. In Abschnitt 5.4 wird das SQLspezifische Konzept der Aggregatfunktionen bei der Änderungspropagierung erläutert. In Abschnitt 5.5 wird ein eigens für diese Arbeit erstellter Algorithmus zur Bildung der SQL-Propagierungssichten formuliert und an einem komplexeren Beispiel erklärt. 5.1 Das Prinzip Zunächst soll hier versucht werden, Delta-/Transitionssichten für eine einfache SQL-Sicht zu entwickeln. Dabei geht man – in Anlehnung an die Datalog-Delta-/Transitionsregeln – intuitiv vor. Später werden die aus einfachen Beispielen gewonnenen Erkenntnisse auf komplexere Beispiele übertragen. 37 Änderungspropagierung in SQL _ Beispiel 5.1 (Die Sicht q ) In SQL SELECT a.Name FROM a, b, c WHERE a.Name=b.Name AND a.Name=c.Name; Die Sicht q in Datalog: q(y) ← a(y),b(y),c(y). In diesem Beispiel wird ein neuer Datenbankzustand nach der "pessimistischen" Methode simuliert. Wie bereits beschrieben, wird die Methode immer dann eingesetzt, wenn mögliche Integritätsverletzungen erwartet werden, und davon ausgegangen wird, dass die Änderungen wahrscheinlich gar nicht anwendbar sind. Für die relevanten Basisrelationen (Tabellen) (hier a, b und c) werden der Datenbank Hilfstabellen mit den Schlüsselwörtern Plus und Minus im Namen hinzugefügt (a_Plus, a_Minus, b_Plus, b_Minus, c_Plus, c_Minus). Dies sind Tabellen, bei denen man die Änderungen – in Plus-Tabellen die einzufügenden, in Minus-Tabellen die zu löschenden Fakten – zwischenspeichert. Außerdem wird für jede relevante Basisrelation eine inkrementelle Transitionssicht erstellt (a_New, b_New c_New). Für die betrachtete Sicht (hier q) werden zusätzliche Delta-Plus- und Delta-Minus-Sichten (q_Plus, q_Minus) sowie eine naive Transitionssicht (q_New) erstellt. Delta-Sichten in SQL entsprechen dabei den Delta-Regeln in Datalog. In Delta-Sichten werden nur die geänderten Fakten (in den Delta_Plus die Einfügungen, in Delta-Minus die Löschungen) angezeigt. Diese Fakten werden ermittelt, ohne die ursprüngliche Sicht (hier q) komplett zu berechnen. Delta-Plus/-Minus-Sichten sowie die Transitionssichten für q werden analog zu den Definitionen 5 und 6 verfasst: In Datalog q+(y) ← a+(y),bnew(y),cnew (y), not q(y). q+(y) ← anew (y),b+(y),cnew (y) ,not q(y). q+(y) ← anew (y),bnew (y),c+(y) ,not q(y). q-(y) ← a-(y),b(y),c(y), not qnew (y). q-(y) ← a(y),b-(y),c(y), not q new (y). q-(y) ← a(y),b(y),c-(y), not q new (y) qnew(y) ← anew(y),bnew(y),cnew(y). anew (y) ← a(y), not a-(y). anew (y) ← a+(y) . bnew (y) ← b(y), not b-(y). bnew (y) ← b+(y). cnew (y) ← c(y), not c-(y). cnew (y) ← c+(y). Die Basisrelationen a, b und c haben dabei positive Polarität. Hier, wie auch im späteren Verlauf des Kapitels, wird der immer gleiche Ausdruck CREATE VIEW AS bei den Sichtdefinitionen weggelassen (was im Übrigen ja auch mit dem Operator CREATE VIEW bei der Sichtdefinition in Access-SQL geschieht, und das SELECT-Statement stattdessen einfach in das Auswahlabfragefenster der entsprechenden Sicht geschrieben). 38 Kapitel 5 Dasselbe in SQL: q_Plus: SELECT a_Plus.Name FROM a_Plus, b_New, c_New WHERE a_Plus.Name=b_New.Name AND a_Plus.Name = c_New.Name UNION ALL SELECT a_New.Name FROM a_New, b_Plus, c_New WHERE a_New.Name=b_Plus.Name AND a_New.Name = c_New.Name UNION ALL SELECT a_New.Name FROM a_New, b_New, c_Plus WHERE a_New.Name=b_New.NameAND a_New.Name = c_Plus.Name; q_Minus: SELECT a_Minus.Name FROM a_Minus , b , c WHERE a_Minus.Name = b.Name AND a_Minus.Name = c.Name UNION ALL SELECT a.Name FROM a , b_Minus , c WHERE a.Name = b_Minus.Name AND a.Name = c.Name UNION ALL SELECT a.Name FROM a , b , c_Minus WHERE a.Name = b.Name AND a.Name = c_Minus.Name; Dort, wo in Datalog mehrere Delta-Regeln mit dem gleichen Kopf stehen (q+ bzw. q-), werden in SQL die entsprechenden SELECT-Anweisungen durch den Operator UNION ALL in eine Sicht (q_Plus bzw. q_Minus) eingebunden. Der Operator UNION ALL eliminiert im Gegensatz zu UNION Duplikate aus der Ergebnismenge nicht. Hier wird der Operator UNION ALL verwendet, da SQL eine multimengenorientierte Sprache ist, und Sichten in SQL auch Duplikate enthalten können. Wo in Datalog für eine Regel mit n Rumpfliteralen jeweils n Delta_Plus- bzw. Delta_Minus-Regeln entstehen, entsteht in SQL nur eine Delta_Plus- Bzw. Delta_Minus-Sicht mit n SELECT-Anweisungen, die durch den Operator UNION ALL miteinander verbunden sind. Transitionsichten für q. q_New: SELECT a_New.Name FROM a_New, b_New, c_New WHERE a_New.Name=b_New.Name AND a_New.Name=c_New.Name; 39 Änderungspropagierung in SQL _ a_New: SELECT * FROM a WHERE NOT EXISTS ( SELECT a_Minus.Name FROM a_Minus WHERE a.Name = a_Minus.Name ) UNION All SELECT * from a_Plus; Die c_New- und b_New-Sichten werden nach dem gleichem Muster gebildet. Was passiert, wenn der Operator OR im Where-Teil vorkommt ? Dieses Problem wird anhand einer Beispielsicht/ Regel r zuerst in Datalog, dann in SQL erläutert. Wie man weiß, wird der Operator OR in Datalog durch mehrere Regeln mit dem gleichen Kopf realisiert, was für die Delta-Regelerstellung kein Problem darstellt. r(y) ← a(y),b(y). r(y) ← a(y),c(y). r+(y) r+(y) r+(y) r+(y) ← ← ← ← a+(y),Bnew(y),not r. Anew(y),Bnew(y), not r. a+(y),Cnew(y), not r. Anew(y),Cnew(y), not r. r-(y) r-(y) r-(y) r-(y) ← ← ← ← a-(y),b(y), a(y),b-(y), a-(y),c(y), a(y),c-(y), not not not not r r r r new. new. new. new. Ziel ist es, SQL_Delta-Sichten für r zu erstellen, ohne r in eine Datalog-ähnliche Form zu transformieren (etwa mit Hilfe des Operators UNION die WHERE-Bedingung umzuformen). Der Vorschlag für die Delta_Plus-Sicht der Sicht r mit der Definition SELECT a.Name FROM a, b, c WHERE a.Name=b.Name OR a.Name=c.Name; lautet: r_Plus: SELECT a_Plus.Name FROM a_Plus , b_New , c_New WHERE a_Plus.Name = b_New.Name OR 40 Kapitel 5 a_Plus.Name = c_New.Name UNION ALL SELECT a_New.Name FROM a_New , b_Plus , c_New WHERE a_New.Name = b_Plus.Name OR a_New.Name = c_New.Name UNION ALL SELECT a_New.Name FROM a_New , b_New , c_Plus WHERE a_New.Name = b_New.Name OR a_New.Name = c_Plus.Name; Man sieht, dass hier die folgenden Kombinationen aus Relationen enthalten sind: a_Plus und b_New a_Plus und c_New a_New und b_Plus a_New und c_New a_New und b_New a_New und c_Plus Dies sind alle Kombinationen, die in Analogie zur Datalog Delta_Plus-Regel r+ vorhanden sein sollten, plus zusätzlich a_New und c_New sowie a_New und b_New. Da man, wie in Abschnitt 4.2 gezeigt wurde, aber auch im SQL-Kontext einen Effektivitätstest benötigt, stellen diese zusätzlichen Kombinationen kein Problem dar. Der Effektivitätstest wird dabei falsche Delta-Fakten, die solche überflüssigen Kombinationen eventuell produziert haben werden, ausfiltern. Diese Vorgehensweise wird aber den Algorithmus zur Delta-Sichterstellung nicht unnötig verkomplizieren. Wie man den SQL-Beispielen in diesem und auch vorigem Kapitel ansieht, fehlt hier der Effektivitätstest. Dadurch wird an dieser Stelle die Komplexität, welche der Effektivitätstest mit sich bringt, vermieden. Er kann eben einfach nicht ohne weiteres in Jet-SQL implementiert werden, weil Jet-SQL den Mengenoperator Minus nicht kennt. Der nächste Abschnitt wird die Implementierung des Effektivitätstests in SQL erläutern. 5.2 Effektivitätstest Im Datalogkontext wird der Effektivitätstest zum einen bei Einfügungen verwendet, um zu prüfen, ob die neu abgeleiteten Fakten nicht schon im alten Datenbankzustand vorhanden waren. Zum anderen wird der Effektivitätstest bei Löschungen eingesetzt, um herauszufinden, ob die gelöschten Fakten nicht trotz der durchgeführten Löschungsmaßnahme durch alternative Ableitungsmöglichkeiten vorhanden geblieben, oder aber durch die Einfügungen hinzugekommen sind. In beiden Fällen gilt es die echten Änderungen herauszufiltern. Echte Einfügungen sind neue Fakten minus schon vorhandener. Echte Löschungen sind gelöschte Fakten minus der übrig gebliebenen bzw. hinzugekommenen. Also gilt für eine Regel p: 41 Änderungspropagierung in SQL p+← ..., not p _ und p-← ..., not pnew. Man könnte eventuell annehmen, da SQL eine multimengenorientierte Sprache ist und Sichten in SQL Duplikaten enthalten sein können, bräuchte man den Test im SQLKontext nicht. In dem in dieser Arbeit vorgestellten Algorithmus für die DeltaSichterstellung benötigt man den Effektivitätstest jedoch sehr wohl, denn es könnten falsche Delta-Fakten entstehen.So zum Beispiel bei der Sicht mit der Definition: SELECT x.Name FROM x WHERE (x.Name) NOT IN (SELECT y.Name FROM y where y.Name NOT IN (SELECT z.Name FROM z )); Für diese Sicht wird die Delta_Plus-Sicht nach dem hier entwickelten Algorithmus folgendermaßen aussehen (hier wird ein wenig vorgegriffen, denn erst in Kapitel 4.3 wird erklärt, wie und weshalb man diese Delta-Sicht so aufbaut): Beispiel 5.2: 1 SELECT x_Plus.Name FROM x_Plus WHERE (x_Plus.Name) NOT IN (SELECT y_New.Name FROM y_New WHERE y_New.Name NOT IN (SELECT z_New.Name FROM z_New)) UNION ALL 2 SELECT x_New.Name FROM x_New WHERE ( x_New.Name ) NOT IN (SELECT y_New.Name FROM y_New WHERE y_New.Name NOT IN (SELECT z_Plus.Name FROM z_Plus)) UNION ALL 3 SELECT x_New.Name FROM x_New WHERE ( x_New.Name ) IN (SELECT y_Minus.Name FROM y_Minus WHERE y_Minus.Name NOT IN (SELECT z_New.Name FROM z_New)); In der zweiten SELECT-Anweisung können falsche Delta-Fakten auftreten. Wenn zum Beispiel die Relationen z und z_Plus leer sind, dann kommen dort Delta-Fakten vor, die schon in der ursprünglichen Relation enthalten waren. Also ist der Effektivitätstest in diesem Algorithmus notwendig. MS Access unterstürzt keinen MINUS-Operator, daher wird MINUS durch NOT EXISTS simuliert. Die Verwendung des Ausdruck NOT EXISTS führt zur Notwendigkeit, zwei zusätzlichen Sichten – die Anfragen für die Implementierung des Effektivitätstests – einzuführen. Diese, hier Delta2-Sichten genannt, werden jeweils auf die Delta_Plus- bzw. Delta_Minus-Sichten angewendet. Somit wird Sichtname_Plus2 für Sichtname_Plus einerseits sowie Sichtname_Minus2 für Sichtname_Minus andererseits in die aktuelle Datenbank eingefügt. Der Effektivitätstest für die Delta_Plus-/ Delta_Minus-Sichten aus Beispiel 5.1 lautet: Effektivitätstest für Delta_Plus-Regel : SELECT DISTINCT r_PLUS.NAME FROM r_PLUS WHERE NOT EXISTS 42 Kapitel 5 ( SELECT * FROM q WHERE IIF ( ISNULL (q.NAME), IIF (ISNULL (q_PLUS.NAME), TRUE, FALSE), IIF(ISNULL(q_PLUS.NAME), FALSE,q.NAME = q_PLUS.NAME) ) ); Effektivitätstest für Delta_Minus-Regel : SELECT DISTINCT q_MINUS.NAME FROM q_MINUS WHERE NOT EXISTS (SELECT * FROM q_NEW WHERE IIF ( ISNULL (q_NEW.NAME), IIF(ISNULL(q_MINUS.NAME),TRUE,FALSE), IIF(ISNULL(q_MINUS.NAME), FALSE,q_NEW.NAME = q_MINUS.NAME) ) ); Der komplizierte Vergleich in der WHERE-Bedingung des SELECT-Statements von Ebene 2 ist notwendig, um NULL-Werte zu vergleichen. Wie schon in Abschnitt 2.2.2 erwähnt, benutzt SQL eine dreiwertige Logik und liefert so durch einen direkten Vergleich der NULL-Werte UNKNOWN zurück. Durch die oben vorgestellte Erweiterung der WHEREKlausel wird erreicht, dass der Vergleich der beiden NULL-Werte TRUE sowie der des NULL -Werts mit dem Nicht-NULL-Wert FALSE zurückliefert. Somit handelt es sich bei den Delta-Fakten um echte Änderungen. Falls also ein Fakt nmal hinzugekommen ist, so wird er nur einmal als neuer Eintrag angezeigt. Falls ein Fakt schon k-mal im alten Zustand vorhanden war und nun m-mal hinzugekommen ist, so wird er überhaupt nicht als neuer Fakt ausgewiesen. Ein Fakt wird nur dann als gelöscht angezeigt, wenn er überhaupt nicht in der Sicht in neuem Zustand enthalten ist. 5.3 Besonderheiten, Schwierigkeiten In diesem Abschnitt wird versucht, für Sichten mit verschiedenen SQL-Operatoren DeltaSichten zu erstellen sowie bereits gewonnene Erkenntnisse über die Änderungspropagierung in SQL an etwas komplexeren Beispielen zu vertiefen. 43 Änderungspropagierung in SQL _ Wie schon in Kapitel 5.1 erwähnt: Wo in Datalog für eine Regel mit N Rumpfliteralen jeweils N Delta_Plus- bzw. Delta_Minus-Regeln entstehen, entsteht in SQL jeweils nur eine Delta_Plus- bzw. Delta_Minus-Sicht mit N SELECT-Anweisungen, welche durch den Operator UNION ALL miteinander verbunden sind. Das heißt, bei einer Sicht, die auf mehr als einer Basistabelle aufbaut, ist der Operator UNION ALL in den Delta-Sichten unvermeidbar. Aus diesem Grund stellt der Operator ORDER BY ein Problem bei der Auswertung der Delta-Sichten dar, da JET-SQL nicht mehr als einen ORDER BY-Operator bei der Abfrage mit dem Operator UNION duldet. Das Problem wird gelöst, indem der Operator ORDER BY ganz aus den Delta-Sichten entfernt wird. Diese Vorgehensweise verfälscht das Ergebnis nicht – die Delta-Fakten werden nur nicht sortiert angezeigt. Ein anderer wichtiger und interessanter Operator, der ebenfalls Probleme verursachen kann, ist GROUP BY. Dieser Operator wird häufig mit dem Zusatz HAVING verwendet. HAVING stellt eine Bedingung an die selektierte Zeile und wird oft in Kombination mit Aggregatfunktionen eingesetzt. Auf die Probleme mit Aggregatfunktionen wird in Abschnitt 5.4 noch genauer eingegangen. Wenn der Operator GROUP BY ...HAVING jedoch ohne Aggregatfunktionen benutzt wird, so stellt er kein Problem dar. Denn die GROUP BY - und HAVING -Zeilen werden unverändert in die Delta-Sichten übernommen, und durch die Verwendung des Effektivitätstests werden die Duplikate eliminiert. Für die Sicht mit der Definition SELECT Artikel.Beschreibung, Artikel.Nr FROM Artikel GROUP BY Artikel.Beschreibung, Artikel.Nr HAVING Artikel.Nr between 1 and 10; sehen Delta-sichten wie folgt aus: Delta_Plus: SELECT Artikel_Plus.Beschreibung, Artikel_Plus.Nr FROM Artikel_Plus GROUP BY Artikel_Plus.Beschreibung, Artikel_Plus.Nr HAVING Artikel_Plus.Nr between 1 and 10; Delta_Minus: SELECT Artikel_Minus.Beschreibung, Artikel_Minus.Nr FROM Artikel_Minus GROUP BY Artikel_Minus.Beschreibung, Artikel_Minus.Nr HAVING Artikel_Minus.Nr between 1 and 10; Der Operator INNER JOIN ist ebenso problemlos einzusetzbar: Beispiel - INNER JOIN: SELECT a.Nr, a.Artikel, D.Anzahl FROM a INNER JOIN D ON a.Nr=D.Nr; 44 Kapitel 5 Delta_Plus: SELECT a_Plus.Nr , a_Plus.Artikel , D_New.Anzahl FROM a_Plus INNER JOIN D_New ON a_Plus.Nr = D_New.Nr UNION ALL SELECT a_New.Nr , a_New.Artikel , D_Plus.Anzahl FROM a_New INNER JOIN D_Plus ON a_New.Nr = D_Plus.Nr; Delta_Minus: SELECT a_Minus.Nr , a_Minus.Artikel , D.Anzahl FROM a_Minus INNER JOIN D ON a_Minus.Nr = D.Nr UNION ALL SELECT a.Nr , a.Artikel , D_Minus.Anzahl FROM a INNER JOIN D_Minus ON a.Nr = D_Minus.Nr; Bei RIGHT/ LEFT OUTER JOIN verhält es sich dagegen anders: Beispiel (Delta-Sichten für die Sicht s, die den Operator LEFT OUTER JOIN beinhaltet): SELECT E.Name, F.Name FROM E LEFT OUTER JOIN F ON E.Name=F.Name; Der hier entwickelte Algorithmus würde die Delta-Sichten wie folgt erstellen: r_Plus: SELECT E_Plus.Name , F_New.Name FROM E_Plus LEFT OUTER JOIN F_New ON E_Plus.Name = F_New.Name UNION ALL SELECT E_New.Name , F_Plus.Name FROM E_New LEFT OUTER JOIN F_Plus ON E_New.Name = F_Plus.Name; r_Minus: SELECT E_Minus.Name , F.Name FROM E_Minus LEFT OUTER JOIN F ON E_Minus.Name = F.Name UNION ALL SELECT E.Name , F_Minus.Name FROM E LEFT OUTER JOIN F_Minus ON E.Name = F_Minus.Name; Im zweiten SELECT-Statement der r-Plus-Sichtdefinition werden für alle Einträge der Tabelle E_New neue Fakten gebildet, und zwar sogar für den Fall, dass die Tabelle F_Plus leer ist. Somit entstehen falsche Delta-Fakten, das heißt, an dieser Stelle tritt ein Fehler auf. Ein ähnlicher Fehler tritt auch bei der r_Minus-Sicht, im Join zwischen E und F_Minus, auf Auch bei den Sichten mit RIGHT OUTER JOIN gibt es eine ähnliche Schwierigkeit. Aus diesem Grund wird der Operator OUTER JOIN von dem in dieser Arbeit entwickelten Compiler nicht bearbeitet. 45 Änderungspropagierung in SQL _ Man kann das Problem aber dadurch lösen, dass man LEFT durch RIGHT und RIGHT durch LEFT ersetzt. Es müssen unbedingt alle Einträge der Plus-/Minus-Tabellen komplett in den Join übernommen werden, und nicht die der New (/Old)-Tabellen. Im Beispiel muss LEFT OUTER JOIN zwischen E_New und F_Plus sowie E und F_Minus durch RIGHT OUTER JOIN ersetzt werden. Für SELECT-Anfragen, die den Operator UNION enthalten, also diejenigen, die aus mehreren SELECT-Statements bestehen, können ganz einfach Delta-Regeln gebildet werden. Für jeden dieser SELECTs werden eigene Delta-Sichten erstellt, die, verknüpft durch den Operator UNION, in einer gemeinsamen Delta-Sicht in die Datenbank eingefügt werden. SELECT a.NAME FROM a UNION SELECT b.NAME FROM b; Delta_Plus: SELECT a_Plus.Name FROM a_Plus UNION ALL SELECT b_Plus.Name FROM b_Plus; Delta_Minus: SELECT a_Minus.Name FROM a_Minus UNION ALL SELECT b_Minus.Name FROM b_Minus; Wo Datalog Hilfsregeln kennt, bedient sich SQL der Verschachtelungen, die mehrere Ebenen tief reichen können. Der folgende Ausdruck hat z.B. Tiefe 2: SELECT deutscher_Bücher.Autor, deutscher_Bücher.Titel, deutscher_Bücher.Art FROM deutscher_Bücher WHERE not exists ( SELECT * FROM Fachliteratur WHERE Fachliteratur.Autor = deutscher_Bücher.Autor AND Fachliteratur.Titel = deutscher_Bücher.Titel). Die Verschachtelungen stellen dabei große Schwierigkeiten bei der Polaritätsbestimmung dar. An dieser Stelle sei bemerkt, dass alle Relationen aus den vorherigen Beispielen eine positive Polarität besitzen. Natürlich lassen sich die verschachtelten SQL-Ausdrücke vereinfachen, indem sie in mehrere SELECT-Anfragen aufgesplittet werden. Dennoch nimmt die Auswertung von mehreren, aufeinander basierenden SQL-Anfragen bedeutend mehr Zeit in Anspruch als diejenige eines verschachtelten Ausdrucks. Somit wäre die trügerische Einfachheit aus der Sicht der Performance unvorteilhaft. Es wird daher 46 Kapitel 5 versucht, die Propagierungssichten zu erstellen, ohne die Sicht in die Hilfssichten aufzusplitten. Zuerst ein einfaches Beispiel: Beispiel 5.3 : SELECT x.Nr, x.Name FROM x WHERE EXISTS (SELECT * FROM y WHERE y.Name=x.Name); Alle Basisrelationen besitzen eine positive Polarität. Somit steht der Bildung der DeltaRegeln nichts im Wege: Delta_Plus: SELECT x_Plus.Nr , x_Plus.Name FROM x_Plus WHERE EXISTS ( SELECT * FROM y_New WHERE y_New.Name = x_Plus.Name ) UNION ALL SELECT x_New.Nr , x_New.Name FROM x_New WHERE EXISTS ( SELECT * FROM y_Plus WHERE y_Plus.Name = x_New.Name ); Delta_Minus: SELECT x_Minus.Nr , x_Minus.Name FROM x_Minus WHERE EXISTS ( SELECT * FROM y WHERE y.Name = x_Minus.Name ) UNION All SELECT x.Nr , x.Name FROM x WHERE EXISTS ( SELECT * FROM y_Minus WHERE y_Minus.Name = x.Name ); Beispiel (mit Negation) Zuerst in Datalog: t(NR,Name) ← x(Nr,Name) , not y(Nr,Name). t+(NR,Name) ← x+(Nr,Name) ,not ynew(Nr,Name), not t. t+(NR,Name) ← xnew(Nr,Name) , y-(Nr,Name), not t. t-(NR,Name) ← x-(Nr,Name) , not y(Nr,Name), not tnew. t-(NR,Name) ← x(Nr,Name) , y+(Nr,Name), not tnew. Zur Erinnerung: Die Änderungspropagierung in Datalog durch Literale mit negativer Polarität erfolgt im Prinzip genauso. Der einzige Unterschied besteht darin, dass der ursprünglich negative Literal mit den invertierten Vorzeichen in die Formel eingeht. 47 Änderungspropagierung in SQL _ Nun die gleiche Sicht t in SQL, wobei die Negation durch den Operator NOT EXISTS ausgedrückt wird: SELECT x.Nr, x.Name FROM x WHERE NOT EXISTS (SELECT * FROM y WHERE y.Name=x.Name); Die Basissicht y hat eine negative Polarität. Wie in Datalog, so geht auch hier bei SQL die ursprünglich negierte Basisrelation (hier y) mit den invertierten Vorzeichen in die DeltaSicht ein: Delta_Plus: SELECT x_Plus.Nr , x_Plus.Name FROM x_Plus WHERE NOT EXISTS ( SELECT * FROM y_New WHERE y_New.Name=x_Plus.Name ) UNION ALL SELECT x_New.Nr , x_New.Name FROM x_New WHERE EXISTS ( SELECT * FROM y_Minus WHERE y_Minus.Name=x_New.Name ); Delta_Minus: SELECT x_Minus.Nr , x_Minus.Name FROM x_Minus WHERE NOT EXISTS ( SELECT * FROM y WHERE y.Name=x_Minus.Name ) UNION ALL SELECT x.Nr , x.Name FROM x WHERE EXISTS ( SELECT * FROM y_Plus WHERE y_Plus.Name=x.Name ); Man sieht, dass die Relation y als y_Minus in der Delta_Plus-Regel und als y-Plus in die Delta_Minus-Regel eingegangen ist. Außerdem wird der Operator NOT EXISTS durch den EXISTS-Operator ersetzt. Diese Vorgehensweise für negierte SELECT-Statements entspricht der Vorgehensweise bei negativen Literalen in Datalog. Aber was passiert, wenn man einen Ausdruck der Tiefe 3 oder mehr betrachtet? Falls alle Relationen positive Polarität besitzen und somit keine Negation in der Anfrage vorkommt, so spielt die Tiefe bekanntermaßen keine Rolle – die Delta-Sichten werden ähnlich dem oben betrachteten Beispiel 5.3 gebildet. Falls aber doch Negationen vorkommen, so wird die Polaritätsbestimmung schwieriger. Beispiel mit Operator NOT IN der Tiefe 3: SELECT x.Name FROM x 48 Kapitel 5 WHERE x.Name NOT IN (SELECT y.Name FROM y WHERE y.Name NOT IN (SELECT z.Name FROM z WHERE y.Name=z.Name)); Delta_Plus: SELECT x_Plus.Name FROM x_Plus WHERE x_Plus.Name NOT IN ( SELECT y_New.Name FROM y_New WHERE y_New.Name NOT IN ( SELECT z_New.Name FROM z_New WHERE y_New.Name=z_New.Name)) UNION ALL SELECT x_New.Name FROM x_New WHERE x_New.Name NOT IN ( SELECT y_New.Name FROM y_New WHERE y_New.Name NOT IN ( SELECT z_Plus.Name FROM z_Plus WHERE y_New.Name = z_Plus.Name )) UNION ALL SELECT x_New.Name FROM x_New WHERE x_New.Name IN ( SELECT y_Minus.Name FROM y_Minus WHERE y_Minus.Name NOT IN ( SELECT z_New.Name FROM z_New WHERE y_Minus.Name = z_New.Name )); Delta_Minus: SELECT x_Minus.Name FROM x_Minus WHERE x_Minus.Name NOT IN ( SELECT y.Name FROM y WHERE y.Name NOT IN ( SELECT z.Name FROM z WHERE y.Name=z.Name )) UNION ALL SELECT x.Name FROM x WHERE x.Name NOT IN ( SELECT y.Name FROM y WHERE y.Name NOT IN ( SELECT z_Minus.Name FROM z_Minus WHERE y.Name=z_Minus.Name )) UNION ALL SELECT x.Name FROM x WHERE x.Name IN ( SELECT y_Plus.Name FROM y_Plus WHERE y_Plus.Name NOT IN ( SELECT z.Name FROM z WHERE y_Plus = z.Name )); 49 Änderungspropagierung in SQL _ Die Relation x besitzt positive Polarität. Die Relation y wurde dagegen negiert und besitzt daher negative Polarität. Die Relation z wird zweimal negiert und erhält wiederum positive Polarität. Das ist auch logisch, denn wie man es aus der Algebra kennt: zweimal Minus ergibt Plus. Bei y_Plus in Delta_Minus und y_Minus in Delta_Plus wird NOT IN durch IN ersetzt. Somit haben Relationen, welche sich in Verschachtelungen mit gerader Anzahl von NOT EXISTS- oder NOT IN-Ausdrücken befinden, eine positive Polarität,– die mit ungerader Anzahl dagegen eine negative. NOT IN und NOT EXISTS sind die einzigen Negationsoperatoren in Access. Darüber hinaus werden weder MINUS noch EXCEPT akzeptiert. Wenn eine Sicht (z.B. Sicht1) von einer anderen (z.B. Sicht2) abhängt, so wird Sicht2 wie eine Basisrelation gehandhabt, jedoch mit dem Unterschied, dass, anders als bei einer Basisrelation, Sicht2_Plus und Sicht2_Minus keine Tabellen, sondern Delta-Sichten sind. Was die Sicht Sicht2_New angeht, so handelt es sich dabei um eine naive und nicht um eine inkrementelle Transitions-Sicht. Unbedingt beachtet werden muss hier Folgendes: Bevor man die Propagierungssichten für Sicht1 generiert, muss man die entsprechende Delta- und Transitionssichten für Sicht2 erzeugen. Ein Beispiel einer von einer anderen Sicht abhängigen Sicht wird in Abschnitt 6.3.2 näher behandelt. Die Operatoren ANY/SOME/ALL, die Unteranfragen einleiten, werden in der Praxis selten benutzt. Aus diesem Grund, und um eine unnötige Komplexität zu vermeiden, werden diese Operatoren vom Compiler auch nicht bearbeitet. Operatoren und Schlüsselwörter wie IN, BETWEEN, LIKE, IS (NOT) NULL, IIF (<Expr>, True, False) oder AS, die in der WHERE-Bedingung oder in der SELECT-Klausel auftreten können, stellen kein Problem dar, da die WHERE-Bedingung und die SELECT-Klausel unverändert in die Delta-Sichten übernommen werden. Schlüsselwörter wie DISTINCT, DISTINCTROW stellen auch keine Schwierigkeiten dar, da die Duplikate durch den Effektivitätstest eliminiert werden. Access-Abfragen können außerdem Parameter enthalten. Aber auch sie stellen kein Problem dar. Werden Delta-Abfragen/Sichten ausgeführt, so sollte man einfach einmal den Parameter eingeben. Rekursionen treten bei Access gar nicht auf und werden deshalb hier auch nicht betrachtet. Der Konstruktor With, mit dessen Hilfe lokale Anfragen eingeleitet werden können, kennt Jet-SQl ebenfalls nicht. 5.4 Duplikate In dieser Arbeit wird das Problem der Duplikate nur bedingt gelöst. In Abschnitt 4.2 wurde die Notwendigkeit eines Effektivitätstests gezeigt; also wird dieser angewendet. 50 Kapitel 5 Somit handelt es sich bei Delta-Fakten um echte Änderungen. Falls also ein Fakt n-mal hinzugekommen ist, wird er nur einmal als neuer Eintrag angezeigt. Falls ein Fakt bereits k-mal im alten Zustand vorhanden war und m-mal hinzugekommen ist, wird er überhaupt nicht als neuer Fakt ausgewiesen. Ein Fakt wird nur dann als gelöscht angezeigt, wenn er überhaupt nicht in neuem Zustand in der Sicht enthalten ist. Die Ausnahme bilden hier die Sichten, deren Attribute auf Aggregatfunktionen basieren. Bei solchen Sichten wird in dieser Arbeit kein Effektivitätstest durchgeführt. Somit werden alle Duplikate als Delta-Fakten erhalten, um die Aggregatfunktionen richtig auswerten zu können. Allerdings darf man dabei nicht vergessen, dass eventuell auch Einträge als Delta-Fakten ausgewertet werden, die überhaupt nicht (nicht mal als Duplikate) eingefügt/ gelöscht wurden (siehe Beispiel 5.2). Wie Aggregatfunktionen genau behandelt werden, wird im nächsten Abschnitt erläutert. 5.5 Aggregatfunktionen Die Änderungspropagierung bei Sichten mit Aggregatfunktionen stellt ein großes Problem dar, das in dieser Arbeit nur teilweise gelöst wird. Kommen Aggregatfunktionen in Selektions- und Gruppierungsbedingungen (wie WHEREoder HAVING-Klauseln) vor, so können Modifikationen einer oder mehrerer Gruppen oder auch Einfügungen oder Löschungen von Gruppen die Folge von Basisänderungen sein. Treten die Aggregatfunktionen ausschließlich in Haupt-SELECT-Klauseln auf, so müssen bestehende Gruppen geändert werden. In dieser Arbeit werden nur die ANSI Standard SQL-Aggregatfunktionen MIN/MAX, COUNT, SUM und AVG behandelt, die in der HauptSELECT-Klausel auftreten. Sichten mit Aggregatfunktionen in den Selektions- und Gruppierungsbedingungen (wie WHERE- oder HAVING-Klauseln) werden in dieser Arbeit nicht behandelt. Die Sichten, deren Attribute auf Aggregatfunktionen wie COUNT, MIN, MAX, SUM oder AVG basieren, brauchen spezielle Delta-Sichten zur Auswertung von Aggregatfunktionen. Zuerst werden die Aggregatfunktionen SUM, COUNT und AVG betrachtet. Wie schon in Abschnitt 5.1 gesagt, werden für jede Sicht eine Delta-Plus- und eine Delta-Minus-Sicht, die Delta-Fakten enthalten, gebildet. Um zum Beispiel die geänderte SUMME (bzw. COUNT-Werte) zu finden, braucht man nur die Summe (bzw. die Count-Werte) der ursprünglichen Sicht zu jenen der Delta_Plus-Sicht zu addieren und davon die der Delta_Minus-Sicht zu subtrahieren. Fast das Gleiche gilt auch für den Durchschnitt, nur dass man das Ergebnis noch durch 3 dividieren muss. Man beachte, dass, obwohl man zwei Sichten (Delta_Plus und Delta_Minus) hat, nur eine Sicht, hier Delta2-Sicht genannt, generiert wird, und zwar auf folgende Weise: 51 Änderungspropagierung in SQL _ Für die Sicht u mit SELECT COUNT / SUM (AVG)(id) FROM a; sieht u_Delta2 wie folgt aus: SELECT (a2.a1+a3.a1-a4.a1) (/3 falls AVG) AS Ausdr1 FROM [ SELECT SUM(EXPR1000) AS a1 FROM uq]. AS a2, [ SELECT SUM(EXPR1000) AS a1 FROM u_Plus ]. AS a3, [ SELECT SUM(EXPR1000) AS a1 FROM u_Minus ]. AS a4; Wobei Expr1000 der Name jener Sichtattribute ist, die auf Aggregatfunktionen basieren. Dieser Name wird den Spalten automatisch von Access gegeben, falls sie nicht explizit in der Anfrage benannt werden. Die Funktionen MIN und MAX stellen allerdings ein Problem dar. Man kann zwar ohne Probleme MIN/MAX für die Sicht und die Delta-Plus-Sicht wie folgt herausfinden: SELECT MIN/MAX (EXPR1000) AS ausdr1 FROM [SELECT * FROM q UNION SELECT * FROM q_Plus]; Wie kann man aber MIN/MAX ermitteln, wenn Löschungen stattgefunden haben? Eine Teillösung besteht hier darin, die Aggregatfunktionen MIN/MAX nicht für die ursprüngliche Ausgangssicht und Delta-Fakten auszuwerten, sondern nur für die DeltaFakten. Wie schon in Kapitel 5.1 erwähnt, entsteht, wo in Datalog für eine Regel mit N Rumpfliteralen jeweils N Delta_Plus- bzw. Delta_Minus-Regeln entstehen, in SQL nur eine Delta_Plus- bzw. Delta_Minus-Sicht mit N SELECT-Anweisungen, die durch den Operator UNION ALL miteinander verbunden sind. Das heißt, bei einer Sicht, die auf mehr als einer Basistabelle aufbaut, ist der Operator UNION ALL in Delta-Sichten unvermeidbar. Auch wenn die Attribute dieser Sicht auf Aggregatfunktionen basieren, werden die Delta-Sichten mehrere Tupel als Ergebnis zurückliefern, nämlich für jede SELECT-Anweisung eines. Man erwartet aber nur ein Tupel als Ergebnis der Aggregatfunktionen. Man kann dieses Problem lösen, indem man auf die Delta_Plus- bzw. Delta_MinusSichten jeweils eine Sicht (hier Delta2-Sicht genannt) anwendet, welche die entsprechenden Aggregatfunktionen auf Ergebnisse der Delta-Sichten anwendet. Für die Sicht v mit: SELECT min(id) FROM a; sehen die Delta2_Sichten wie folgt aus: 52 Kapitel 5 v_Plus2 : SELECT Min(expr1000) AS Ausdr1 FROM v_Plus; v_Minus2: SELECT Min(expr1000) AS Ausdr1 FROM v_Minus; Da die richtige Behandlung von Duplikaten im Compiler nicht garantiert ist, so ist auch die Korrektheit der Auswertung von Aggregatfunktionen nicht garantiert. 5.6 Algorithmen Der Kern der Bemühungen war es, einen Algorithmus zur Delta-Sichterstellung zu entwickeln. Zunächst einige Fakten, die in den vorigen Kapiteln entwickelt oder festgelegt wurden: Da MS Access keinen MINUS-Operator unterstützt, können nur Relationen in Ausdrücken mit NOT EXIST oder NOT IN negative Polarität besitzen. Bei den Propagierungsbeispielen wird grundsätzlich ein neuer Datenbankzustand simuliert. Die "optimistische" Methode, bei der ein alter Zustand simuliert wird, kommt hier eher nicht in Frage, weil MS Access mit Integritätsbedingungen arbeitet, und daher mögliche Integritätsverletzungen erwartet werden. Im Folgenden werden Regel und Algorithmen vorgestellt, die der hier zu entwickelnde Compiler zur Generierung von Delta-Sichten benutzen wird. Regel zur Polaritätbestimmung: Relationen, die sich in Verschachtelungen mit einer geraden Anzahl von NOT EXISTS- oder NOT IN-Ausdrücken befinden, besitzen positive Polarität, jene mit einer ungerader Anzahl hingegen eine negative. Alle anderen Relationen besitzen positive Polarität. Gegeben sei eine nicht-rekursive Sicht, die auf R1 bis Rn Relationen aufbaut, mit einer Sichtdefinition, die als SQL-String gegeben wird. Teile die Relationen mit Hilfe der Regel zur Polaritätbestimmung in zwei Mengen auf: S1 – Menge der Relationen mit positiver Polarität und S2 – Menge der Relationen mit negativer Polarität. Algorithmus für die Delta_Plus-Sicht: Eingabe: S1 ,S2 , Sichtdefinition als String; Ausgabe: Delta_Plus-Sicht als String; 53 Änderungspropagierung in SQL _ Methode: While S1 is not empty: nehme eine Relation Ri aus S1 lege String-Kopie(i) der Sichtdefinition ersetze Ri durch Ri_Plus in der String-Kopie(i) lösche Ri aus S1 ersetze alle Rj aus (R1,..., Ri-1, Ri+1,...Rn) durch Rj_New in String- Kopie(i) füge UNION ALL zu String- Kopie(i) While S2 is not empty: nehme Relation Ri aus S2 lege String-Kopie(i) der Sichtdefinition ersetze Ri durch Ri_Minus in der String-Kopie(i) lösche Ri aus S2. ersetze NOT EXISTS bzw. NOT IN, die die Relation Ri einleiten durch EXISTS bzw. IN ersetze alle Rj aus (R1,..., Ri-1, Ri+1,...Rn) durch Rj_New in String- Kopie(i) falls S2 is not empty, füge UNION ALL zu String- Kopie(i) Füge alle n veränderter String-Kopie der Sichtdefinition zu einer Delta_Plus-Sicht. Algorithmus für die Delta_Minus-Sicht: Eingabe: S1 ,S2 , Sichtdefinition als String; Ausgabe: Delta_Minus-Sicht als String; Methode: While S1 is not empty: nehme Relation Ri aus S1 lege String-Kopie(i) der Sichtdefinition ersetze Ri durch Ri_Minus in der String-Kopie(i) lösche Ri aus S1 füge UNION ALL zu String- Kopie(i) While S2 is not empty: nehme Relation Ri aus S2 lege String-Kopie(i) der Sichtdefinition ersetze Ri durch Ri_Plus in der String-Kopie(i) lösche Ri aus S2. ersetze NOT EXISTS bzw. NOT IN, die die Relation Ri einleiten durch EXISTS bzw. IN falls S2 is not empty, füge UNION ALL zu String- Kopie(i) Füge alle n veränderten String-Kopie der 54 Sichtdefinition zu Kapitel 5 einer Delta_Minus-Sicht Zusätzlich werden noch die Delta2-Sichten gebildet. Delta2-Sichten implementieren entweder den Effektivitätstest oder stellen spezielle Sichten für die Auswertung der Aggregatfunktion dar (siehe 5.2 und 5.5). Delta_Plus2- / Delta_Minus2-Sichten (Effektivitätstest) für die Delta_Plus(/_Minus)Sicht: SELECT DISTINCT (alle) Sicht.Attribute FROM Sichtname_Plus WHERE NOT EXISTS (SELECT * FROM Sichtname WHERE IIF (ISNULL((alle)Sichtname.Attribute), IIF (ISNULL ((alle)Sichtname_Plus(Minus).Attribute), TRUE, FALSE), IIF(ISNULL(((alle)Sichtname_Plus(Minus).Attribute),FALSE, (alle)Sichtname.Attribute= (alle)Sichtname_Plus(Minus).Attribute))); Spezielle Delta2-Sicht für die Aggregatfunktionen COUNT, AVG, SUM: SELECT (a2.a1+a3.a1-a4.a1) (/3 falls AVG) AS Ausdr1 FROM [ SELECT SUM( Sicht.Attribut) AS a1 FROM u]. AS a2, [ SELECT SUM(Sicht.Attribut) AS a1 FROM u_Plus ]. AS a3, [ SELECT SUM(Sicht.Attribut) AS a1 FROM u_Minus ]. AS a4; Spezielle Delta_Plus(/_Minus)2-Sichten für die Aggregatfunktionen Min/Max: SELECT MIN/MAX_Aggregatfunktion (Sicht.Attribut ) AS Ausdr1 FROM Sichtname_Plus(Minus). Die naive Transitionssicht erhält man, indem in der ursprünglichen Definition alle Basisrelationen einfach durch Basisrelation_New ersetzt werden. Die inkrementellen Transitionssichten für die Basisrelationen : (SELECT * FROM Basisrelation WHERE NOT EXISTS (SELECT * FROM Basisrelation_Minus WHERE (alle)Basisrelation.Attribute= Basisrelation_Minus.Attribute)) UNION (SELECT * FROM Basisrelation_Plus) 55 Änderungspropagierung in SQL 56 _ Kapitel 6 6 Implementierung Mit diesem Kapitel beginnt der praktische Teil dieser Arbeit. In Abschnitt 6.1 werden die Implementierung des Compilers in VBA und in Abschnitt 6.2 das Programm, in welches der Compiler integriert ist, vorgestellt. Das Programm erlaubt es dabei dem Benutzer, auf bequeme Weise für die ausgewählten Sichten die Delta-Sichten zu generieren, Basisänderungen einzugeben, die induzierten Änderungen anzusehen und gegebenenfalls die Änderungen in der Datenbank zu speichern. Die Korrektheit des Compilers wird anhand von zwei Beispielsichten in Abschnitt 6.3 demonstriert. 6.1 Compiler „Ein Compiler ist ein Programm, das ein in einer bestimmten Sprache – der Quell-Sprache – geschriebenes Programm liest und es in ein äquivalentes Programm einer anderen Sprache – der Ziel-Sprache – übersetzt“ ([ASU88]). Während der Übersetzung festgestellte Fehler im Quellprogramm werden als Fehlermeldungen ausgegeben. Abbildung 6.1 zeigt eine einfache Vorstellung eines Compilers. Abbildung 6.1 Compiler [ASU88] Der in dieser Arbeit entwickelte Compiler hat die Aufgabe, Delta-Sichten zu erstellen. Somit übersetzt er Sichten in Delta-Sichten. Falls er in der ursprünglichen Sichtdefinition 57 Implementierung _ Operatoren oder Schlüsselwörter findet, die er nicht bearbeiten kann, so gibt er eine Fehlermeldung aus. Abbildung 6.2 zeigt eine einfache Skizze des Compilers. Abbildung 6.2 Delta-Sichten-Compiler Der hier beschriebene Compiler besteht aus folgenden Teilen: 1. Parser 2. Analyser 3. Code Generator Im Parser werden die Zeichen des Quellprogramms von Links nach Rechts gelesen und in Tokens aufgesplittet. Dabei wird geprüft, ob das Quellprogramm Zeichen enthält, die vom Compiler nicht bearbeitet werden. Der Analyser hat die Aufgabe, einen Code zu erzeugen, der leicht in ein Zielprogramm übersetzt werden kann. Die Endphase − der Code Generator − bei der Arbeit des Compilers besteht in der Codeerzeugung. Diese Phase wandelt die Zwischendarstellung des Quellprogramms in ein äquivalentes Zielprogramm um. Eine detailliertere Beschreibung folgt in den nächsten Abschnitten. 6.1.1 Parser In diesem Abschnitt wird die erste Komponente des Compilers – der Parser – erläutert. Der Anwender gibt dem Programm (siehe Kapitel 6.2), in welchem der Compiler aufgerufen wird, den Sichtnamen einer der Sichten der aktuellen Datenbank. Die Sichtdefinition wird aus dem Access-Datenbankschema gelesen. Der Parser erhält als einzigen Inputparameter die SQL-Sichtdefinition als Zeichenkette der in der aktuellen Datenbank enthalten Sicht. Die Richtigkeit der Syntax und der Semantik der Sichtdefinition wird nicht geprüft, da dies überflüssig ist. Denn das AccessDBMS führt diese Prüfung durch, noch bevor die Sichtdefinition im Datenbankschema gespeichert wird. Der SQL-String wird zuerst in eine einheitliche Form gebracht. Dafür werden alle Zeilenumbrüche eliminiert sowie zwischen einzelnen Operatoren und Relationen und deren Attributen Leerzeichen platziert. Der String, wie zum Beispiel Fachliteratur.Autor, wird durch das Leerzeichen in Fachliteratur und .Autor 58 Kapitel 6 aufgesplittet. Diese Vorgehensweise erlaubt es dem Compiler später, zwischen dem Relationsnamen und dessen Attributen zu unterscheiden. Dabei wird geprüft, ob in der Sichtdefinition eventuell SQL-Operatoren vorkommen, die vom Compiler nicht ausgewertet werden können. Dies sind zum einen Operatoren, die zwar in der Sichtdefinition vorkommen können, aber von dem für diese Arbeit entwickelten Compiler nicht ausgewertet werden. Dies sind OUTER JOINS, SOME/ ANY / ALL-Unterabfragen sowie Access-eigene Aggregatfunktionen (siehe Abschnitt 3.5) und Aggregatfunktionen in Selektions- und Gruppierungsbedingungen. Zum anderen müssen – da in Access Sichten als Abfragen gespeichert werden – jene Abfragen, die keine Sichten im Sinne von virtuellen Relationen darstellen, ausgefiltert werden. Dies sind Abfragen mit den Schlüsselworten: CREATE/ ALTER/ DROP TABLE (INDEX), DELETE/UPDATE/INSERT INTO/ SELECT INTO TABLE, TOP. Ursprünglicher SQL-String der Sicht deutsche_Belletristik: SELECT deutscher_Bücher.Autor, deutscher_Bücher.Titel, deutscher_Bücher.Art FROM deutscher_Bücher WHERE NOT EXISTS (SELECT * FROM Fachliteratur WHERE Fachliteratur.Autor=deutscher_Bücher.Autor AND Fachliteratur.Titel=deutscher_Bücher.Titel); SQL-String in einheitlicher Form: SELECT deutscher_Bücher .Autor, deutscher_Bücher .Titel , deutscher_Bücher .Art FROM deutscher_Bücher WHERE NOT EXISTS ( SELECT * FROM Fachliteratur WHERE Fachliteratur .Autor = deutscher_Bücher .Autor AND Fachliteratur .Titel = deutscher_Bücher .Titel ); Erst jetzt kann der SQL-String an die Funktion String_Tokenizer (Text As String) As Liste übergeben werden, wo er in einzelne Tokens – einzelne Wörter der SELECT-Anweisung – gesplittet wird. Die Funktion nimmt dabei an, dass zwischen zwei Leerzeichen genau ein Token liegt. Diese Tokens werden in Form einer Liste zurückgegeben. 59 Implementierung _ Beispiel : (für die dem Leser schon bekannte Sicht deutsche_Belletristik sieht die Tokenliste folgendermaßen aus) [ 'SELECT', 'deutscher_Bücher', '.Autor', ',', 'deutscher_Bücher', '.Titel', ',', 'deutscher_Bücher', '.Art', 'FROM', 'deutscher_Bücher', 'WHERE', 'NOT', 'EXISTS', '(', 'SELECT', '*', 'FROM', Fachliteratur', 'WHERE', 'Fachliteratur', '.Autor', '=', 'deutscher_Bücher', '.Autor', 'and', 'Fachliteratur', '.Titel', '=', 'deutscher_Bücher', '.Titel', ')' ] 6.1.2 Analyser Der Analyser ist der wichtigste Teil des gesamten Compilers. Als Eingabe für den Analyser dient die Tokenliste, die vom Parser zurückgeliefert wird. Die Tokensliste wird an die Prozedur Polaritaet_bestimmen (Tokenliste, Neg_Pol, Pos_Pol, Alle_rel) zusammen mit den leeren Mengen Neg_Pol (für die Relationen mit negativer Polarität), Pos_Pol (für die Relationen mit positiver Polarität) und Alle_Rel (Gesamtliste aller vorkommenden Relationen) übergeben. Dabei sind Neg_Pol, Pos_Pol und Alle_Rel Mengen von Objekten der benutzerdefinierten Klasse „Relation“ mit den Attributen: Name Index Index_Not - Relationsname Positionsnummer der Relation in der Tokenliste Position von NOT (aus NOT EXISTS- oder NOT INAusdrücken), falls vorhanden, sonst leer Die Prozedur Polarität_bestimmen liest die Tokenliste von Anfang bis zum Ende, was der Leserichtung der ursprünglichen Sichtdefinition von Rechts nach Links entspricht. Die Aufgabe der Methode ist es, anhand der Sichtdefinition die Relation und deren Polarität festzustellen. Dabei erkennt sie die Relation als Wörter, die zwischen FROM- und WHERESchlüsselwörtern stehen und durch Kommata getrennt sind. Da die negierten Relationen nur in Verschachtelungen auftreten können, die durch NOT EXISTS/ NOT IN eingeleiteten werden, ist es wichtig, die Verschachtelungstiefe sowie die Anzahl der NOT EXISTS / NOT IN-Ausdrücke zu bestimmen. Dies wird mit Hilfe eines Stacks realisiert. Die Polaritätsbestimmung geschieht nach dem Prinzip aus der Regel zur Polaritätbestimmung in Abschnitt 4.6. Für alle Relationen aus der Output-Menge Alle_Rel der Methode Polaritaet_Bestimmen wird geprüft, ob es sich um eine Tabelle oder eine Sicht handelt. Bei einer Sicht wird der Hauptprozess angehalten und der Compiler zwecks kompletter Verarbeitung der gefundenen Sicht neu gestartet. Sobald er damit fertig ist – wenn also die Delta- und Transitionssichten für diese Sicht erstellt wurden – wird der 60 Kapitel 6 Hauptprozess fortgesetzt. Dank der Rekursivität des Compilers lassen sich selbst solche Sichten, die auf anderen Sichten aufbauen, verarbeiten. Die Mengen für das Beispiel deutsche_Belletrisktik sehen wie folgt aus: Pos_Pol enthält nur ein Objekt der Klasse „Relation“ mit Name = deutscher_Bücher Index = 11 Neg_Pol enthält nur ein Objekt der Klasse „Relation“ mit Name = Fachliteratur Index =19 Index_not =13 Alle_Rel enthält beide Objekte. 6.1.3 Code Generator Der Code Generator ist der letzte Teil des Compilers. Nachdem im Parser eine SQL-Sicht in eine Liste von Tokens zerlegt wurde, und im Analyser diese Liste bearbeitet und die weiteren Listen erzeugt wurden, besteht die Aufgabe des Code Generators nun darin, die in den Parser- und Analyse-Teilen erzeugten Listen/ Mengen in Delta-Sichten und Delta2Sichten – Sichten für den Effektivitätstest beziehungsweise spezielle Sichten zur Behandlung der Aggregatfunktionen – sowie Transitionssichten umzuwandeln und in der Datenbank abzuspeichern. Der Code Generator wird in vier Schritten ausgeführt. Im ersten Schritt werden für alle Tabellen aus der Menge Rel-Alle die Tabellen Plus und Minus sowie die inkrementellen Transitionssichten mittels der Methode Transitionssicht_Inkrementell (Tabellenname) in die Datenbank eingefügt. Im zweiten Schritt wird die naive Transitionssicht für die bearbeitete Sicht durch die Methode Transitionssicht_Naiv(Tokenliste, Sichtname, Alle_Rel) erstellt und im Datenbankschema gespeichert. Für eine Sicht, die von n Relationen abhängt, geschieht dies durch den n-maligen Aufruf der Methode Sub Ersetzen (Byref Tokenliste, Objekt Der Klasse „Relation“, Zeichenkette Durch). Der Methode wird eine Liste der Token der Sichtdefinition übergeben. Sie ändert die Liste und gibt die geänderte Version zurück, wobei der Relationsname, der mit dem Objekt der Klasse „Relation“ an die Methode übergeben wurde, überall durch die Zeichenkette Durch ersetzt wird. Beim ersten Aufruf wird der Methode die Kopie der Tokenliste übergeben. Bei n-1 Aufrufen wird dieselbe Kopie weiter verändert. Bei jedem der n Aufrufe wird eine der Relationen, auf welchen die Sicht aufbaut, durch die entsprechende Relation mit dem 61 Implementierung _ Schlüsselwort new im Namen ersetzt. Diese Liste wird dann durch die Methode SQLstring_Aufbauen( Listenkopien) zu einer SQL-Zeichenkette zusammengesetzt und durch die Access Standard-Methode (dem Leser bekannt aus Abschnitt 3.4) Currentdb.Createquerydef unter dem Namen „Sichtname_new“ in die aktuelle Datenbank eingefügt. Im dritten Schritt werden dann die Delta-Sichten kreiert. Diese Aufgabe übernehmen die Prozeduren Delta_Sicht_Plus(Tokenliste, Sichtname) und Delta_Sicht_Minus(Tokenliste, Sichtname). Diese Prozeduren implementieren den „Algorithmus für die Delta-Minus-Sicht“ sowie den „Algorithmus für die Delta-Plus-Sicht“ – dem Leser beide aus Abschnitt 4.6 bekannt. Die Zeile „ersetze…“ aus den Algorithmen wird durch die Methode Sub Ersetzen(Byref Tokenliste, Objekt Der Klasse „Relation“, Zeichenkette Durch) realisiert. Die Zeichenkette Durch ist „Relationsname_New“, „Relationsname_Plus“ oder „Relationsname_Minus“. Falls es sich um eine Relation mit negativer Polarität handelt, die durch „Relationsname_Plus“ oder „Relationsname_Minus“ ersetzt wird, so wird der entsprechende NOT EXISTS- bzw. NOT IN-Ausdruck durch EXISTS bzw. IN ersetzt, indem das entsprechende NOT, dessen Position wiederum in dem Objekt der Klasse „Relation“ im Attribut Not_Index steckt, aus der Listenkopie gelöscht wird. Für eine Sicht, die auf n Relationen aufbaut, werden jeweils für die Delta-Sichten n SELECT-Statements generiert. Diese SELECT-Statements werden mit Hilfe des Operators UNION ALL zu den Delta_Plus/_Minus-Sichten verbunden. Für jedes der SELECT-Statements der Delta_Plus-Sicht wird durch den n-maligen Aufruf der Methode Ersetzen eine Tokenliste erstellt. Beim ersten Aufruf wird der Methode die Kopie der Tokenliste sowie die Relation übergeben, die durch Relationsname_Plus oder Relationsname_Minus (im Falle einer negativen Polarität der Relation) ersetzt werden soll. Bei anderen n-1 Aufrufen wird der Methode dieselbe – bereits von ihr geänderte – Liste übergeben sowie andere Relationen, die durch Relationsname_New ersetzt werden müssen. Für jedes der SELECT-Statements der Delta_Minus-Sicht wird durch den 1-maligen Aufruf der Methode Ersetzen eine Tokenliste erstellt. Beim Aufruf wird der Methode die Kopie der Tokenliste übergeben sowie die Relation, die durch Relationsname_Plus oder Relationsname_Minus (im Falle einer negativen Polarität der Relation) ersetzt werden soll. Die anderen n-1 Aufrufe benötigt man in diesen Fall nicht, da in dieser Arbeit ein neuer Datenbankzustand simuliert wird, und die Relationen nicht durch die Relationen mit dem Schlüsselwort Old im Namen ersetzt zu werden brauchen. Somit werden für jede Delta-Sicht der Sicht, die auf n Relationen aufbaut, n solcher Listen generiert. Diese n Listen werden dann an die Methode SQLstring_Aufbauen( Listenkopien) übergeben, wo sie Token für Token eine nach der anderen, verknüpft durch den Operator UNION ALL, als Zeichenkette aufbereitet werden. Diese Zeichenkette wird an die Access Standard-Methode Currentdb.Createquerydef (dem Leser bekannt aus Abschnitt 3.4) zusammen mit dem neuen Sichtnamen (wie z.B Sichtname + _Plus /_Minus) übergeben. 62 Kapitel 6 Beispiel: generierte Listen für die Sicht deutsche_Belletristik_Plus 1 Liste: (ersetze Relation deutscher_Bücher durch deutscher_Bücher_Plus in der Kopie der Tokenliste) [ 'SELECT', 'deutscher_Bücher_Plus', '.Autor', ',', 'deutscher_Bücher_Plus', '.Titel', ',', 'deutscher_Bücher_Plus', '.Art', 'FROM', 'deutscher_Bücher_Plus', 'WHERE', 'NOT', 'EXISTS', '(', 'SELECT', '*', 'FROM', 'Fachliteratur', 'WHERE', 'Fachliteratur', '.Autor', '=', 'deutscher_Bücher_Plus', '.Autor', 'and', 'Fachliteratur', '.Titel', '=', 'deutscher_Bücher_Plus', '.Titel', ')' ] 1.Liste: (ersetze Relation Fachliteratur durch Fachliteratur_new in Liste 1) [ 'SELECT', 'deutscher_Bücher_Plus', '.Autor', ',', 'deutscher_Bücher_Plus', '.Titel', ',', 'deutscher_Bücher_Plus', '.Art', 'FROM', 'deutscher_Bücher_Plus', 'WHERE', 'NOT', 'EXISTS', '(', 'SELECT’, '*', 'FROM', 'Fachliteratur_new', 'WHERE', 'Fachliteratur_new', '.Autor', '=', 'deutscher_Bücher_Plus', '.Autor', 'and', 'Fachliteratur_new', '.Titel', '=', 'deutscher_Bücher_Plus', '.Titel', ')' ] 2 Liste (ersetze Relation Fachliteratur durch Fachliteratur_Minus in der Kopie der Tokenliste) [ 'SELECT', 'deutscher_Bücher’, '.Autor', ',', 'deutscher_Bücher’, '.Titel', ',', 'deutscher_Bücher’, '.Art', 'FROM', 'deutscher_Bücher’, 'WHERE', 'EXISTS', '(', 'SELECT', '*', 'FROM', 'Fachliteratur_Minus', 'WHERE', 'Fachliteratur_Minus', '.Autor', '=', 'deutscher_Bücher’, '.Autor', 'and', 'Fachliteratur_Minus', '.Titel', '=', 'deutscher_Bücher’, '.Titel', ')', ] 2 Liste (ersetze Relation deutscher_Bücher durch deutscher_Bücher_new in Liste 1) [ 'SELECT', 'deutscher_Bücher_new', '.Autor', ',', 'deutscher_Bücher_new', '.Titel', ',', 'deutscher_Bücher_new', '.Art', 'FROM', 'deutscher_Bücher_new', 'WHERE', 'EXISTS', '(', 'SELECT', '*', 'FROM', 'Fachliteratur_Minus', 'WHERE', 'Fachliteratur_Minus', '.Autor', '=', 'deutscher_Bücher_new', '.Autor', 'and', 'Fachliteratur_Minus', '.Titel', '=', 'deutscher_Bücher_new', '.Titel', ')', ] 63 Implementierung _ Abbildung 6.3 Compileraufbau 64 Kapitel 6 Abbildung 6.4 Compileraufbau Liste 1 und Liste 2 werden durch den Operator UNION ALL zu der – dem Leser schon aus dem Beispiel 4.2 bekannten – Delta-Sicht deutsche_Belletristik_Plus zusammengesetzt. Dabei sei angemerkt, dass die virtuelle Relation deutsche_Belletristik selbst auf der Sicht deutscher_Bücher basiert. Hierbei wird die Sicht deutscher_Bücher genauso wie eine Basisrelation behandelt, nur dass deutscher_Bücher_Plus und deutscher_Bücher_Minus keine Tabellen, sondern Delta-Sichten sind. Im vierten und letzten Schritt schließlich werden in Abhängigkeit davon, ob die Aggregatfunktionen in der Haupt-Select-Klausel auftreten oder nicht, Aggregatfunktion_Min_Max_First_Last(Tokenliste,Sichtname), Aggregatfunktion_Count_Sum_Avg(Tokenliste,Sichtname) oder 65 Implementierung _ Effektivitätstest(Sichtname) aufgerufen. Diese Methoden wiederum bilden und fügen die Delta2-Sichten, die entweder den Effektivitätstest oder spezielle Auswertungen für die Aggregatfunktionen darstellen, der aktuellen Datenbank hinzu(siehe Kapiteln 5.2 und 5.5). Die Abbildungen 6.3 und 6.4 zeigen das Aktivitätsdiagramm des in der Diplomarbeit entwickelten Compilers. 6.2 Einbettung in MS Access Der Compiler wurde durch mehrere benutzerdefinierte Prozeduren und Funktionen realisiert. Für die Testzwecke – und um den Compiler bequem benutzen zu können – wurde ein Programm geschrieben, das es erlaubt, für eine ausgewählte Sicht die Delta-Sichten zu generieren, Änderungen in die Plus-/Minus-Tabellen einzutragen und die geänderten Fakten in den Delta-Sichten anzusehen. Für einen anwenderfreundlichen Umgang mit dem Programm werden insgesamt vier Formulare benötigt: „Start“, „Sichten_Generieren“, „Tabellen_Ändern“ und „Sichten_Ansehen“. Formular Anwender Compiler MS Access DBMS Meta-DB DB Abbildung 6.5 Systemarchitektur 66 Kapitel 6 Abbildung 6.6 Formular „ Start“ Abbildung 6.5 zeigt, wie das Programm und der Compiler in die MS Access-Datenbank integriert wurden. Der Anwender arbeitet nur mit den Formularen. Das Formular übergibt dem Compiler die SQL-Definition der Sicht, für die die Delta-Sichten generiert werden müssen, und der Compiler legt die dafür generierten Delta- und Transitionssichten sowie die Hilfstabellen in die aktuelle Datenbank. Aus dem Formular heraus kann der Anwender die Plus-/Minus-Tabellen ändern und die Delta-Sichten ansehen. Der Compiler wird aus dem Formular „Start“ (Abbildung 6.6) gestartet. Beim ersten Aufruf des Formulars ist nur eine Option auswählbar: „Sichten generieren“. Bei Auswahl dieser Option wird das Formular „Sichten_generieren“ aufgerufen (Abbildung 6.7). Dort werden dem Anwender die Sichten zur Auswahl vorgeschlagen, die in der aktuellen Datenbank enthalten sind. Für die ausgewählte Sicht wird deren SQL-Definition aus der aktuellen Datenbank gelesen und an den Compiler übergeben. Der Compiler wird nun gestartet. Falls die Sichtattribute auf den Aggregatfunktionen aufbauen, wird die Meldung aus Abbildung 6.8 angezeigt. 67 Implementierung _ Abbildung 6.7 Formular „Sichten_generieren“ Abbildung 6.8 Warnung Bei Sichten, die keine Aggregatfunktionen in der Haupt-Select-Klausel enthalten, werden in dieser Arbeit Duplikate grundsätzlich ignoriert. Wie schon in Kapitel 5.5 dargelegt, werden die Duplikate bei Sichten, deren Attribute auf Aggregatfunktionen basieren, nicht immer richtig ausgewertet. Ich habe mich aber trotzdem dazu entschieden, solche Sichten – auch wenn die Richtigkeit der Duplikatenauswertung nicht immer garantiert ist – zu bearbeiten. Der Anwender wird an dieser Stelle auf die dadurch möglicherweise entstehenden Fehler hingewiesen. Falls die Sichtdefinition Konstruktionen enthält, die der Compiler nicht auswertet, erscheint die Fehlermeldung aus Abbildung 6.9, und die Verarbeitung wird abgebrochen. 68 Kapitel 6 Abbildung 6.9 Fehlermeldung Falls in der Sicht Aggregatfunktionen in Selektions- und Gruppierungsbedingungen vorkommen, wird die Verarbeitung mit der Meldung aus Abbildung 6.10 abgebrochen. Abbildung 6.10 Fehlermeldung 2 Falls der Anwender fälschlicherweiseweise den Compiler für dieselbe Sicht mehrmals startet, erscheint die Meldung aus Abbildung 6.11 Abbildung 6.11 Infomeldung Wenn der Compiler seine Arbeit beendete hat, so sind alle benötigten Tabellen und Sichten, die in Kapitel 4 beschrieben wurden, angelegt. Der Compiler kann für mehrere Sichten gestartet werden. Erst jetzt sind auch andere Optionen auswählbar. Mit der Option „Tabellen ändern“ wird das Formular „Tabellen_ändern“ (Abbildung 6.12) gestartet. Im Formular kann der Anwender Plus/Minus-Tabellen aufrufen und Änderungen (Einfügungen oder Löschungen) darin zwischenspeichern. Dabei handelt es sich um die Plus-/Minus-Tabellen jener Tabellen, auf denen die Sichten aufbauen, und für die vorher im Formular „Sichten_generieren“ der Compiler gestartet wurde. Wenn man vorher die Delta-Sichten für die Sicht „Deutsche_Belletristik“ generiert hat, so werden dem Anwender die folgenden Tabellen – genauer gesagt: deren Plus-/Minus-Tabellen – zum Ändern vorgeschlagen: Bücher, Autoren und Fachliteratur. Dabei werden einfach die noch leeren Formulare der Plus-/Minus-Tabellen aufgerufen und zum Ändern bereitgestellt. Durch Klick auf den Button „Verwerfen“ kann man die Änderungen in den Plus-/Minus-Tabellen der gerade ausgewählten Tabelle wieder löschen. 69 Implementierung _ Abbildung 6.12 Formular „Tabellen_ändern“ Die Option „Sichten ansehen“ startet das Formular „Sichten_ansehen“ (Abbildung 6.13). Im Formular kann man die Delta-Sichten, also die geänderten Fakten der ursprünglichen Sicht, ansehen. Dabei werden für die Sichten, für die entweder der Effektivitätstest oder die speziellen Sichten für die MIN/MAX/FIRST/LAST-AggregatsfunktionenAuswertung generiert wurden, die Delta2_Sichten aufgerufen, welche separat die eingefügten und die gelöschten Fakten anzeigen. Für die Sichten, deren Attribute auf Aggregatfunktionen wie COUNT, SUM oder AVG basieren, wird nur eine Delta3-Sicht aufgerufen, bei der man die neuen Wert der ursprünglichen Sicht und nicht die DeltaFakten sieht. Jedes der drei Formulare verfügt über einen „Zurück“-Button, der das betroffene Formular schließt und den Fokus wieder auf das Hauptformular „Start“ setzt. Der Anwender kann die Änderungen speichern oder aber verwerfen. Das Formular „Start“ verfügt über zwei entsprechende Knöpfe. Das heißt, die Änderungen aus den Plus/Minus-Tabellen werden entweder in die Basistabellen übertragen oder unwiderruflich gelöscht. Alle Änderungen, die der Benutzer in die vorhandenen Plus-/Minus-Tabellen eingegeben hat, werden INNERHALB EINER Transaktion an die Datenbank übergeben. Falls eine der geänderten Fakten die Datenbankintegrität verletzt und nicht eingefügt/ gelöscht werden kann, werden alle Änderungen an den Basistabellen verworfen, und der Benutzer wird durch die Nachricht aus Abbildung 6.14 davon unterrichtet. Die Änderungen werden dabei in Plus-/Minus-Tabellen enthalten bleiben. Dem Anwender wird somit die Möglichkeit gegeben, die Daten zu korrigieren. Die Daten können eventuell nicht eingefügt/ gelöscht werden, falls dadurch die Entity-Integrität oder die 70 Kapitel 6 referentielle Integrität (falls die Tabellen in Beziehung zueinander stehen und die referentielle Integrität aktiviert ist – siehe Abschnit 2.3.3) verletzt werden, oder man versucht, nicht vorhandene Fakten zu löschen, oder die Tabellen von anderen Programmen oder Prozessen geblockt werden. Werden Daten erfolgreich in die Datenbank übertragen, so erscheint die Meldung aus Abbildung 6.15. Abbildung 6.13 Formular „Sichten_ansehen“ Abbildung 6.14 Fehlermeldung 3 Falls der Benutzer versucht, das Programm – also in diesem Fall das Formular “Start“ – zu schließen, ohne vorher die Änderungen zu speichern oder zu verwerfen, so wird er vom Programm gefragt, wie er mit den Änderungen verfahren möchte. Bei Programmende werden alle generierten Sichten und neu angelegten Tabellen gelöscht. Alle Datenbankschema-Änderungen werden zurückgenommen. 71 Implementierung _ Abbildung 6.2-15 Infomeldung 2 6.3 Compiler-Testbeispiele Da es leider nicht möglich ist, den Compiler über eine formale Spezifikation zu prüfen, wurde der Compiler durch das systematische Testen geprüft. Die Tests sollen aufgrund der tatsächlichen Sichtänderungen und der Änderungen, welche die Delta-Sichten anzeigen, überprüfen, ob die Delta-Sichten richtig erstellt wurden und der Compiler eine korrekte Übersetzung leistet. Alle Testbeispiele zu zeigen, in denen der Compiler geprüft wurde, würde den Rahmen dieser schriftlichen Arbeit sprengen. Im Folgenden werden nur zwei der komplexeren Beispielfälle gezeigt. Der jeweilige Test wird auf folgende Weise durchgeführt: Zuerst wird die Sicht für den Test ausgewählt und der Inhalt der relevanten Basistabellen festgehalten. Der Compiler wird für die ausgewählte Sicht gestartet. Als nächstes werden die Änderungen in die entsprechenden Plus-/Minus-Tabellen eingetragen und festgehalten. Die Delta-Sichten werden ausgeführt und deren Ergebnisse protokolliert. Die Änderungen werden aus den Plus-/Minus-Tabellen mit Hilfe des Formulars "Start" in die entsprechenden Tabellen übertragen. Als letzter Schritt wird die ausgewählte Sicht nochmals, diesmal im neuen Datenbankzustand, ausgeführt. Die Inhalte der ausgewählten Sicht im alten Datenbankzustand und der Inhalt der Delta-Sichten werden zusammengerechnet und mit dem Inhalt der Sicht im neuen Datenbankzustand verglichen. Falls die Mengen übereinstimmen, arbeitet der Compiler korrekt. 6.3.1 Test 1 Die Sicht hat keine bestimmte Bedeutung. Sie wird an dieser Stelle lediglich benutzt, um zu demonstrieren, welche Konstruktionen mit dem Compiler ausgewertet werden können. Sichtdefinition: SELECT DISTINCT A.Name FROM AAA as A, BBB as B WHERE A.Name=B.Name AND EXISTS (SELECT * FROM CCC AS C WHERE A.Name<>C.Name ) OR A.Name NOT IN (SELECT NAME FROM DDD AS D ) UNION SELECT E.Name FROM EEE AS E 72 Kapitel 6 WHERE EXISTS ( SELECT * FROM FFF AS F WHERE e.Name= F.Name) OR e.name NOT IN (SELECT G.Name FROM GGG as G INNER JOIN HHH as H ON G.Name=H.Name WHERE G.Name NOT IN (SELECT A.NAME FROM AAA AS A WHERE A.Name IS NOT NULL )); Relevante Basistabellen im alten Datenbankzustand: 73 Implementierung Sicht im alten Datenbankzustand: Relevante Plus-/Minus-Tabellen: 74 _ Kapitel 6 Delta-Sichten: Sicht im neuen Datenbankzustand: Der Inhalt der ausgewählten Sicht im alten Datenbankzustand und der Inhalt der DeltaSichten zusammengerechnet, stimmt mit dem Inhalt der Sicht im neuen Datenbankzustand überein. 6.3.2 Test 2 Die beiden Sichten deutscher_Bücher und deutsche_Belletristik aus Beispiel 2.2.3, welche bei diesem Test zum Einsaz kommen, wurden schon mehrmals in dieser Arbeit erwähnt. Dieser Test hier soll zeigen, dass sogar Sichten, die von anderen Sichten abhängen, durch den Compiler auswertbar sind. In diesem Fall hängt die Sicht Deutsche_Belletristik von der Sicht Deutscher_Bücher ab. Man braucht lediglich die Sicht Deutsche_Belletristik an 75 Implementierung _ den Compiler zu übergeben – die Sicht Deutscher_Bücher dagegen kann, muss aber nicht explizit übergeben werden. Die Delta-Sichtenfür die beiden Sichten sehen wie folgt aus: Delta_Plus-Regel für deutscher_Bücher: SELECT Bücher_Plus.Autor , Bücher_Plus.Titel , Bücher_Plus.Art FROM Bücher_Plus , Autoren_New WHERE Bücher_Plus.Autor = Autoren_New.Autor And Autoren_New.Land = "Deutschland" UNION ALL SELECT Bücher_New.Autor , Bücher_New.Titel , Bücher_New.Art FROM Bücher_New , Autoren_Plus WHERE Bücher_New.Autor = Autoren_Plus.Autor And Autoren_Plus.Land = "Deutschland"; Delta_Minus-Regel für deutscher_Bücher: SELECT Bücher_Minus.Autor , Bücher_Minus.Titel , Bücher_Minus.Art FROM Bücher_Minus , Autoren WHERE Bücher_Minus.Autor = Autoren.Autor And Autoren.Land = "Deutschland" UNION ALL SELECT Bücher.Autor , Bücher.Titel , Bücher.Art FROM Bücher , Autoren_Minus WHERE Bücher.Autor = Autoren_Minus.Autor And Autoren_Minus.Land = "Deutschland"; Delta_Plus-Regel für deutsche_Belletristik: SELECT deutscher_Bücher_Plus.Autor , deutscher_Bücher_Plus.Titel , deutscher_Bücher_Plus.Art FROM deutscher_Bücher_Plus WHERE NOT EXISTS ( SELECT * FROM Fachliteratur_New WHERE Fachliteratur_New.Autor=deutscher_Bücher_Plus.Autor AND Fachliteratur_New.Titel=deutscher_Bücher_Plus.Titel ) UNION ALL SELECT deutscher_Bücher_New.Autor , deutscher_Bücher_New.Titel , deutscher_Bücher_New.Art FROM deutscher_Bücher_New WHERE EXISTS ( SELECT * FROM Fachliteratur_Minus WHERE Fachliteratur_Minus.Autor = deutscher_Bücher_New.Autor AND Fachliteratur_Minus.Titel = deutscher_Bücher_New.Titel); Delta_Minus-Regel für deutsche_Belletristik: 76 Kapitel 6 SELECT deutscher_Bücher_Minus.Autor , deutscher_Bücher_Minus.Titel ,deutscher_Bücher_Minus.Art FROM deutscher_Bücher_Minus WHERE NOT EXISTS ( SELECT * FROM Fachliteratur WHERE Fachliteratur.Autor = deutscher_Bücher_Minus.Autor AND Fachliteratur.Titel = deutscher_Bücher_Minus.Titel ) UNION ALL SELECT deutscher_Bücher.Autor , deutscher_Bücher.Titel , deutscher_Bücher.Art FROM deutscher_Bücher WHERE EXISTS ( SELECT * FROM Fachliteratur_Plus WHERE Fachliteratur_Plus.Autor = deutscher_Bücher.Autor AND Fachliteratur_Plus.Titel = deutscher_Bücher.Titel ); Relevante Basistabellen im allten Datenbankzustand: 77 Implementierung Sicht im alten Datenbankzustand: Relevante Plus-/Minus-Tabellen: 78 _ Kapitel 6 Delta-Sichten: 79 Implementierung _ Sichten im neuen Datenbankzustand: Der Inhalt der ausgewählten Sicht im alten Datenbankzustand und der Inhalt der DeltaSichten zusammengerechnet, stimmt mit dem Inhalt der Sicht im neuen Datenbankzustand überein. 6.4 Schlussfolgerung Die Überprüfung der Testdaten war erfolgreich. Daraus lässt sich schließen, dass die Übersetzung der SQL-Sichten in die Delta-Sichten mit Hilfe des Compilers korrekt ist. Durch die Tests wurde festgestellt, dass die Performance der Übersetzungsalgorithmen gut ist. Sogar bei verschachtelten Sichten wurden die Definitionen ohne Verzögerungen übersetzt. Alle Übersetzungen dauerten weniger als 1 Sekunde. Die Dauer der Auswertung der Delta-Sichten hängt von der benutzten Hardware und der Komplexität der zugrunde liegenden Sichten und Datenmengen ab – im Average-Case dauert es nur wenige Sekunden, im Worst-Case dagegen kann es bis zu einigen Minuten dauern. 80 Kapitel 7 7 Zusammenfassung und Perspektiven In der vorliegenden Arbeit wurde eine inkrementelle Sichtanpassung in einer relationalen Datenbank, MS Access 2002, realisiert. Dafür wurde ein Compiler für die Übersetzung der SQL-Sichten in Delta-Sichten entworfen und implementiert. Zusammenfassung Zu Beginn wurden die Grundlagen dieser Arbeit erläutert. SQL wurde in Abschnitt 2 vorgestellt. In Kapitel 3 wurden die Grundlagen der MS Access Datenbank sowie der Programmiersprache VBA erklärt. In Abschnitt 4 wurden relationale deduktive Datenbanken und deren Anfragesprache Datalog sowie das Konzept der inkrementellen Sichtenanpassung erläutert.. In Abschnitt 5 wurde ein Algorithmus entwickelt, der DeltaSichten für SQL-Sichten erstellt, ohne dass erstere in datalogähnliche Form überführt werden müssten. Bei dieser Arbeit wurde nicht eine Teil-Sprache von SQL betrachtet, sondern es wurde in erster Linie darauf geachtet, dass die von dem Compiler bearbeiteten Sichten die Möglichkeiten von Access-SQL zum größten Teil ausschöpfen können. Es wurde festgestellt, dass der Compiler SELECT-Statements mit Verschachtelungen beliebiger Tiefe, die durch Operatoren (NOT) EXISTS und (NOT) IN eingeleitet werden, bearbeiten kann. Operatoren und Schlüsselwörter wie DISTINCT, DISTINCTROW, IN, BETWEEN, LIKE, IS (NOT) NULL, IIF (<Expr>, True, False) oder AS, welche in der WHERE-Bedingung oder in der SELECT-Klausel auftreten können, stellen dabei kein Problem dar. Der einzige Mengenoperator – UNION – und der INNER JOINOperator werden vom Compiler ebenfalls richtig übersetzt. Es haben sich aber Beschränkungen für die SQL-Anweisungen, die vom Compiler bearbeitet werden, herausgestellt. Es wurde festgestellt, dass der für diese Arbeit entwickelte Compiler die LEFT/RIGHT (OUTER) JOINs nicht richtig auswerten kann. Es wurde allerdings auch eine Lösung für dieses Problem vorgeschlagen. Außerdem wurde festgestellt, dass das Problem der Duplikate in dieser Arbeit nur bedingt gelöst wurde. Standard-Aggregatfunktionen wie MIN, MAX, COUNT, SUM oder AVG, die in der HauptSELECT-Klausel auftreten, werden zwar vom Compiler bearbeitet – allerdings ist, da die richtige Behandlung von Duplikaten durch den Compiler nicht garantiert ist, auch die Korrektheit der Auswertung der Aggregatfunktionen nicht garantiert. 81 Zusammenfassung und Perspektiven _ In Abschnitt 6 wurde die Implementierung des Compilers im VBA-Kontext vorgestellt sowie das Programm und die dabei verwendeten Formulare erläutert und abgebildet. Für die Testzwecke – und um den Compiler bequem benutzen zu können – wurde ein Programm geschrieben, das es erlaubt, für die ausgewählte Sicht die Delta-Sichten zu generieren, Änderungen in die Plus-/Minus-Tabellen einzutragen sowie die geänderten Fakten in den Delta-Sichten anzusehen. Die Überprüfung der Testdaten war erfolgreich. Daraus lässt sich schließen, dass die Übersetzung der SQL-Sichten in die Delta-Sichten mit Hilfe des Compilers korrekt ist. Alle Übersetzungen dauerten weniger als 1 Sekunde. Perspektiven Der Compiler kann leicht um eine Verarbeitung der OUTER JOIN-Operatoren ergänzt werden, indem man LEFT durch RIGHT sowie RIGHT durch LEFT ersetzt, so dass unbedingt alle Einträge der Plus-/Minus-Tabellen komplett in den Join übernommen werden – und nicht die der New-(/Old-)Tabellen (siehe Abschnitt 5.3). Als mögliche Weiterentwicklung des in dieser Arbeit beschriebenen Compilers kann eine Verarbeitung der Aggregatfunktionen in den Selektions- und Gruppierungsbedingungen und eine Garantie für die Richtigkeit der Duplikatenverarbeitung genannt werden. Bis jetzt wurde die Änderungspropagierung nur in Bezug auf Einfügungen und Löschungen betrachtet; sie kann aber auch auf Modifikationen, also auf Änderungen schon existierender Fakten, übertragen werden. Man kann schon jetzt die Modifikationen durch die vom Compiler erstellten Delta-Sichten auswerten lassen. Dazu muss man die Änderungen schon existierender Fakten als Löschung und Wiedereinfügung betrachten. Allerdings wird ein so modifizierter Fakt einmal als gelöschter Fakt in der alten Konstellation sowie einmal als neu eingefügter Fakt in der neuen Konstellation angezeigt. 82 Literaturverzeichnis Literaturverzeichnis [ANSI99I] ANSI/ISO/IEC 9075-1-1999: Database Languages - SQL – Part l:Framework. ANSI American National Standards Institute, New York, 1999. http://www.cse.iitb.ac.in/dbms/Data/Papers-Other/SQL1999/ansi-iso9075-1-1999.pdf [ANSI99II] ANSI/ISO/IEC 9075-2-1999: Database Languages - SQL – Part 2: Foundation. ANSI American National Standards Institute, New York, 1999. http://www.cse.iitb.ac.in/dbms/Data/Papers-Other/SQL1999/ansi-iso9075-2-1999.pdf [ASU88] Aho, A.V., Sethi, R. & Ullmann, J.D.: Compilerbau. Teil 1, Teil 2. Addison-Wesley, Deutschland, 1988 [BMS86] Bancilhon, F., Maier, D., Ullman, J., Sagiv, Y.: "Magic Sets and other strange ways to implement logic programs" [Bro99] Brosius, G.: “Access 2000 professionell: Datenbank-Management mit Office 2000”. Addison-Wesley-Longman, 1999 [Cod70] Codd,. E.F. “A Relational Model of Data for Large Shared Data Banks”. Communications of the ACM, 13(6),June 1970. [Cal04] Calí, V.. Erweiterungskonzepte für das aktive Datenbanksystem ARTA. Rheinische Friedrich-Wilhelms-Universität Bonn, 2004 [GMS93] Gupta, A., Mumick, I.S., Subrahmanian, V.S.: Maintaining Views Incrementally. Proc. SIGMOD 1993, Washington/DC, USA, S. 157-166. [HS02] Hoffbauer, M/ Spielmann, C. Access XP Version 2002 Programmierung – das Buch. SYBEX-Verlag , Düsseldorf , 1. Auflage, 2001 [KE01] Kemper, A. Eicker, A. Datenbanksysteme. Oldenburg Verlag, München, 4.Auflage, 2001 83 Literatursverzeichnis [Jet] Microsoft Access Hilfe http://msdn.microsoft.com/library/default.asp?url=/library/ens/dnacc2k/ht ml/acfundsql.asp [Man00] Manthey, R.: Skript zur Vorlesung Deduktive Datenbanken WS00/01, Universität Bonn, Institut für Informatik III. [Man02] Manthey, R.: Skript zur Vorlesung Informationssysteme Universität Bonn, Institut für Informatik III [Mar01] Martini,R.:Effiziente Implementierung des alternierenden Fixpunktoperators unter Verwendung von Änderungspropagierung. Rheinische Friedrich-Wilhelms-Universität Bonn, 2001 [Mul03] Mullere, P.: Entwurf und Implementierung eines Compilers zur Übersetzung von SQL-Anfragen nach Datalog. Rheinische Friedrich-Wilhelms-Universität Bonn, 2003 [ Sta99] Staas, D.: VBA Programmierung mit Office 2000. Carl Hanser Verlag, München/ Wien, 1999 [WiPe] http://wikipedia.t-st.de/ 84 WS02/03,