PDF file - IDB - Universität Bonn

Werbung
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,
Herunterladen