Semantik und Realisierung einer erweiterten Relationenalgebra für temporale Datenbanken Diplomarbeit im Studiengang Mathematik mit Studienrichtung Informatik vorgelegt von Christian Stahlhut Matrikelnummer: 1937473 Universität Hannover Institut für Informationssysteme Fachgebiet Datenbanksysteme Prüfer: Prof. U. Lipeck Zweitprüfer: Prof. R. Parchmann Hannover, den 27. Juli 2004 Zusammenfassung In dieser Arbeit wird ein Konzept zur relationalen Modellierung von zeitabhängigen ( temporalen“) Daten beschrieben. Zunächst wird der zugrundeliegende Zeitbegriff nä” her erläutert. Dann kann die klassische Relationenalgebra um einen Zeitbezug erweitert werden. Anhand dieser Erweiterung wird diskutiert wie temporale Datenbanken zu modellieren sind und welche zusätzlichen Integritätsbedingungen es zu beachten gilt. Durch Anfragebeispiele für konkrete temporale Datenbanken wird versucht eine gewisse Pragmatik bezüglich der erweiterten Relationenalgebra aufzubauen. Ein weiterer Schritt zur Realisierung der erweiterten Relationenalgebra ist die Optimierung der erweiterten Operatoren. Abschließend wird eine beispielhafte Implementierung der Algebra als Anfragesprache auf Grundlage des relationalen Datenbankmanagementsystems Oracle vorgestellt. Inhaltsverzeichnis 1. Einleitung 5 2. Semantik temporaler Daten 2.1. Zeitstempel, Chronons und Granularität . . . . . . . . . . . . . . . . . . 2.2. Modellierung des Zeitstempels . . . . . . . . . . . . . . . . . . . . . . . . 2.3. Gültigkeitszeit, Transaktionszeit und benutzerdefinierte Zeit . . . . . . . 7 7 7 8 3. Definition der erweiterten Relationenalgebra 3.1. Relationen . . . . . . . . . . . . . . . . . . 3.2. Intervalle . . . . . . . . . . . . . . . . . . 3.3. pack und unpack . . . . . . . . . . . . . . 3.4. Erweiterung der Relationenalgebra . . . . . . . . 10 10 15 18 22 4. Modellierung temporaler Datenbanken 4.1. Integritätsbedingungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. Tupel- oder Attributzeitstempel . . . . . . . . . . . . . . . . . . . . . . . 4.3. Zukunft, Gegenwart und Vergangenheit . . . . . . . . . . . . . . . . . . . 24 24 29 32 5. Pragmatik – Anfragebeispiele 5.1. Beispiele aus [Bei2001a]– ein Vergleich mit TSQL2 . . . . . . . . . . . . . 5.2. Beispiele aus [DDL2003a] . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3. Beispiele zu Gruppierung und Aggregation . . . . . . . . . . . . . . . . . 35 35 38 40 6. Optimierung 6.1. Zerlegung in Teilintervalle – split 6.2. Vermeidung von unpack und pack . 6.3. Verbesserungen für pack . . . . . . 6.4. Verbesserungen für unpack . . . . . 6.5. Verbesserungen für split . . . . . . . . . . 44 44 46 54 57 57 7. Realisierung 7.1. Erweiterung des DBMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2. Übersetzung von Termen . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3. Implementierung des Evaluators . . . . . . . . . . . . . . . . . . . . . . . 61 61 69 72 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inhaltsverzeichnis 8. Ausblick 75 A. Intervalle und Intervalloperatoren 77 B. Grammatik der Anfragesprache B.1. Terminalsymbole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2. Grammatik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 78 78 4 1. Einleitung Motivation und Zielsetzung Veränderungen unter dem Einfluß der Zeit sind ein alltägliches Phänomen. Zeitliche Veränderung zu beschreiben erfordert gewisse Begriffe. Sollen solche Beschreibungen in einem Datenbankmanagementsystem (DBMS) realisiert werden, sind also zunächst entsprechende Begriffe zu schaffen. Moderne kommerzielle DBMS unterstützen jedoch den Umgang mit zeitabhängigen (temporalen) Daten bisher kaum. Allerdings ist heute die Speicherung großer (historischer) Datenmengen ökonomisch vertretbar, da Speicherkosten fast kein Problem mehr darstellen. Außerdem sind Modellierung und Verarbeitung temporaler Daten Aufgaben, die immer mehr an Bedeutung gewinnen – zum Beispiel in Data Warehouses. DBMSe um zeitliche Funktionalität zu erweitern ist also naheliegend. Ziel dieser Arbeit soll es demnach sein, durch eine erweiterte Relationenalgebra zunächst die Begriffe zu schaffen, die in einem relationalen DBMS (RDBMS) für den Umgang mit temporalen Daten benötigt werden. Ausgehend von dieser Algebra ist exemplarisch eine temporale Erweiterung für das RDBMS Oracle zu realisieren. Quellen und verwandte Arbeiten Arbeiten zu diesem Thema entstanden schon vor einiger Zeit (etwa [TCG93a]): Eine Erweiterung der deskriptiven Anfragesprache SQL (Structured Query Language) um temporale Elemente wurde unter dem Namen TSQL2 ([Sno95a]) dem ISO-Komitee zur Standardisierung vorgelegt. Verschiedene Probleme mit dem TSQL2-Ansatz führten jedoch dazu, dass bis heute (2004) keine solche SQL-Erweiterung verabschiedet1 ist. Einige der Kritiker von TSQL2 veröffentlichten vor kurzem das Buch Temporal Da” ta and the Relational Model“ ([DDL2003a]), in dem ein alternativer Umgang mit der Problematik vorgeschlagen wird. Die hier vorgestellte erweiterte Relationenalgebra ist an den in dieser Quelle beschriebenen Konzepten orientiert. Zusätzlich werden bereits am Institut entstandenen Arbeiten (sowie deren Quellen) beachtet: In [Rei99a] und [Bei2001a] wird versucht, eine temporale Erweiterung für SQL92 im Sinne von TSQL2 zu schaffen. Eine graphische Anfragesprache für temporale Datenbanken wird in [Kuh97a] beschrieben. 1 Leicht am fehlenden Part 7“ ( SQL/Temporal“) in der SQL-Spezifikation zu erkennen – etwa durch ” ” eine Suche bei http://iso.org nach ’SQL’. 5 1. Einleitung Gliederung dieser Arbeit Der Literatur folgend werden Daten mit einem zeitlichen Bezug versehen, indem ihnen ein Gültigkeitszeitraum zugeordnet wird. Die Modellierung dieser Zeitstempel geschieht mit diskreten Intervallen, welche als Zeitspannen zu verstehen sind. In Kapitel 2 wird so die grundlegende Semantik einer temporalen Datenbank festgelegt. Danach sind zunächst die Grundlagen für den Umgang mit solchen Intervallen im relationalen Datenmodell zu schaffen. Davon ausgehend wird die klassische Relationenalgebra um Intervallfunktionalität erweitert (Kapitel 3). In Kapitel 4 wird dargelegt, wie Integritätsanforderungen einer temporalen Datenbank durch die erweiterte Relationenalgebra zu formulieren sind. Außerdem ist zu klären wie die Relationen einer temporalen Datenbank modelliert werden können. Um die Ausdrucksfähigkeit der Algebra zu testen und die beschriebenen Begriffe zu konkretisieren, werden in Kapitel 5 Anfragebeispiele diskutiert. In Kapitel 6 wird versucht die erweiterten Operatoren in Hinblick auf Speicherbedarf und Geschwindigkeit zu verbessern und so eine Implementierung zu ermöglichen. Eine Beispielimplementierung soll zeigen, ob und wie die erweiterten Relationenalgebra als Anfragesprache auf Basis eines bestehenden RDBMS realisiert werden kann (Kapitel 7). Schließlich wird in Kapitel 8 ein Ausblick auf weitere Möglichkeiten gegeben, die durch das hier beschriebene Konzept erschlossen werden. 6 Eins-zwei-drei! Im Sauseschritt läuft die Zeit, wir laufen mit. (Wilhelm Busch) 2. Semantik temporaler Daten Die Bedeutung von Daten in einem zeitlichen Kontext hängt vom verwendeten Zeitbegriff ab. Deshalb werden im folgenden zunächst die Begriffe Zeitpunkt“ und Zeitraum“ ” ” erläutert, damit eine Semantik für zeitveränderliche Daten angegeben werden kann. Abschließend wird Bezug auf in der Literatur häufig verwendete Zeitbegriffe genommen. 2.1. Zeitstempel, Chronons und Granularität In einer Datenbank wird jedem Tupel einer Relation stets eine gewisse Aussage zugeordnet. Diese Aussage soll für eine temporalen Datenbank in einen zeitlichen Zusammenhang gestellt werden, indem ihr ein gewisser Gültigkeitszeitraum zugesprochen – sozusagen ein Zeitstempel aufgeprägt“ – wird. Die allgemeine Struktur dieses Zeitstempels muss also ” eine (potentiell unendliche) Menge1 von Zeitpunkten sein. Einem Zeitpunkt (Chronon), als kleinster in der Datenbank darstellbaren Zeiteinheit, kann wiederum ein (kontinuierliches) Zeitintervall oder eine (endliche) Menge von (atomaren) Zeiteinheiten entsprechen. Die Länge eines Zeitpunkts2 wird als Granularität bezeichnet. Bemerkungen: • Ein Zeitstempel besteht nicht notwendigerweise aus einem Intervall von Zeitpunkten (z.B. bei periodisch wiederkehrenden Ereignissen). • Es können verschiedene (gleichberechtigte) Arten von Zeitpunkten mit unterschiedlichen Granularitäten existieren (etwa verschieden lange Vorlesungszeiten im Winterund Sommersemester). • Ein Zeitstempel kann Vergangenheit oder Zukunft (auch beides) enthalten. Dafür muss jedoch ein Zeitpunkt als gegenwärtig“ festgelegt sein. ” 2.2. Modellierung des Zeitstempels 2.2.1. Natürliche Zahlen als Zeitpunkte Da die Granularität frei bestimmbar (interpretierbar) ist, sollte es ausreichen ein diskretes3 Zeitmodell zu verfolgen. Damit genügt als Grundmenge von Zeitpunkten die Menge 1 2 diskret oder kontinuierlich – je nach Philosophie also die Länge des Zeitintervalls eines Chronons im kontinuierlichen Fall oder die Anzahl von atomaren Zeiteinheiten eines Chronons im diskreten Fall 7 2. Semantik temporaler Daten der natürlichen Zahlen. Beispiel: Ist (genau) eine Stunde als Granularität gewählt, dann entspricht der Zeitpunkt ’4’ der vierten Stunde – also der Zeit von 4:00 Uhr bis kurz vor“ 5 Uhr. ” 2.2.2. Intervalle als Zeitstempel Da in der Praxis häufig Tupel betrachtet werden, deren Attributwerte in einer gewissen Zeitspanne konstant bleiben, erscheint es sinnvoll ein (geschlossenes) Intervall als Datenstruktur für Zeitstempel zu nutzen: Ist die Aussage eines Tupels zu einem bestimmten Zeitpunkt gleich der Aussage eines anderen Tupels zum Zeitpunkt danach (oder davor), so können diese Tupel mit dem entsprechenden intervallwertigen Zeitstempel zusammengefasst werden. Prozesse, die Aussagen an nicht aufeinanderfolgenden Zeitpunkten identifizieren (etwa periodische Prozesse) werden allerdings mit dieser Wahl nicht so effizient modelliert. Beispiel: Mit [4, 9] wäre also der Zeitraum von 4:00 Uhr bis kurz vor“ 10 Uhr gemeint, ” wenn ein Zeitpunkt einer Stunde entspricht. [4, 4] kann dann mit dem Zeitpunkt ’4’ identifiziert werden. Bemerkung: Mit den zwei Zeitpunkten b und e mit b ≤ e entspricht das geschlossene Intervall [b, e] dem rechts offenen Intervall [b, e + 1) und umgekehrt. Aus programmiertechnischen Gründen wird hier die geschlossene Darstellung bevorzugt – auch wenn beide Darstellung austauschbar sind. Außerdem besteht mit rechts offenen Intervallen eher die Gefahr undefinierte Intervalle zu erzeugen – etwa durch [b, b). 2.2.3. Temporale Elemente Mit der im TSQL-Ansatz als Zeitstempel verwendeten Menge von Zeitintervallen ( tem” porales Element“) ist keine größere Ausdrucksfähigkeit zu erwarten. Ein solches temporales Element kann stets durch mehrere Tupel mit (verschiedenen) Intervall-Zeitstempeln ohne Verlust nachgebildet werden (vgl. die Beispiele in 5.1). Daher wird hier auf dieses komplizierende Konzept verzichtet. 2.3. Gültigkeitszeit, Transaktionszeit und benutzerdefinierte Zeit In der Literatur zu temporalen Datenbanken sind häufig die Begriffe Gültigkeitszeit (valid time), Transaktionszeit (transaction time) und benutzerdefinierte Zeit (userdefined time) zu lesen. Gültigkeitszeit“ bezieht sich auf Aussagen mit Gültigkeitszeitraum. ” 3 Eine Menge ist diskret, wenn sie umkehrbar-eindeutig auf die natürlichen Zahlen abbildbar ist. 8 2. Semantik temporaler Daten Mit Transaktionszeit“ ist die Protokollierung von Datenbanktransaktionen auf Tu” pelebene gemeint – Tupel X stand im Zeitraum Z in Relation R“. Zeitstempel für ” Transaktionszeit sind vom Datenbanknutzer unabhängig und werden allein vom DBMS verwaltet. Im Sinne eines Logs, also der Protokollierung von Transaktionen, sind Aussagen über die Vergangenheit hier nicht veränderbar4 . Transaktionszeit läßt sich natürlich über (eine zusätzliche) Gültigkeitszeit nachbilden. Ein DBMS, das Gültigkeitszeit und Transaktionszeit enthält wird bitemporal genannt. Als benutzerdefinierte Zeit“ werden Zeitangaben ohne Zeitstempelbezug bezeichnet. ” Der oben erläuterte Zeitstempel entspricht also eher der Gültigkeitszeit. Im hier vorgestellten Konzept müssen die genannten Zeitbegriffe jedoch nicht weiter unterschieden werden. 4 Ansätze für einen Umgang mit Transaktionszeit im Hinblick auf Datenrettung sind unter dem Stichwort Flashback Query“ in der Version 10g des Oracle-DBMS zu finden (siehe [OraAD] Kapitel ” 15) 9 3. Definition der erweiterten Relationenalgebra Um eine erweiterte Relationenalgebra definieren zu können, wird in 3.1 zunächst erläutert, wie die zugrundeliegende Relationenalgebra definiert ist. Nach 2.2.2 wird ein Zeitstempel als Intervall modelliert. In 3.2 wird daher ein entsprechender Intervallbegriff eingeführt. Zusätzlich werden Operatoren für den Umgang mit Intervallen definiert. Bezüglich temporaler Daten werden also die für den Umgang mit Zeiträumen benutzbaren Begriffe bereitgestellt. Die dann in 3.3 neu eingeführten Operatoren dienen dazu, eine atomare“, zeitpunkt” ” artige“ Sicht auf die Information in einer Relation zu erhalten (unpack), bzw. diese Information möglichst zusammengefasst“ und redundanzfrei“ betrachten zu können ” ” (pack). Damit läßt sich die zugrundeliegende Relationenalgebra um Funktionalität für Zeitstempel (bzw. Intervalle) erweitern (3.4). 3.1. Relationen Die hier aufgeführten Definitionen sollen weitgehend der gewohnten“ klassischen Re” lationenalgebra entsprechen. Der einzige wichtige Unterschied besteht in der Definition von Gruppierung und Aggregation: Das Ergebnis einer Aggregationsfunktion ist nicht auf einen einzigen Wert beschränkt, sondern kann eine Menge von Werten zurückliefern. Dies wird das grundlegende Konzept für die Formulierung der erweiterten Relationenalgebra in 3.3. 3.1.1. Definition Eine Relation ist ein 2-Tupel R = (S, I): • S heißt Schema von R und ist eine Funktion der Form S : {a1 , . . . , an } −→ D mit n ∈ N0 wobei der Definitionsbereich def(S) = {a1 , . . . , an } eine Menge von (paarweise verschiedenen) Zeichenketten (den Attributnamen) und D eine Menge von Mengen (den Wertebereichen oder Datentypen) ist. S ordnet also jedem Attributnamen einen Wertebereich zu. 10 3.1. Relationen • Sei T (S) := {t : def(S) −→ [ S(a) mit t(a) ∈ S(a) für alle a ∈ def(S)} a∈def(S) eine Menge von Abbildungen, die allen Attributnamen eines Schemas einen Wert aus dem zugehörigen Wertebereich zuordnet. Eine endliche Teilmenge I von T (S) heißt Inhalt von R. Bemerkungen: • Das Bild im(S) von S ist die Menge aller den Attributnamen zugeordneten Wertebereichen; im(S) ⊆ D. • Ein 2-Tupel (a, S(a)) ∈ def(S) × im(S) heißt Attribut (von R). • Ein t ∈ T (S) heißt Tupel und ein t ∈ I ⊂ T (S) entsprechend Tupel von R. • T (S) ist die Menge aller möglichen Tupel zu einem Schema S. • Häufig wird R statt I geschrieben1 , z.B. t ∈ R statt t ∈ I. Oder: Ist P eine Relation mit gleichem Schema wie R, dann ist mit P ⊂ R gemeint, dass der Inhalt von P Teilmenge des Inhalts von R ist. • Der Inhalt von R enthält das gleiche Tupel nicht mehr als einmal (keine Duplikate). Es ist keine bestimmte Reihenfolge der Attribute oder der Tupel festgelegt. • Ist eine Reihenfolge a1 , . . . , an der Attribute vorgegeben, so wird ein t ∈ T (S) auch kurz als (t(a1 ), . . . , t(an )) geschrieben. • Eine Relation wird häufig in Form einer Tabelle dargestellt, z.B: Name Alter Müller 33 Schmidt 35 Schulze 32 Die Namen der Attribute ergeben sich aus dem Tabellenkopf. Die Wertebereiche werden meist nicht explizit angegeben. Dieser Tabelle entspricht ein Schema mit den Attributen (Name, string) und (Alter, integer). Der Inhalt der Relation ist die Menge der Tupel (Müller, 33), (Schmidt, 35) und (Schulze, 32). 3.1.2. Operatoren auf Relationen – Relationenalgebra Die folgenden Operatoren bilden eine Algebra auf der Menge der Relationen. Seien X = (S X , I X ) und Y = (S Y , I Y ) zwei Relationen. 1 Ob eine Relation oder deren Inhalt gemeint ist, sollte aus dem Kontext erkennbar sein. 11 3.1. Relationen Projektion Sei A eine Teilmenge der Attributnamen von X: A ⊆ def(S X ). Dann wird X durch die Projektion folgendermaßen auf A eingeschränkt: πA (X) := (S 0 , I 0 ) mit S 0 = S X |A = S 0 : A −→ im(S X ) mit S 0 (a) = S X (a) für alle a ∈ A I 0 = {t|A : t ∈ X} Erweiterung Sei M eine Menge, β eine Abbildung der Form β : I X → M und b 6∈ def(S X ) eine Zeichenkette. Dann ist die Erweiterung von X um b = β definiert als: εb=β (X) := (S 0 , I 0 ) mit S 0 : def(S X ) ∪ {b} −→ im(S X ) ∪ {M } mit S 0 |def(S X ) = S X und S 0 (b) = M I 0 = {t0 ∈ T (S 0 ) : ∃t ∈ X mit t0 |def(S X ) = t und t0 (b) = β(t)} Umbenennung Sei a ∈ def(S X ) ein Attributname von X und b 6∈ def(S X ) eine Zeichenkette. Außerdem sei β : I X → S(a) mit β(t) = t(a). Dann kann das Attribut a von X wie folgt in b umbenannt werden: ρb=a := πdef(S X )−{a} (εb=β (X)) Für mehrere Umbenennungen der Form ρb1 =a1 (ρb2 =a2 (. . . (X))) sei auch die Schreibweise ρb1 =a1 ,b2 =a2 ,... (X) erlaubt. Selektion Es sei ϕ eine Selektionsfunktion: ϕ : I X → {wahr, falsch}. Mit σϕ (X) werden alle Tupel aus X ausgewählt, die der Selektionsfunktion genügen: σϕ (X) : = (S X , I 0 ) mit I 0 = {t ∈ X : ϕ(t) = wahr} Verbund (Join) Durch den Verbund von X und Y werden alle Tupel verknüpft, die gleiche Werte für gleichnamige Attribute haben: Xo n Y := (S 0 , I 0 ) mit S 0 : def(S X ) ∪ def(S Y ) −→ im(S X ) ∪ im(S X ) mit S 0 |def(S X ) = S X und S 0 |def(S Y ) = S Y I 0 = {t ∈ T (S 0 ) : ∃x ∈ X ∧ ∃y ∈ Y mit t|def(S X ) = x ∧ t|def(S Y ) = y} 12 3.1. Relationen Bemerkung: Haben X und Y keine gleichnamigen Attribute, so entspricht der Verbund dem Kartesischen Produkt von X und Y . Umgekehrt entspricht der Verbund dem Durchschnitt von X und Y falls X und Y nur gleichnamige Attribute haben. Vereinigung Falls X und Y das gleiche Schema (S X = S Y ) haben, ist die Vereinigung wie folgt definiert: X ∪ Y : = (S 0 , I 0 ) mit S0 = SX = SY I 0 = {t : t ∈ X ∨ t ∈ Y } Differenz Haben X und Y das gleiche Schema (S X = S Y ), so ist die Differenz von X und Y definiert als: X − Y : = (S 0 , I 0 ) mit S0 = SX = SY I 0 = {t : t ∈ X ∧ t 6∈ Y } Durchschnitt Mit (S X = S Y ) gilt: X ∩ Y := X − (X − Y ) = X o nY Gruppierung und Aggregation Sei A eine Teilmenge der Attributnamen von X: A ⊆ def(S X ). Für alle 1 ≤ i ≤ n ∈ N seien außerdem bi 6∈ def(S X ) paarweise verschiedene Zeichenketten, Mi Mengen und αi (Aggregations-)Funktionen der Form αi : P(I X ) −→ P(Mi ) Dann gilt: ΓA # b1 =α1 ,...,bn =αn (X) := (S 0 , I 0 ) mit S 0 : {b1 , . . . , bn } −→ {Mi , . . . , Mn } mit S 0 (bi ) = Mi ∀i ∈ {1, . . . , n} I 0 = {t0 ∈ T (S 0 ) : ∃t ∈ πA (X) ∧ ∀i ∈ {1, . . . , n} : t0 (bi ) ∈ αi ({x ∈ X : x|A = t}) } 13 3.1. Relationen Bemerkungen: • Ist A = ∅ die leere Menge, dann wird nicht gruppiert und das Ergebnis einer Aggregationsfunktion wird für ganz X bestimmt. • Diese Definition der Gruppierung unterscheidet sich in (nur) einem Punkt von der Herkömmlichen: Eine Aggregationsfunktion liefert anstelle von Werten (aus einer Menge M ) eine Menge von Werten (aus P(M )) zurück. Der Nutzen dieser Erweiterung ergibt sich im Umgang mit intervallwertigen Attributen (siehe 3.3.1). Vereinfachungen: Mit einer Menge G ⊆ I X und einem Attributnamen a ∈ def(S X ) seien folgende abkürzende Schreibweisen möglich: • count für α(G) := {|G|} • min(a) für α(G) := {mint∈G t(a)} P • sum(a) für α(G) := { t∈G t(a)} • weitere Aggregationsfunktionen (max, avg, . . . ) analog zu min und sum Beispiel: Sei folgende Relation X gegeben: Produkt Rennrad X := Rennrad Mountainbike Preis 300 250 270 Zeit [1, 7] [8, 14] [1, 20] Dann liefert eine Gruppierung über ’Produkt’ der Form ΓProdukt # Produkt, Summe = sum(Preis) (X) eine Relation mit dem Schema S 0 : {Produkt, Summe} −→ {S X (Produkt), S X (Preis)} mit S 0 (Produkt) = S X (Produkt) und S 0 (Summe) = S X (Preis) Schreibt man die Vereinfachungen aus, ergibt sich für den Inhalt: I 0 = {t0 ∈ T (S 0 ) : ∃t ∈ πProdukt (X) ∧ t0 (Produkt) ∈ {x(Produkt) : x ∈ X ∧ x|Produkt = t} ∧ X t0 (Summe) ∈ { x(Preis) } } {x∈X:x|Produkt =t} 14 3.2. Intervalle Für jede der beiden durch πProdukt gegebenen Partitionen von X lassen sich die (hier einelementigen) Ergebnismengen der Aggregationsfunktionen berechnen: I 0 = {t0 ∈ T (S 0 ) : ∃t ∈ {(Rennrad), (Mountainbike)} = πProdukt (X) mit t0 (Produkt) ∈ {Rennrad} ∧ t0 (Summe) ∈ {300 + 250} für Rennräder und t0 (Produkt) ∈ {Mountainbike} ∧ t0 (Summe) ∈ {270} für Mountainbikes } Dies entspricht der Ergebnisrelation Produkt Rennrad Mountainbike Summe 550 270 3.2. Intervalle In diesem Abschnitt werden die Intervalle zur Modellierung des in Kapitel 2 beschriebenen Zeitstempels definiert. Zusätzlich werden Operatoren zum Umgang mit diesen Intervallen eingeführt. Es sei sets N = {0, 1, 2, 3, . . .} die Menge der natürlichen Zahlen. 3.2.1. Definition Für b, e ∈ N mit b ≤ e sei das Intervall von b bis e definiert als [b, e] := {n ∈ N : b ≤ n ≤ e} Sei I die Menge aller so definierten Intervalle. 3.2.2. N-wertige Operatoren Für ein Intervall I = [b, e] ∈ I gelte: • begin(I) := b • end(I) := e • count(I) := end(I) − begin(I) + 1 3.2.3. Vergleichsoperatoren Vergleichsoperatoren liefern Information über die Beziehung zweier Intervalle: I × I −→ {wahr, falsch} Seien also I1 := [b1 , e1 ] ∈ I und I2 := [b2 , e2 ] ∈ I zwei Intervalle. Allens Operatoren“ ” Die folgenden Operatoren sind als Allens Operatoren“ [All83a]2 bekannt: ” 2 is_included_in wird dort during genannt, begins als starts bezeichnet und ends entsprechend als finishes 15 3.2. Intervalle • I1 equals(=) I2 :⇔ b1 = b2 ∧ e1 = e2 b1 • I1 is_included_in(⊆) I2 :⇔ b1 ≥ b2 ∧ e1 ≤ e2 e1 b2 • I1 before I2 :⇔ e1 < b2 b1 • I1 meets I2 :⇔ b1 = e2 + 1 ∨ b2 = e1 + 1 • I1 overlaps I2 :⇔ b1 ≤ e2 ∧ b2 ≤ e1 e2 e1 b2 e2 b1 e 1 b2 e2 b1 e1 b2 b1 • I1 begins I2 :⇔ b1 = b2 ∧ e1 ≤ e2 e2 e1 b2 e2 b1 • I1 ends I2 :⇔ e1 = e2 ∧ b1 ≥ b2 e1 b2 e2 Abgeleitete Vergleichsoperatoren Weitere Vergleichsoperatoren sind: • I1 after I2 :⇔ I2 before I1 • I1 includes(⊇) I2 :⇔ I2 is_included_in I1 • I1 ⊂ I2 :⇔ I1 ⊆ I2 ∧ I1 6= I2 • I1 ⊃ I2 :⇔ I1 ⊇ I2 ∧ I1 6= I2 • I1 merges I2 :⇔ I1 meets I2 ∨ I1 overlaps I2 Zusätzlich läßt sich der Spezialfall n ∈ I1 für ein n ∈ N definieren: n ∈ I1 :⇔ [n, n] is_included_in I1 ⇔ b1 ≤ n ≤ e1 3.2.4. Intervallwertige Operatoren Um zwei Intervalle miteinander zu einem neuen Intervall zu kombinieren, werden Operatoren der Form I × I → I benötigt. Mit I1 := [b1 , e1 ] ∈ I und I2 := [b2 , e2 ] ∈ I sei: • I1 union I2 := [min (b1 , b2 ), max (e1 , e2 )] falls I1 merges I2 b1 e1 b2 e2 16 b1 e2 3.2. Intervalle • ( [b1 , min (b2 − 1, e1 )] falls b1 < b2 ∧ e1 ≤ e2 I1 minus I2 := [max (e2 + 1, b1 ), e1 ] falls b1 ≥ b2 ∧ e1 > e2 b1 e1 b2 e2 b1 b2 • I1 intersect I2 := I1 minus (I1 minus I2 ) falls I1 overlaps I2 = [max (b1 , b2 ), min (e1 , e2 )] b1 e1 b2 e2 b2 e1 Diese Operatoren bilden mit den obigen Bedingungen eine Algebra auf I. 3.2.5. Operatoren auf Mengen von Intervallen Die beiden folgenden Operatoren bilden eine Teilmenge von I auf eine Teilmenge von I ab: P(I) → P(I). Sei nun X ∈ P(I). expand Der Operator expand zerlegt alle übergebenen Intervalle in Einheitsintervalle (d.h. Intervalle I mit end(I) = begin(I)): expand(X ) := {[b, b] : ∃I ∈ X ∧ b ∈ I} Beispiel: expand({[2, 2], [4, 6], [5, 7], [8, 9]}) = {[2, 2], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9]} collapse Im Gegensatz dazu verschmilzt“ collapse alle Intervalle für die dies möglich ist. ” Beispiel: collapse({[2, 2], [4, 6], [5, 7], [8, 9]}) = {[2, 2], [4, 9]} = collapse(expand({[2, 2], [4, 6], [5, 7], [8, 9]})) 17 3.3. pack und unpack Iterative Definition: Zu jedem Intervall X ∈ X sei die transitiven Hülle T ∗ (X) bezüglich merges gegeben: T 0 (X) := {X} T n (X) := {I ∈ X : ∃J ∈ T n−1 (X) ∧ I merges J} ∀n ∈ N≥1 T ∗ (X) := ein (beliebiges) T n (X) mit T n (X) = T n+1 (X) Vereinigt (union) man nun alle Intervalle in jeder Hülle zu einem einzigen Intervall, ergibt sich collapse als Vereinigung dieser Intervalle: [ collapse(X ) := { [ min (begin(I)), max (end(I)) ] } ∗ ∗ X∈X I∈T (X) I∈T (X) Deskriptive Definition: collapse(X ) := {[b1 , e2 ] : ∃ [b1 , e1 ] ∈ X ∧ ∃ [b2 , e2 ] ∈ X ∧ b1 ≤ b2 ∧ e1 ≤ e2 ∧ (¬∃I ∈ X mit I meets [b1 , e2 ]) ∧ (∀n ∈ [b1 , e2 ] gilt: (∃J ∈ X mit n ∈ J))} (1) (2) (3) (4) Erläuterung: 1: Zu jedem Intervall X in der Ergebnismenge von collapse(X ) existiert ein Intervall [b1 , e1 ] ∈ X mit begin(X) = b1 und ein Intervall [b2 , e2 ] ∈ X mit end(X) = e2 . (Es kann auch [b1 , e1 ] = [b2 , e2 ] sein.) 2: X wird größtmöglich gewählt; es soll gelten [b1 , e1 ] ⊆ X und [b2 , e2 ] ⊆ X. 3: Es soll kein an X angrenzendes Intervall in X existieren. 4: Für alle Elemente in X muss es ein Intervall in X geben, welches dieses Element enthält. Eine Entsprechung im TSQL-Konzept findet collapse im Verschmelzungsoperator coalesce. Dieser Operator hat dort jedoch keine tiefgreifende Bedeutung und dient dazu das Ergebnis lesbarer zu machen (vgl. etwa [Rei99a] Kapitel 3, Definition 10). 3.3. pack und unpack Mit den nun definierten Operatoren auf Intervallen läßt sich die in 3.1 definierte Relationenalgebra um Funktionalität für Zeitstempel (bzw. Intervalle) erweitern. Hierzu werden expand und collapse als Aggregationsfunktionen aufgefasst, damit sie auf intervallwertige Attribute einer Relation wirken können. Diese neu enstehenden Operatoren dienen dazu eine atomare“, zeitpunktartige“ Sicht auf die Information in einer Relation zu ” ” 18 3.3. pack und unpack erhalten (unpack), bzw. diese Information möglichst zusammengefasst“ und redun” ” danzfrei“ betrachten zu können (pack). Im folgenden seien X = (S, I) und Y zwei Relationen. Außerdem sei a der Name eines intervallwertigen Attributs (a, I) von X; A := def(S) − {a} sei die Menge der Attributnamen von X ohne a und G ⊆ I eine Menge (Gruppe) von Tupeln. 3.3.1. collapse und expand als Aggregationsfunktionen Man kann collapse und expand als Aggregationsfunktionen (nach 3.1.2) auffassen: collapse(a) : P(I) −→ I mit collapse(a)(G) := collapse({t(a) : t ∈ G}) und analog expand(a) : P(I) −→ I mit expand(a)(G) := expand({t(a) : t ∈ G}) 3.3.2. pack und unpack für ein (einziges) Attribut Mit diesen neuen Aggregationsfunktionen lassen sich nun pack und unpack für ein Attribut a definieren: packa (X) := ΓA#A,a=collapse(a) und analog unpacka (X) := ΓA#A,a=expand(a) Beispiel: Sei folgende Relation X gegeben: Name Müller X := Müller Schmidt Schmidt VT [2, 2] [4, 6] [5, 8] [8, 9] Dann ergibt sich für den Inhalt I von packVT (X) = ΓName # Name, VT = collapse(VT) (vgl. das Beispiel zu Gruppierung und Aggregation in 3.1.2): I = {t0 ∈ T (S) : ∃t ∈ πName (X) ∧ t0 (Name) ∈ {x(Name) : x ∈ X ∧ x|Name = t} ∧ t0 (VT) ∈ collapse(VT)({x ∈ X : x|Name = t}) } 19 3.3. pack und unpack also I = {t0 ∈ T (S) : ∃t ∈ {Müller, Schmidt} = πName (X) mit t0 (Name) ∈ {Müller} ∧ t0 (VT) ∈ {[2, 2], [4, 6]} für Müller und t0 (Name) ∈ {Schmidt} ∧ t0 (VT) ∈ {[5, 9]} für Schmidt } und damit die Ergebnisrelation Name VT Müller [2, 2] packVT (X) = Müller [4, 6] Schmidt [5, 9] Für unpackVT (X) ergibt sich analog das Ergebnis Name Müller Müller Müller Müller unpackVT (X) = Schmidt Schmidt Schmidt Schmidt Schmidt VT [2, 2] [4, 4] [5, 5] [6, 6] [5, 5] [6, 6] [7, 7] [8, 8] [9, 9] 3.3.3. pack und unpack für mehrere Attribute Sei L := l1 , . . . , ln mit n ∈ N≥1 eine (nichtleere) Liste 3 von Attributnamen: unpackL (X) := unpackln (unpackln−1 (. . . unpackl1 (X) . . . )) und damit packL (X) := packln (packln−1 (. . . packl1 (unpackL (X)) . . . )) S# P# Beispiel: Für X = [1, 2] [3, 4] ist [5, 6] [7, 8] 3 keine Menge, denn für pack spielt die Reihenfolge eine Rolle 20 3.3. pack und unpack S# [1, 1] [1, 1] [2, 2] unpackS#,P# (X) = [2, 2] [5, 5] [5, 5] [6, 6] [6, 6] S# [3, 5] Mit X = [2, 4] [2, 4] [2, 4] P# [3, 3] [4, 4] [3, 3] [4, 4] = unpackP#,S# (X). [7, 7] [8, 8] [7, 7] [8, 8] P# [1, 5] [1, 4] [5, 6] [6, 9] S#,P# gilt pack S# P# S# P# (X) = [2, 5] [1, 5] 6= [2, 4] [1, 9] = packP#,S# (X). [2, 4] [6, 9] [5, 5] [1, 5] graphische Darstellung: P# P# P# 9 9 9 8 8 8 7 7 7 6 6 6 5 5 5 4 4 4 3 3 3 2 2 2 1 1 1 2 3 4 5 S# 1 1 2 3 4 5 S# packS#,P# (X) X 1 2 3 4 5 S# packP#,S# (X) Bemerkungen: • Für unpack spielt die Reihenfolge der Attribute in L keine Rolle, für pack dagegen schon. • Im allgemeinen gilt nicht packL (X) = packln (packln−1 (. . . packl1 (X) . . . )) Mit X wie oben im Beispiel für pack ergibt sich 21 3.4. Erweiterung der Relationenalgebra S# P# packP# (packS# (X)) = packS# (packP# (X)) = [3, 5] [1, 4] [2, 4] [1, 9] graphisch: P# 9 8 7 6 5 4 3 2 1 1 2 3 4 5 S# Das anfängliche unpackL (X) ist für packL (X) also notwendig. 3.3.4. pack und unpack ohne Attribute Um die Operatoren der Relationenalgebra zu ersetzen, wird pack sowie unpack ein Ergebnis zugewiesen, auch wenn kein Attribut angegeben ist. In diesem Fall gelte: pack(X) := X und auch unpack(X) := X 3.4. Erweiterung der Relationenalgebra Mit den obigen Definitionen ist es nun möglich die Operatoren der Relationenalgebra neu zu definieren. Sei L eine Liste von Attributnamen. L kann auch leer sein (Schreibweise: einfach weglassen); in diesem Fall erfüllen (wegen 3.3.4) die neu definierten Operatoren die gleiche Funktion wie die alten. 3.4.1. Dyadische Operatoren Sei etwa die Differenz zweier Relationen X und Y neu definiert als: X −L Y := packL (unpackL (X) − unpackL (Y )) Natürlich müssen auch die gleichen Voraussetzungen gelten wie für die original“ Diffe” renz, d.h. auch hier müssen X und Y das gleiche Schema haben. Analog werden ∪L , ∩L und o nL definiert. 22 3.4. Erweiterung der Relationenalgebra 3.4.2. Monadische Operatoren Entsprechendes gilt für Operatoren, die auf eine einzigen Relation wirken, z.B.: πAL (X) := packL (πA (unpackL (X))) analog für σϕL , εLb=β und ΓLA#b1 =α1 ,...,bq =αq (Für Umbennennungen ρb=a erscheint eine Erweiterung nicht sinnvoll, weil nur das Schema einer Relation verändert wird und nicht ihr Inhalt.) Bemerkung: Möglicherweise wird nur das anfängliche unpack oder das abschließende pack ausgeführt, nicht aber beides: Durch Projektion, Umbenennung oder Gruppierung kann das in L aufgeführte Attribut entfallen (kein pack) oder erst entstehen (kein unpack). 23 4. Modellierung temporaler Datenbanken Für die Formulierung von Integritätsbedingungen einer temporalen Datenbank sind Begriffe notwendig, die sich auf Zeitstempel beziehen. In Abschnitt 4.1 wird erläutert, wie solche Bedingungen mit der erweiterten Relationenalgebra beschrieben werden können. Abhängig davon, welcher Aussage einer temporalen Datenbank ein Gültigkeitszeitraum zugeordnet sein soll, sind die Relationen entsprechend zu modellieren. Wie dabei vorzugehen ist wird in 4.2 diskutiert. Die besondere Bedeutung der Gegenwart führt auf die Frage wie eine Modellierung von Zukunft und Vergangenheit erreicht werden kann (Abschnitt 4.3). 4.1. Integritätsbedingungen Die Modellierung zeitabhängiger Daten in einem DBMS stellt zusätzliche Anforderungen an den Zustand der Datenbank. Im folgenden werden diese Anforderungen detailliert besprochen. 4.1.1. Eindeutige Identifikation Wird eine bestehende Relation ohne Zeitstempel um einen solchen erweitert, sind die bisherigen Schlüsselkandidaten1 der Relation möglicherweise nicht mehr zur eindeutigen Identifizierung ausreichend: Mehrere verschiedene Tupel in der temporal erweiterten Relation würden nach dem nicht-temporalen Primärschlüssel2 als ein einziges Tupel identifiziert werden, wenn sich die Werte von nicht zum Primärschlüssel gehörenden Attributen im Laufe der Zeit verändern. In [Kuh97a] und [Bei2001a] wird dies durch die Einführung eines zeitlich invarianten Schlüsselsurrogats gelöst. So wird etwa eine Abteilung nicht wie in der nicht-temporalen Version der Datenbank durch ihren Namen, sondern durch eine Nummer identifiziert, die sich niemals ändert. Der Name einer Abteilung kann sich somit im Laufe der Zeit ändern. Eine weitere Möglichkeit ist, die Schlüsselkandidaten jeweils um den Zeitstempel zu erweitern. Das hat (im Sinne des obigen Beispiels) den Vorteil, dass kein weiteres künstliches Nummer“-Attribut eingefügt werden muss und den Nachteil, dass eine Abteilung ” keine Namensänderung durchführen darf. Allerdings sollte auch die Semantik beachtet 1 2 die minimalen Mengen von Attributen, die ein Tupel stets eindeutig identifizieren der (aus inhaltlichen Gründen) tatsächlich zur Identifikation gewählte Schlüsselkandidat 24 4.1. Integritätsbedingungen werden: Ist die ’Finanz’-Abteilung wirklich noch dieselbe Abteilung, sobald sie etwa in ” ’Forschung und Entwicklung’ umbenannt wird?“3 Formalisierung Für eine Relation R ohne Zeitstempel sei A die Menge der Attributnamen von R und K ⊆ A der Primärschlüssel. Dann kann bei einer Erweiterung von R zu R̃ um einen Zeitstempel VT die eindeutige Identifikation erhalten werden, indem VT zum Primärschlüssel hinzugenommen wird: K̃ := K ∪ {V T }. Die vorherige Integritätsbedingung ∀r1 ∈ R : ∀r2 ∈ R : r1 |K = r2 |K ⇒ r1 = r2 wird für die neue Relation R̃ und ihren Primärschlüssel K̃ übernommen: ∀r1 ∈ R̃ : ∀r2 ∈ R̃ : r1 |K̃ = r2 |K̃ ⇒ r1 = r2 Wenn also bisher für die Einhaltung der eindeutigen Identifikation gesorgt werden konnte, sollte dies nach Erweiterung um einen Zeitstempel weiterhin möglich sein. Bemerkung: Manchmal werden nicht alle Attribute von K̃ benötigt: Eine Teilmenge von K̃ (etwa nur der Zeitstempel) könnte zur Identifikation von Tupeln aus R̃ genügen. Welcher Schlüsselkandidat der richtige“ ist, hängt stets von der gewünschten Semantik ” einer temporalen Datenbank ab. 4.1.2. Widerspruchsfreiheit Für gewöhnlich wird davon ausgegangen, dass sich widersprechende Aussagen nicht zur gleichen Zeit gültig sind. Die Zeitstempel inhaltlich verschiedener4 Tupel einer Relation dürfen sich also (im Sinne von overlaps) nicht überlappen. Da verschiedene Intervalle auch als verschiedene Werte für Zeitstempel gelten – selbst wenn sie sich überlappen – wird diese Integritätsbedingung nicht durch die oben beschriebene Eindeutigkeitsbedingung erfüllt. Es reicht jedoch aus, die Einhaltung der Eindeutigkeitsbedingung aus 4.1.1 für eine Relation R auch für unpackZeitstempel (R) zu fordern: ∀r1 ∈ unpackZeitstempel (R) : ∀r2 ∈ unpackZeitstempel (R) : r1 |K = r2 |K ⇒ r1 = r2 Semantisch sind R und unpackZeitstempel (R) gleich (d.h. sie stellen die gleichen Aussagen dar), die Einheitsintervalle in unpackZeitstempel (R) können sich aber nicht überlappen. 3 4 Nomen est omen. d.h. es besteht ein Unterschied in mindestens einem Attribut, das nicht Zeitstempelattribut ist 25 4.1. Integritätsbedingungen Ausnahmen Es sind Relationen mit intervallwertigem Attribut denkbar deren Semantik verlangt, dass Widerspruchsfreiheit nicht eingehalten wird, etwa: Prozent während 91 [10, 15] 83 [16, 20] 72 [17, 17] 87 [10, 20] 56 [1, 24] ... Diese Relation könnte beispielsweise die prozentuale Auslastung eines Servers beschreiben: Während des Zeitraums von 10 bis 15 Uhr betrug die durchschnittliche Auslastung 91%, von 16 bis 20 Uhr 83% um 17 Uhr 72% usf. . . 4.1.3. Zeitliche Fremdschlüssel Eine Fremdschlüsselbeziehung von R bzgl. S ist gegeben, falls R den Primärschlüssel von S (= Fremdschlüssel von R) enthält und zu jedem Tupel von R ein Tupel aus S mit den gleichen Werten in all diesen Schlüsselattributen existiert ( R referenziert S“). ” Analog zu 4.1.2 gilt: Wenn der Fremdschlüssel den Zeitstempel enthält, wird diese Integritätsbedingung nicht durch die Eindeutigkeitsbedingung erfüllt. Es reicht jedoch aus die Fremdschlüsselbeziehung für unpackZeitstempel (R) bzgl. unpackZeistempel (S) zu fordern (siehe Bemerkung zu 4.2.2 für ein Beispiel). 4.1.4. Redundanzfreiheit Redundanz tritt in temporalen Datenbanken auf, wenn sich die Zeitstempel inhaltlich gleicher5 Tupel überlappen: Die zu den Zeitpunkten in der Schnittmenge gehörenden Aussagen werden durch mehrere Tupel ausgedrückt. Ausserdem sind verschiedene Aussagen redundant, wenn sie sich durch eine einzige ausdrücken lassen. Dies ist der Fall, wenn inhaltlich gleiche Tupel aneinandergrenzende (im Sinne von meets) Zeitstempel haben. Durch die Vereinbarung die temporale Relation R stets gepackt zu halten“ so ” dass stets R = packZeitstempel (R) gilt, wird diese Redundanz vermieden. Ist eine Relation mit mehr als einem intervallwertigen Attribut (etwa in einer bitemporalen Datenbank) redundanzfrei im obigen Sinn, dann enthält sie nicht notwendigerweise die minimale Anzahl von Tupeln. Umgekehrt folgt aus der minimalen Tupelanzahl einer Relation im allgemeinen nicht die Redundanzfreiheit. 5 d.h. es besteht kein Unterschied zwischen allen Attributen, die nicht Zeitstempelattribut sind 26 4.1. Integritätsbedingungen Beispiel R enthält mit den beiden Tupeln ([3, 8], [1, 4]) und ([5, 9], [3, 8]) die redundante Aussage ([5, 8], [3, 4]). Die gepackten“ Relationen enthalten keine Redundanz mehr: ” P# 9 8 7 S# P# [3, 8] [1, 4] R= [5, 9] [3, 8] [1, 7] [7, 9] 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 S# P# 9 S# [3, 8] [3, 9] packS#, P# (R) = [5, 9] [1, 9] [1, 7] 8 P# [1, 2] [3, 4] [5, 6] [7, 8] [9, 9] 7 6 5 4 3 2 1 S# P# 9 S# [3, 4] [1, 4] packP#, S# (R) = [5, 7] [8, 8] [9, 9] 8 P# [1, 4] [7, 9] [1, 9] [1, 8] [3, 8] 7 6 5 4 3 2 1 S# Ausnahmen Es sind Relationen mit Zeitstempel denkbar deren Semantik verlangt, dass Redundanzfreiheit nicht eingehalten wird, beispielsweise: 27 4.1. Integritätsbedingungen Präsident Ford Carter Reagan Reagan Clinton Clinton Jahre [1974, 1976] [1977, 1980] [1981, 1984] [1985, 1988] [1993, 1996] [1997, 2000] ... Für diese Relation, als Liste von amerikanischen Präsidenten und den dazugehörigen Amtszeiten, mag es sinnvoll sein sie nicht gepackt zu halten“, um anzudeuten wieviele ” Legislaturperioden jeder Präsident im Amt war. 4.1.5. Stetigkeit Weitere weniger elementare Integritätsbedingungen sind selbstverständlich denkbar und mit der erweiterten Relationenalgebra formulierbar. Eine nicht explizit in [DDL2003a] behandelte Integritätsbedingung ist etwa die Forderung nach der Stetigkeit temporaler Daten, d.h. der modellierte zeitlichen Ablauf darf keine Lücken aufweisen. Formalisierung Sei K der Primärschlüssel von R. Dann sei K̃ := K − {Zeitstempel} die Menge der Primärschlüsselattribute ohne Zeitstempel. Zeitstempel R ist stetig falls K̃ ein Schlüsselkandidat für πK (R) ist. Ist der Zeitstempel das Zeitstempel einzige Primärschlüsselattribut, muss πK (R) aus genau einem Tupel bestehen. Beispiel Sei {Name, VT)} der Primärschlüssel von R. Name Gehalt VT Name VT Albers 6000 [1, 60] Müller 3000 [10, 20] Albers [1, 60] VT (R) = Mit R := Schmidt 3000 [21, 30] ist πName, Müller [10, 20] . VT Schmidt 4000 [31, 50] Schmidt [21, 50] Müller 4000 [51, 60] Müller [51, 70] Müller 5000 [61, 70] VT Da ’Müller’ in πName, VT (R) zweimal auftaucht, ist der Name hier kein Schlüsselkandidat und damit R nicht stetig. Allerdings wäre R stetig, wenn ’Schmidt’ in ’Müller’ (oder umgekehrt) umbenannt würde. 28 4.2. Tupel- oder Attributzeitstempel 4.2. Tupel- oder Attributzeitstempel Die Betrachtung von Relationen mit Tupeln deren Attributwerte in einer gewissen Zeitspanne konstant bleiben führt zu der Frage, wie solche Relationen zu modellieren sind. Denn wird der Wert eines Attributs häufig verändert, die Werte der anderen Attribute aber kaum, so entstehen viele Tupel, die sich inhaltlich nur in einem einzigen Attribut unterscheiden. Eine Lösung wäre, für einzelne Gruppen von Attributen deren Werte gemeinsam verändert werden, eigene Zeitstempel einzuführen – Zeitstempel auf Attributen (Attribute Timestamping) im Gegensatz zu Zeitstempeln auf Tupeln (Tupel Timestamping). Die Einführung weiterer Zeitstempel in dieselbe Relation würde aber selbstverständlich das Problem nur verschlimmern. Es ist also notwendig die Ausgangsrelation in mehrere Relationen aufzuteilen: In [DDL2003a] wird vorgeschlagen für jedes Attribut eine eigene Relation zu modellieren. Ändern sich Attribute stets synchron, wäre es jedoch unnötig wenn diese Attribute jeweils eigene Relationen erhielten. Jede Gruppe von synchron veränderlichen Attributen kann also in je einer einzigen Relation modelliert werden. Damit diese Relationen wieder ohne Informationsverlust zur Ausgangsrelation zusammengefügt werden können, müssen sie jeweils den Primärschlüssel (also nach 4.1.1 auch einen Zeitstempel) enthalten. Die Rekonstruktion kann dann über den erweiterten Verbund (o nZeitstempel ) geschehen. Für die beschriebene Zerlegung gelten die Vor- und Nachteile einer Normalisierung: Anfragen bezüglich des Zeitstempels eines bestimmten Attributs können anhand der weniger umfangreichen Teilrelation beantwortet werden. Aufgrund der zusätzlich benötigten Verbunde verringert sich jedoch die Ausführungsgeschwindigkeit von Anfragen, die sich nun auf mehrere Relationen beziehen müssen. Es mag allgemein für die zugrundeliegende Semantik vorteilhaft sein, die durch die ursprüngliche Relation modellierten Aussagen in mehrere kürzere, prägnantere aufzuteilen. Die Modellierung bestimmter Aussagen kann durch die Zerlegung erst möglich werden (vgl. Bemerkungen zum Beispiel). 4.2.1. Formalisierung Sei R eine Relation mit Attributmenge A und sei {A1 , . . . , An } ⊂ P(A) eine Menge von Teilmengen der Attribute. Weiterhin sei L eine Liste intervallwertiger Attribute von R – für eine temporale Datenbank also das Zeitstempelattribut: L = Zeitstempel. • R erfüllt die Join-Abhängigkeit ∗(A1 , . . . , An ) sofern stets gilt: R = πAL1 (R) o nL · · · o nL πALn (R) (Ist L die leere Liste, entspricht dies der klassischen Definition der Join-Abhängigkeit.) • Eine Join-Abhängigkeit heißt trivial, falls ein Ai die gesamte Attributemenge von R ist, also Ai = A für ein 1 ≤ i ≤ n gilt. 29 4.2. Tupel- oder Attributzeitstempel • Eine nicht-triviale Join-Abhängigkeit ∗(A1 , . . . , An ) sei hier als kollektiv bezeichnet, falls stets gilt: R = πAL1 (R) o n ··· o n πALn (R) Enthält jedes Ai (1 ≤ i ≤ n) alle Attribute aus L, so kann R im allgemeinen nur genau dann über den klassischen Join-Operator wieder verlustlos aus den Projektionen zusammengesetzt werden, wenn jedes πALi (R) die Werte aller L-Attribute unverändert läßt, also stets πALi (R) = πAi (R) gilt. Im temporalen Kontext (L = Zeitstempel) bedeutet dies eine synchrone Änderung der Attribute. Dieser Begriff wird bedeutungslos, falls L die leere Liste ist. • Die Zerlegung von R in Projektionen πAL1 (R), . . . , πALn (R) wird vertikale Dekomposition (von R) genannt. (L kann auch die leere Liste sein.) • Eine Relation R ist genau dann in der sechsten Normalform (6NF), wenn sie keine nicht-trivialen Join-Abhängigkeit erfüllt. (Der Begriff 6NF“ bezieht sich auf die ” gebräuchliche Projection/Join-Normalform, welche mit 5NF abgekürzt wird (siehe Bemerkung). Für einen tieferen Einblick in die Normalisierungstheorie siehe z.B. [Fag79] oder [Dat2000a].) Um nun Zeitstempel für Attribute zu modellieren, ist es hinreichend die 6NF für jede Relation der temporalen Datenbank zu fordern. Für jede Relation, die nicht in 6NF ist, kann die durch eine nicht-triviale Join-Abhängigkeit vorgegebene vertikale Dekomposition durchgeführt werden, bis alle Relationen diese Forderung erfüllen. Allerdings erscheint es unnötig eine Relation R entsprechend zu zerlegen, falls sich Attribute stets synchron verändern. Dies ist der Fall, wenn R eine kollektive JoinAbhängigkeit erfüllt. Bemerkung: Jede Relation in 6NF ist auch in Projection/Join-Normalform (5NF). Diese ist über die klassische Join-Abhängigkeit (also mit π und o n statt π L und o nL ) definiert. Für 5NF wird gefordert, dass jede nicht-triviale Join-Abhängigkeit erfüllt, auf einen Schlüsselkandidaten von R zurückgeht. 4.2.2. Beispiel Nr Name Gehalt ChefNr VT 2 Albers 6000 2 [1, 60] 10 Müller 3000 7 [10, 20] R := 10 Schmidt 3000 7 [21, 30] 10 Schmidt 4000 2 [31, 50] 10 Müller 4000 2 [51, 60] 10 Müller 5000 1 [61, 70] 30 4.2. Tupel- oder Attributzeitstempel Da zu jedem Zeitpunkt einem Angestellten genau ein Name, ein Gehalt und ein Chef zugeordnet werden, besteht der Primärschlüssel hier aus den Attributen ’Nr’ und ’VT’. R erfüllt die nicht-triviale Join-Abhängigkeit ∗({Nr, Name, VT}, {Nr, Gehalt, ChefNr, VT}) Damit ist R nicht in 6NF (jedoch in 5NF). Für die Projektion Nr Name VT 2 Albers [1, 60] VT πNr, Müller [10, 20] Name, VT (R) = 10 10 Schmidt [21, 50] 10 Müller [51, 70] existieren keine nicht-trivialen Join-Abhängigkeiten – sie ist in 6NF. Hingegen erfüllt die Projektion Nr Gehalt ChefNr VT 2 6000 2 [1, 60] VT πNr, 3000 7 [10, 30] Gehalt, ChefNr, VT (R) = 10 10 4000 2 [31, 60] 10 5000 1 [61, 70] die Join-Abhängigkeit ∗({Nr, Gehalt, VT}, {Nr, ChefNr, VT}) ist also nicht in 6NF. Unter der (zugegeben unrealistischen) Annahme, dass Gehalt und ChefNr stets gemeinsam (also synchron) verändert werden, ist diese Join-Abhängigkeit jedoch kollektiv. Die Zerlegung von R in die obigen beiden Projektionen ermöglicht dann eine Modellierung von Zeitstempeln für die Attribute VT sowie Gehalt synchron mit ChefNr. Ansonsten kann R anhand dieser (nicht kollektiven) Join-Abhängigkeit aufgeteilt werden: ∗({Nr, Name, VT}, {Nr, Gehalt, VT}, {Nr, ChefNr, VT}) Die genannten Zerlegungen sind natürlich nur dann sinnvoll, wenn die entsprechenden Join-Abhängigkeiten für jede semantisch mögliche Ausprägung von R gelten und nicht nur für diese konkreten Werten. Bemerkungen: • Durch die Zerlegung können getrennte Aussagen über Name und Gehalt/ChefNr getroffen werden – etwa könnte der Namen eines Angestellten für einem bestimmten Zeitpunkt bekannt sein, das Gehalt (und der Chef) jedoch nicht. Ist das nicht erwünscht, wäre eine zeitliche Fremdschlüsselbeziehung notwendig: VT Zu jedem Tupel aus unpack(πNr, Name, VT (R)) muss ein Tupel aus VT unpack(πNr, Gehalt, ChefNr, VT (R)) mit gleichen Werten für Nr und VT existieren. 31 4.3. Zukunft, Gegenwart und Vergangenheit • Allein von der konkreten Anzahl der Beispieltupel ausgehend, lohnt sich die Dekomposition hier nicht – 8 Tupel in zwei Relationen statt 6 Tupel in einer Relation. Im allgemeinen Fall kann sich das natürlich ändern. • Eine weitere nicht-triviale Join-Abhängigkeit die R offensichtlich erfüllt ist: ∗({Nr, VT}, {Nr, Name, VT}, {Nr, Gehalt, ChefNr, VT}) VT Ob es zweckmäßig ist durch die zusätzliche Relation πNr, VT (R) explizit zu materialisieren wie lange eine Person angestellt ist, kann nicht allgemein entschieden werden (vgl. die Relationen S, SS und SP in 5.2.1). 4.3. Zukunft, Gegenwart und Vergangenheit Wird an den Zeitstempel einer Relationen keine besondere zeitliche Bedingung gestellt, so kann die Aussage eines Tupels für beliebige Zeiträume in Zukunft, Gegenwart oder Vergangenheit gelten. Ist das Ende der Gültigkeit einer Aussage nicht abzusehen, wird ein künstlicher bis” auf-weiteres“-Wert benötigt. Ebenso müsste ein ∞“-Wert für ewig“ geltende Aussagen ” ” existieren, oder zumindest durch den spätesten darstellbaren Zeitpunkt angenähert werden können. Diese Konstrukte können jedoch zu logischen und semantischen Komplikationen führen (vgl. [DDL2003a] Kapitel 10.5). Eine weitere Möglichkeit mit unbekannten Gültigkeitszeiträumen umzugehen, ist diese gar nicht erst zu modellieren: Aussagen können in historische“ (für die der Gül” tigkeitszeitraum bekannt ist) und aktuelle“ (für die noch nicht feststeht wann ihre ” Gültigkeit endet) aufgeteilt werden. Für historische Aussagen wird wie bisher die ( voll” temporale“) Modellierung durch Relationen mit Zeitstempelintervall genutzt. Die ( semi” temporalen“) Relationen für aktuelle Aussagen enthalten dagegen kein ganzes Intervall als Zeitstempel, sondern nur den Zeitpunkt des Beginns der Gültigkeit. Steht das Ende der Gültigkeit einer aktuellen Aussage fest, wird das entsprechende Tupel mit dem nun bekannten Gültigkeitszeitraum in die historischen Relationen übernommen und aus den Relationen für aktuelle Daten entfernt. Diese Modellierung hat den Vorteil auf die Einführung der oben genannten speziellen Werte verzichten zu könnnen. Der Nachteil liegt in der zusätzlichen Anzahl von Relationen die durch die Partitionierung nach der Zeit (also durch die zeitliche horizontale Dekomposition) entstehen. Anfragen, für die keine Unterscheidung nach aktuell“ und ” historisch“ erwünscht ist, sind dann meist umständlicher zu formulieren. ” Anmerkungen: • Es ist natürlich möglich zukünftige Aussagen bei denen der Gültigkeitszeitraum feststeht“ ebenso durch eigene Relationen wie die historischen Aussagen zu mo” dellieren. Allerdings kann nicht automatisch vom DBMS festgestellt werden, wann 32 4.3. Zukunft, Gegenwart und Vergangenheit eine die Zukunft betreffende Aussage gilt und wann nicht6 . • Hier wird eine Zeitrichtung von Vergangenheit“ nach Zukunft“ unterstellt, da ” ” angenommen wird, dass der End zeitpunkt der Gültigkeit einer Aussage häufig unbekannt ist. Die umgekehrte Richtung (etwa wenn in einer archäologischen Datenbank der Beginn unbekannt ist) läßt sich offensichtlich analog modellieren. Beispiel Ist R die Relation aus Beispiel 4.2.2 und jetzt“ der Zeitpunkt 52, dann ergibt sich: ” Nr Name Gehalt ChefNr VT 2 Albers 6000 2 [1, bis-auf-weiteres] 10 Müller 3000 7 [10, 20] R := 10 Schmidt 3000 7 [21, 30] 10 Schmidt 4000 2 [31, 50] 10 Müller 4000 2 [51, bis-auf-weiteres] und mit horizontaler Dekomposition: Raktuell RHistorie Nr Name Gehalt ChefNr VT := 2 Albers 6000 2 1 10 Müller 4000 2 51 Nr Name Gehalt ChefNr VT 10 Müller 3000 7 [10, 20] := 10 Schmidt 3000 7 [21, 30] 10 Schmidt 4000 2 [31, 50] Die nächste Veränderung findet zum Zeitpunkt 61 statt: ’Albers’ scheidet aus und ’Müller’ bekommt ein höheres Gehalt und einen neuen Chef. Die entprechenden Tupel werden in die Historie übernommen. Nr Name Gehalt ChefNr VT 2 Albers 6000 2 [1, 60] 10 Müller 3000 7 [10, 20] R := 10 Schmidt 3000 7 [21, 30] 10 Schmidt 4000 2 [31, 50] 10 Müller 4000 2 [51, 60] 10 Müller 5000 1 [61, bis-auf-weiteres] mit horizontaler Dekomposition: 6 Wenn es im Jahre 1879 schon Computer gegeben hätte, würden diese vorausgesagt haben, dass man ” Infolge der Zunahme von Pferdewagen im Jahre 1979 im Pferdemist ersticken würde.“ (John C. Edwards) 33 4.3. Zukunft, Gegenwart und Vergangenheit Raktuell := RHistorie Nr Name Gehalt ChefNr VT 10 Müller 5000 1 61 Nr Name Gehalt ChefNr VT 2 Albers 6000 2 [1, 60] 10 Müller 3000 7 [10, 20] := 10 Schmidt 3000 7 [21, 30] 10 Schmidt 4000 2 [31, 50] 10 Müller 5000 1 [61, 70] Anfragebeispiele: Um die Frage nach den Namen der aktuell angestellten Personen zu beantworten, ist nun kein jetzt- oder bis-auf-weiteres-Wert mehr nötig, es genügt schlicht: πName (Raktuell ) im Gegensatz zu πName (σjetzt∈VT (R)) wobei zusätzlich darauf zu achten ist, dass etwa gilt jetzt ∈ [1, bis-auf-weiteres]. Wird allerdings die Frage Wie lauten die Namen aller jemals angestellten Personen?“ ” gestellt, ist die Anfrage an die zwei Relationen Raktuell und RHistorie komplizierter: πName (Raktuell ) ∪ πName (RHistorie ) im Gegensatz zu πName (R) 34 Der Tag ist 24 Stunden lang, aber unterschiedlich breit. (unbekannt) 5. Pragmatik – Anfragebeispiele In diesem Kapitel wird versucht eine gewisse Pragmatik (Erfahrung, Gefühl“) im Um” gang mit der erweiterten Relationenalgebra aufzubauen. Hierfür werden Anfragen an temporale Datenbanken aus verschiedenen Quellen in der neuen Algebra formuliert. Dadurch wird eine Erprobung der Ausdrucksfähigkeit der Algebra ermöglicht. Außerdem wird ein Vorrat an Beispielen zur Veranschaulichung der Modellierung und zum besseren Verständnis von Optimierung und Realisierung angeboten. 5.1. Beispiele aus [Bei2001a] – ein Vergleich mit TSQL2 Die in [Bei2001a] beschriebene objekt-relationale Realisierung einer temporalen Datenbanksprache basiert auf dem z.B. in [Sno95a] beschriebenen TSQL2-Ansatz. Statt der dort verwendeten temporalen Elemente wird in dieser Arbeit ein intervallwertiges Zeitstempelattribut verwendet (siehe 2.2.3). Um mit TSQL2 vergleichen zu können, wird im folgenden zuerst die entsprechende TSQL2-Anfrage angegeben. Hierbei sind einige syntaktische Besonderheiten zu beachten: temporale Aufwärtskompatibilität: Dieses Konzept führt dazu, dass Anfragen ohne spezielle Kennzeichnung durch das validtime-Schlüsselwort nur auf dem aktuellen Datenbestand (von heute“) arbeiten. Eine validtime-Anfrage liefert stets das ” Attribut mit dem Zeitstempel zurück, sofern sie nicht als nonsequenced gekennzeichnet ist1 . sequenced: Die Operatoren einer mit sequenced gekennzeichneten Anfrage wirken nur auf Tupel, die im gleichen Zeitraum gültig sind. Mit nonsequenced gibt es keine Einschränkungen. Die Angabe von sequenced nach validtime kann entfallen. coalesce: entspricht etwa einem pack auf das Zeitstempelattribut. (Es dient jedoch lediglich dazu das Ergebnis einer Anfrage besser lesbar zu machen.) 5.1.1. Beispieldatenbank Nr Name Gehalt ChefNr ang:= 12 Müller 5000 27 ... 1 VT [3, 7] In TSQL2 sind temporale (mit verstecktem Zeitstempel) und nicht-temporale Tabellen (ohne Zeitstempel) zwei verschiedene, nicht immer miteinander vereinbare Arten von Tabellen. 35 5.1. Beispiele aus [Bei2001a] – ein Vergleich mit TSQL2 Der Name der Beispielrelation ist ’ang’ – mit der offensichtlichen Semantik für Daten von Angestellten. In diesem Sinn sind die Tupel aus ’ang’ eindeutig durch ’Nr’ und den Zeitstempel ’VT’ identifiziert. Da in [Bei2001a] der Zeitstempel jedoch versteckt ist, wird dort zu diesem Zweck das (ebenfalls versteckte) Schlüsselsurrogat ’RWO’ eingeführt. Das Attribut ’Nr’ dient der Unterscheidung von Angestellten gleichen Namens. Im folgenden sei der Einfachheit halber angenommen, dass es keine Namensvettern in ’ang’ gibt.2 5.1.2. Anfragebeispiele Wie lauten die Daten der derzeit angestellten Personen? SELECT * FROM ang Sei heute ∈ N die aktuelle Zeit, dann ist die Anfrage formulierbar als: σheute∈VT (ang) Es werden alle Tupel ausgewählt, deren Zeitstempel die aktuelle Zeit enthalten. Wie lautet der Beschäftigungszeitraum eines Angestellten? VALIDTIME SELECT x.name, VALIDTIME(x.RWO) FROM ang x validtime wird hier auf ein Attribut angewandt und liefert laut [Bei2001a] den zum ” größtmöglichem Intervall erweiterten Gültigkeitszeitraum des Attributs“ zurück. VT πName, VT (ang) Um den Beschäftigungszeitraum zu erhalten, werden nur der Name (und der zugehörige VT eingebettete pack sorgt dann Zeitstempel) eines Angestellten projiziert. Das in π... dafür, dass der größtmögliche (zusammenhängende) Zeitraum ausgegeben wird. In welchen Zeiträumen war das Gehalt eines Angestellten konstant? NONSEQUENCED VALIDTIME SELECT x.name, VALIDTIME(x.gehalt) FROM ang x VT πName, VT (πName, Gehalt, VT (ang)) Wie oben werden wieder die für diese Anfrage unwichtigen Attribute durch die Projektion ausgeblendet und dann die Zeitstempel (durch pack) entsprechend zusammengefasst. Wer verdient(e) wann mehr als 5250? VALIDTIME COALESCE SELECT name FROM ang WHERE gehalt > 5250 VT πName, VT (σGehalt>5250 (ang)) Die Tupel aus ’ang’ mit einem Gehaltswert größer 5250 werden ausgewählt und dann der größtmögliche Zeitraum für jeden Namen ausgegeben. 2 sonst ist überall wo nach einem Angestellten gefragt ist, zusätzlich dessen Nummer auszugeben 36 5.1. Beispiele aus [Bei2001a] – ein Vergleich mit TSQL2 Wer verdient(e) mehr als sein Chef und wie heißt/hieß der Chef? VALIDTIME SELECT x.name, y.name FROM ang x, ang y WHERE x.chefnr = y.nr AND x.gehalt > y.gehalt Mit X := ρX.Nr = Nr, X.Name = Name, X.Gehalt = Gehalt, X.ChefNr = ChefNr (ang) und Y := ρY.Nr = Nr, Y.Name = Name, Y.Gehalt = Gehalt, Y.ChefNr = ChefNr (ang) ergibt sich πX.Name, Y.Name (σX.ChefNr = Y.Nr ∧ X.Gehalt > Y.Gehalt (X o nVT Y )) Durch den verwendeten erweiterten Join wird zunächst ein unpack auf das Zeitstempelattribut von X und Y durchgeführt. Die so entstandenen Einheitsintervallen der betrachtete Zeitraum von Angestellten (X) und Chefs (Y) werden dann durch o nVT auf Gleichheit geprüft: X.VT = Y.VT. Welche Angestellten haben jemals eine Gehaltserhöhung erhalten? NONSEQUENCED VALIDTIME SELECT x.name FROM ang x, ang y WHERE x.name = y.name AND x.gehalt < y.gehalt AND END(VALIDTIME(x)) = BEGIN(VALIDTIME(y)) (VALIDTIME(x) = x.VT und VALIDTIME(y) = y.VT – denn VT ist verstecktes Attribut) Mit X := ρX.Nr = Nr, X.Gehalt = Gehalt, X.ChefNr = ChefNr, X.VT = VT (ang) und Y := ρY.Nr = Nr, Y.Gehalt = Gehalt, Y.ChefNr = ChefNr, Y.VT = VT (ang) ergibt sich πName (σX.VT before Y.VT ∧ X.Gehalt < Y.Gehalt (X o n Y )) Hat ein Angestellter eine Gehaltserhöhung bekommen, so müssen zwei Tupel in ’ang’ existieren, die seine Gehaltsdaten vor (X) und nach (Y) der Erhöhung repräsentieren. Solche Tupelpaare werden ausgewählt und der zugehörige Name des Angestellten ausgegeben. Wie lauteten/lauten alle Angestelltendaten, falls jemand jemals 5500 verdient hat? VALIDTIME COALESCE SELECT * FROM ang WHERE EXISTS ANYTIME( VALIDTIME COALESCE SELECT * FROM ang WHERE gehalt = 5500) Hat ein Angestellter irgendwann 5500 verdient, so ist die Relation σGehalt=5500 (ang) nichtleer. Mit der konstanten Selektionsfunktion ϕ = σGehalt=5500 (ang) 6= ∅ entspricht die Anfrage dem Term σϕ (ang) der erweiterten Relationenalgebra. Es werden also entweder alle Angestelltendaten ausgegeben oder keine. 37 5.2. Beispiele aus [DDL2003a] Wie lauteten/lauten alle Angestelltendaten, als jemand 5500 verdient hat? VALIDTIME COALESCE SELECT * FROM ang WHERE EXISTS SOMETIME( VALIDTIME COALESCE SELECT * FROM ang WHERE gehalt = 5500) Die Unteranfrage“ in TSQL2 kann durch einen Join entschachtelt werden: ” Xo nVT Y mit mit Y :=πVT (σGehalt = 5500 (ang)) und X := ang 5.2. Beispiele aus [DDL2003a] In [DDL2003a] werden zunächst temporal-relationale Operatoren in einer beispielhaften Sprache ( Tutorial D“) definiert, an denen sich die hier vorgestellte erweiterte Relationen” algebra anlehnt. Kapitel 13 beinhaltet Beispielanfragen an eine Temporale Datenbank. 5.2.1. Beispieldatenbank Die Beispiele beziehen sich auf drei Relationen: S# during 2 [2, 4] ... Relation S: Diese Relation stellt die Vertragslaufzeit (during) eines Zulieferers (S# – supplier) dar – also hier: Zulieferer 2 ist (oder war oder wird sein) von 2 bis 4 unter Vertrag.“ ” S# Status during Relation SS: 2 5 [2, 2] ... Hier wird der Status den ein Zulieferer in einem bestimmten Zeitraum hat/hatte vermerkt: Zulieferer 2 hat den Status 5 zum Zeitpunkt 2.“ ” S# P# during Relation SP: 2 1 [2, 3] ... In SP wird angegeben welche Teile (P# – part) ein Zulieferer in welchem Zeitraum liefern kann: Zulieferer 2 kann Teil 1 von 2 bis 3 liefern.“ ” 5.2.2. Anfragebeispiele Wie lautet der Status des Zulieferers ’1’ zum Zeitpunkt n? Dies läßt sich für ein n ∈ N formulieren als πStatus (σS#=’1’ ∧ n ∈ during (SS)) 38 5.2. Beispiele aus [DDL2003a] Welche Zulieferer konnten im gleichen Zeitraum die beiden Teile ’1’ und ’2’ liefern? Ist bekannt wann ein Zulieferer ’1’ liefern kann X := πS#, during (σP#=’1’ (SP )) und entprechend wann ein Zulieferer ’2’ liefern kann Y := πS#, during (σP#=’2’ (SP )) liefert ein erweiterter Verbund das gesuchte Ergebnis: πS# (X o nduring Y ) Welche Zulieferer konnten nie im gleichen Zeitraum die beiden Teile ’1’ und ’2’ liefern? Ist X die Ergebnisrelation aus dem vorigen Beispiel, dann entspricht die Anfrage dem Term πS# (S) − X In welchen Zeiträumen konnte ein Zulieferer keine Teile liefern? S −during πS#, during (SP ) Welche Zulieferer konnten wann welche Teile liefern? Für die geforderte Übersicht erscheint es sinnvoll nicht nur die Zeiträume, sondern auch die Teile als Intervall auszugeben. Hierzu wird zunächst ein Einheitsintervall für jedes Teil gebildet. Die größtmöglichen Intervalle werden dann ausgegeben. during, parts πS#, parts, during (parts=[P#,P#] (SP )) In welchen Zeiträumen war mindestens ein Zulieferer unter Vertrag? during πduring (S) Durch die Verwendung der erweiterten Projektion erhält man die größtmöglichen Zeiträume. In welchen Zeiträumen war kein Zulieferer unter Vertrag? Sei C eine Relation mit dem intervallwertigen Attribut ’during’, welche das Tupel [0, max] enthält, wobei max der letzte darstellbare Zeitpunkt ist. Damit ergibt sich das Ergebnis C −during πduring (S) 39 5.3. Beispiele zu Gruppierung und Aggregation Welche Zulieferer, die schon mal unter Vertrag waren, sind heute erneut unter Vertrag? Sei heute ∈ N die aktuelle Zeit. Unter der Annahme, dass jedes Tupel in S einem eigenen Vertrag entspricht (vgl. 4.1.4), ist die Anfrage formulierbar als: πS# (σheute∈during (S)) o n πS# (σheute>end(during) (S)) Wie lauten die Paare von (verschiedenen) Zulieferern, die zum selben Zeitpunkt einen (neuen) Status zugewiesen bekamen? Geht man davon aus, dass SS = packduring (SS) gilt, lautet der entsprechende Ausdruck in der Relationenalgebra: πX.S#, Y.S# (σbegin(X.during) = begin(Y.during) ∧ X.S# < Y.S# (X o n Y ) mit X := ρX.S# = S#, X.during = during (SS) und Y := ρY.S# = S#, Y.during = during (SS) Bemerkung: Geht man nicht von der Annahme aus, dass SS stets gepackt“ gehalten ” wird, wäre eine kompliziertere Formulierung nötig: Es müsste festgestellt werden, ob für ein Tupel aus X (bzw. Y ) eine Veränderung des Status bezüglich des vorherigen Zeitpunkts vorliegt. 5.3. Beispiele zu Gruppierung und Aggregation Die bisher betrachteten Quellen bieten wenig Beispiele für gruppierende oder aggregierende Anfragen. Um die Auswirkung von den in die erweiterte Relationenalgebra eingebetteten Operatoren pack und unpack auf solche Anfragen beurteilen zu können, erscheinen weitere Beispiele sinnvoll. 5.3.1. Beispieldatenbank Produkt Rennrad Rennrad Relation R: Rennrad Mountainbike Mountainbike Preis 300 250 270 270 240 Hersteller Produkt Teua Rennrad Shnel Rennrad Relation S: Teua Rennrad Shnel Mountainbike Robus Mountainbike 40 Zeit [1, 7] [8, 14] [15, 25] [1, 20] [21, 30] Zeit [1, 7] [8, 20] [21, 25] [1, 10] [11, 30] 5.3. Beispiele zu Gruppierung und Aggregation 5.3.2. Anfragebeispiele Was ist der höchste Preis pro Produkt? ΓProdukt # Produkt, Höchstpreis = max(Preis) (R) Eine Formulierung mit dem erweiterten Operator ΓZeit ... führt zum gleichen Ergebnis, denn das Maximum ist von der Anzahl der Tupel einer Gruppierung unabhängig: unpackZeit (R) enthält zwar mehr Tupel als R, jedoch bleibt der Wertebereich von ’Preis’ gleich. Was ist der Höchstpreis pro Produkt und wann wird er angenommen? Ist X das Ergebnis aus obiger Anfrage, dann lassen sich die gesuchten Tupel mit einem natürlichen Verbund aus R herausfiltern: Ro n (ρ Höchstpreis = Preis (X)) Eine Formulierung mit o nZeit würde bewirken, dass überlappende Zeitbereiche mehrerer Maxima zusammengefasst werden. Wieviele verschiedene Produktpreise pro Hersteller gibt es? Diese Anfrage soll etwa für den Hersteller ’Shnel’ das Ergebnis ’2’ liefern: Er hat Rennräder im Zeitraum [8, 14] zum Preis ’250’ und im Zeitraum [15, 20] zum Preis ’270’ sowie Mountainbikes im Zeitraum [1, 10] zum Preis ’270’ hergestellt. Das sind genau zwei verschiedene Produktpreise. ΓHersteller # Hersteller, Anzahl = count (πHersteller, Preis (R o nZeit S)) Zunächst wird die Beziehung von Hersteller und Preis über den Fremdschlüssel (bestehend aus Produkt und Zeit) durch die Verwendung eines erweiterten Verbunds hergestellt. Da nicht nach Produkt (sondern nur nach Preis) unterschieden werden soll und der Zeitraum hier ebenfalls uninteressant ist, wird das Ergebnis auf Hersteller und Preis eingeschränkt. Wie hoch ist der Durchschnittspreis pro Produkt? Bei dieser Frage ist zu präzisieren auf welchen Zeitraum sich die Bildung des Durchschnitts beziehen soll: ΓProdukt # Produkt, Durchschnitt = avg(Preis) (R) = 255 als Durchschnittspreis für Mountainbikes, wobei sich der liefert etwa 270+240 2 Durchschnitt auf die zwei Tupel in R bezieht. Andererseits liefert ΓZeit Produkt # Produkt, Durchschnitt = avg(Preis) (R) 270∗20+240∗10 20+10 = 260 als Durchschnittspreis für Mountainbikes, wobei der Preis ’270’ mit 20 Zeiteinheiten ins Gewicht fällt und ’240’ mit 10 Zeiteinheiten. 41 5.3. Beispiele zu Gruppierung und Aggregation Wie hoch ist der Durchschnittspreis aller Produkte insgesamt pro Zeiteinheit? Das Produkt ’Rennrad’ hat im Zeitraum [1, 7] den Preis 300; ’Mountainbike’ kostet in diesem Zeitraum 270. Die Anfrage sollte also beispielsweise für den Zeitraum [1, 7] den = 285 liefern. Durchschnittspreis 300+270 2 ΓZeit Zeit # Zeit, Durchschnitt = avg(Preis) (R) Damit für jeden einzelnen Zeitpunkt der Durchschnittspreis bestimmt wird, muss hier ΓZeit ... benutzt werden. Eine Formulierung mit dem klassischen Gruppierungsoperator Γ... ergäbe πZeit, Preis (R) als Ergebnis, denn alle Zeit-Intervalle in R sind verschieden. Wie hoch ist der Durchschnittspreis pro Produkt pro Jahr? Angenommen die Zeiteinheiten im ’Zeit’-Attribut entsprechen fortlaufenden Monaten vom Januar 2000 an, also z.B.: Das Produkt ’Mountainbike’ hat im Zeitraum vom ” Januar 2000 (1) bis August 2001 (20=12+8) den Preis ’270’“. In diesem Fall läßt sich S um ein Attribut ’Jahr’ erweitern, welches das zum Zeitstempel gehörige Jahr angibt: (S) X := εZeit begin(Zeit) e+1999 Jahr=d 12 Bemerkung: Die Benutzung des erweiterten Operators εZeit ist notwendig, damit ein ... Zeitstempel eindeutig einem Jahr zugeordnet werden kann. Im gepackten“ Zustand ist ” dies hier nicht immer möglich (etwa bei [1, 20]). Wieder sind zwei Formulierungen denkbar: (Zeit) ΓProdukt, Jahr # Produkt, Jahr, Durchschnitt = avg(Preis) (X) 270∗12 = 270 für das Jahr 2000 Für Mountainbikes ergibt die mit ΓZeit ... formulierte Anfrage 12 270∗8+240∗4 und = 260 für 2001; entsprechend liefert die Anfrage mit dem klassischem 8+4 270 270+240 Γ-Operator 1 = 270 für 2000 und = 255 für 2001. 2 Wann ist der frühste Zeitpunkt des Beginns der Produktion pro Hersteller pro Produkt? Der gesuchte Zeitpunkt wäre z.B. ’8’ für den Hersteller ’Shnel’ und das Produkt ’Rennrad’. ΓHersteller, Produkt # Hersteller, Produkt, Beginn = min(Beginn) (εBeginn=begin(Zeit) (R)) Durch die Verwendung des Intervalloperators begin wird der frühste Zeitpunkt eines Zeitraums ermittelt. 42 5.3. Beispiele zu Gruppierung und Aggregation Wie lange wird im Durchschnitt ein bestimmtes Produkt von einem Hersteller produziert? Rennräder werden etwa vom Hersteller ’Teua’ durchschnittlich lang produziert. 7+5 2 = 6 Zeiteinheiten ΓHersteller, Produkt # Hersteller, Produkt, Durchschnitt = avg(Anzahl) (εAnzahl=count(Zeit) (R)) Der Intervalloperator count liefert die Länge eines Zeitraums. 43 6. Optimierung Die erweiterte Relationenalgebra basiert auf pack und unpack (siehe 3.4). Für eine Realisierung der erweiterten Relationenalgebra erscheint es deshalb sinnvoll, diese Operatoren möglichst optimal zu implementieren. Insbesondere unpack erscheint hier kritisch, denn unter Umständen enthält eine Relation im unpacked“-Zustand sehr viel mehr Tupel als das letztendliche Ergebnis. Wenn ” möglich sollte also die Materialisierung von ungepackten“ Relationen vermieden werden. ” Für diesen Abschnitt seien X = (S, I) und Y Relationen. L = l1 , . . . , ln sei eine Liste von Attributnamen intervallwertiger Attribute von X oder Y , und L := def(S) − L sei die Menge aller Attribute von X, die nicht in L vorkommen. 6.1. Zerlegung in Teilintervalle – split Die komplette Zerlegung einer Relation X in Einheitsintervalle durch unpackL (X) ist häufig unnötig: meist genügt es zu fordern, dass die betreffenden Intervalle nicht überlappen. Eine Zerlegung von X in maximale, nicht überlappende Intervalle hat den Vorteil, dass für gewöhnlich weniger Tupel produziert werden als durch unpack(X). In den meisten Fällen kann unpack durch einen Operator split ersetzt werden, der solch eine Zerlegung in Teilintervalle leistet: splitlPX (X) zerlegt die Tupel einer Ausgangsrelation X für ein intervallwertiges Attribut l anhand einer Punktmenge PX . Ein Tupel mit dem l-Intervallwert [b, e] wird durch einen Punkt p ∈ PX in zwei Teile“ mit [b, p − 1] und [p, e] gespalten, falls b < p ≤ e gilt. ” Ein Tupel x ∈ X mit x(l) = [b, e] wird anhand von b < p1 < · · · < pm ≤ e für p1 , . . . , pm ∈ PX demnach durch eine Menge von Tupeln mit folgenden l-Werten ersetzt: [b, p1 − 1], [p1 , p2 − 1], . . . , [pm−1 , pm − 1], [pm , e] Algorithmus für splitlPX (X): Für alle x ∈ X: Setze q := begin(x(l)). Für alle p ∈ PX in aufsteigender Reihenfolge mit begin(x(l)) < p und p ≤ end(x(l)): Füge ein Tupel t mit gleichen Attributwerten wie x bis auf t(l) := [q, p − 1] dem Ergebnis hinzu. Setze q := p. Füge ein Tupel t mit t(l) := [q, end(x(l))] dem Ergebnis hinzu. 44 6.1. Zerlegung in Teilintervalle – split Analog zu unpackL (X) wird splitL (X) durch die Hintereinanderausführung für alle Attribute in L = l1 , . . . , ln definiert: l splitLPX (X) := splitlPnX (splitPn−1 (. . . splitlP1X (X) . . . )) X Maximale nicht-überlappende Intervalle Will man erreichen, dass die l-Intervalle zweier Tupel aus X nur überlappen, falls sie gleich sind, soll also die Bedingung ∀x, x̃ ∈ X : x(l) overlaps x̃(l) ⇒ x(l) = x̃(l) erfüllt sein, kann die Menge PX folgendermaßen gewählt werden: PX := { begin(x(l)) : x ∈ X ∧ ∃ x̃ ∈ X ∧ x(l) 6= x̃(l) ∧ begin(x(l)) ∈ x̃(l) } ∪ { end(x(l)) + 1 : x ∈ X ∧ ∃ x̃ ∈ X ∧ x(l) 6= x̃(l) ∧ end(x(l)) ∈ x̃(l) } PX entsteht durch alle Anfangs- und Endpunkten von l-Intervallen aus X, die im lIntervall eines anderern Tupels liegen (vgl. Abbildung 6.1 im unteren Beispiel). PX ist die Menge mit der minimalen Anzahl von Elementen für die splitlPX (X) obige Bedingung erfüllt1 . Folglich ist die Zerlegung der Tupel von X anhand von PX so grob wie möglich, die Intervalle haben also größtmögliche Länge und sind damit in diesem Sinn maximal. Eine Zerlegung in maximale nicht-überlappende Intervalle ist auch mit einer einfacher zu konstruierenden Punktmenge möglich – siehe 6.5.1. Beispiel: Name VT Müller [2, 15] Mit X := ist PX = {3, 13} Müller [60, 68] Schmidt [3, 12] Name VT Müller [2, 2] Müller [3, 12] und damit splitVT . PX (X) = Müller [13, 15] Müller [60, 68] Schmidt [3, 12] 1 Annahme: Es genügt eine Menge QX mit |QX | < |PX |. Für p ∈ PX − QX existieren x, x̃ ∈ X mit x(l) 6= x̃(l) und es gilt p ∈ x(l) (bzw. p − 1 ∈ x(l)) sowie p ∈ x̃(l) (bzw. p − 1 ∈ x̃(l)) also x(l) overlaps x̃(l). 45 6.2. Vermeidung von unpack und pack Müller Schmidt 3 12 Abbildung 6.1.: Graphische Veranschaulichung von splitVT PX (X) 6.2. Vermeidung von unpack und pack Für fast alle Operatoren der erweiterten Relationenalgebra ist eine Implementierung möglich, die keine Materialisierung der Ausgangsrelationen im unpacked“-Zustand be” nötigt. In manchen Fällen kann sogar eine äquivalente Formulierung ohne anfängliches unpack angegeben werden. Das abschließende pack ist ebenfalls nicht immer nötig: Sind die Ausgangsrelationen bereits gepackt“, so bleibt diese Eigenschaft bei einigen Opera” toren erhalten. Im folgenden wird jeder Operator detailliert besprochen. 6.2.1. Projektion Da für die Projektion die konkreten Werte eines Tupels unwichtig sind, spielt es keine Rolle ob die Ausgangsrelation ungepackt“ vorliegt oder nicht: ” L πA (X) := packL ( πA (unpackL (X)) ) = packL ( πA (X) ) Werden alle Attribute von X projiziert (oder kein Attribut aus L) kann auch packL entfallen. 6.2.2. Erweiterung Bei der Erweiterung εLb=β (X) := packL ( εb=β (unpackL (X)) ) einer Relation X um ein Attribut b wie sie in 3.1.2 beschrieben ist, sind in Abhängigkeit von β zwei Fälle zu unterscheiden: Gilt β = β|L , wird also für die Berechnung von b kein Gebrauch von den intervallwertigen Attributen aus L gemacht, dann ist unpackL unnötig: εLb=β (X) = packL ( εb=β (X) ) Das abschließende packL kann auch hier entfallen, sofern X bereits entsprechend ge” packt“ ist. Andernfalls läßt sich εLb=β (X) in einem einzigen Durchlauf über X erzeugen. In diesem Fall müssen entsprechend auch nur intervallwertige Attribute betrachtet werden, die β wirklich benötigt: Ist l ∈ L = {l1 , . . . , ln } so kann dieses l entfallen, falls β = β|def(S)−{l} gilt. 46 6.2. Vermeidung von unpack und pack Algorithmus: Für alle x ∈ X: Für alle pi ∈ x(li ) (1 ≤ i ≤ n): Berechne für ein Tupel mit gleichen L-Werten wie x, aber mit Einheitsintervallen [pi , pi ] das Ergebnis von β. Bilde also ein Tupel t mit t|L := x|L , t(li ) := [pi , pi ], setze t(b) := β(t|def(S) ) und füge t dem Ergebnis hinzu. Wende packL für die eben erzeugten Tupel an. Durch das auf Tupelebene“ (Pipelining – siehe 6.3.1) für jedes x ∈ X durchgeführte ” packL , wird die Materialisierung von unpackL (X) auf einzelne Tupel beschränkt. Liegt jedoch X nicht bereits gepackt“ vor, so kann dadurch kein gepacktes“ Ergebnis garan” ” tiert werden und es wird zusätzlich ein abschließendes packL (X) benötigt. Ist mehr über β (und X) bekannt, ist es manchmal möglich εL durch ε zu ersetzen: Die im Beispiel 5.3.2 verwendete Erweiterung um das Attribut ’Jahr’ könnte mit den regulären Operator ausgedrückt werden, wenn kein Zeitstempel mehr als ein Jahr enthält. 6.2.3. Selektion Anhand der Selektionsformel ϕ einer Selektion σϕL (X) := packL ( σϕ (unpackL (X)) ) können analog zur Erweiterung zwei Fälle unterschieden werden: Gilt ϕ = ϕ|L , ist die Selektion also von den intervallwertigen Attributen aus L unabhängig, dann ist unpackL unnötig: σϕL (X) := packL ( σϕ (X) ) Wieder entfällt packL falls X bereits entsprechend gepackt“ ist. ” Andernfalls läßt sich auch σϕL (X) in einem einzigen Durchlauf über X ohne die Materialisierung von unpackL (X) erzeugen: Für alle x ∈ X: Für alle pi ∈ x(li ) (1 ≤ i ≤ n): Berechne für ein Tupel mit gleichen L-Werten wie x, aber mit Einheitsintervallen [pi , pi ] das Ergebnis von ϕ. Füge also das Tupel t mit t|L := x|L und t(li ) := [pi , pi ] der Ergebnisrelation hinzu, falls ϕ(t) = wahr ist. Wende packL für die eben erzeugten Tupel an. Wie bei εLb=β (X) sind nur die intervallwertigen Attribute wichtig, die wirklich von ϕ benötigt werden: Ist l ∈ L so kann dieses l entfallen, falls ϕ = ϕ|def(S)−{l} gilt. Ebenso wird zusätzlich ein abschließendes packL (X) benötigt, sollte X nicht bereits gepackt“ ” vorliegen. Angenommen ϕ läßt sich in einen von Attributen in L unabhängigen Teil ϕ1 und einen abhängigen Teil ϕ2 zerlegen: ϕ = ϕ1 ∨ ϕ2 mit ϕ1 = ϕ1 |L und ϕ2 6= ϕ2 |L . Dann kann bereits vor der inneren Schleife ( Für alle pi ∈ x(li )“) geprüft werden, ob ein Tupel x ” selektiert wird, also ϕ1 (x) = wahr gilt. Ist dies der Fall, so ist es möglich den Test für ϕ2 (x) zu übergehen, das Tupel in das Ergebnis zu übernehmen und mit dem nächsten Tupel fortzufahren. (Analog wäre auch eine konjuktive Zerlegung ϕ = ϕ1 ∧ ϕ2 möglich.) 47 6.2. Vermeidung von unpack und pack 6.2.4. Verbund Für den erweiterte Verbund Xo nL Y := packL ( unpackL (X) o n unpackL (Y ) ) genügt die Zerlegung in maximale nicht-überlappende Intervalle: Xo nL Y = packL ( splitLPX∪Y (X) o n splitLPX∪Y (Y ) ) Die für split benötigte Punktmenge PX∪Y ergibt sich hier aus den Intervallgrenzen der beiden Relationen X und Y. Beispiel: Name Meier Müller Sei X := Müller Schmidt Müller VT Name VT [6, 13] Müller [10, 20] [10, 15] Müller [33, 44] und Y := . [18, 20] Schmidt [22, 32] [21, 55] Schmidt [45, 50] [56, 70] Schulze [51, 55] Dann können die VT-Intervalle anhand der Punktmenge PX∪Y := {10, 14, 16, 18, 22, 33, 45, 51, 56} zerlegt werden: Name Meier Meier Müller Müller Müller splitVT PX∪Y (X) = Schmidt Schmidt Schmidt Schmidt Schmidt Müller VT [6, 9] Name VT [10, 13] Müller [10, 13] [10, 13] Müller [14, 15] [14, 15] Müller [16, 17] [18, 20] , splitVT (Y ) = Müller [18, 20] PX∪Y [21, 21] Müller [33, 44] [22, 32] Schmidt [22, 32] [33, 44] Schmidt [45, 50] [45, 50] Schulze [51, 55] [51, 55] [56, 70] Name Müller Müller splitLPX∪Y (X) o n splitLPX∪Y (Y ) ergibt Müller Schmidt Schmidt 48 VT [10, 13] [14, 15] [18, 20] [22, 32] [45, 50] 6.2. Vermeidung von unpack und pack Für den Spezialfall, dass keine gleichnamigen, intervallwertigen Attribute von X und Y in L enthalten sind, ist o nL... schlicht unnötig: Xo nL Y = packL (X o nY) packL kann hier entfallen, wenn X und Y bereits gepackt“ sind. ” Alternativ läßt sich der erweiterte Verbund auch direkt auf den klassischen Operator abbilden: Mit dem Intervalloperator overlaps werden Verbundpartner bestimmt, dann mit intersect das Schnittintervall berechnet. Sei etwa l ∈ L das einzige gleichnamige, intervallwertige Attribut von X und Y . Dann kann zunächst der klassische Verbund für alle anderen gleichnamigen Attribute gebildet werden. Hierzu ist l etwa in xl (für X) bzw. yl (für Y ) umzubenennen2 : R := ρ l=xl (X) o n ρ l=yl (Y ) Mittels overlaps wird nun festgestellt ob Verbundpartner für das Attribut l vorliegen, wobei intersect den zu l gehörigen Wert liefert: S := ε l=xl intersect yl (σxl overlaps yl (R)) Zuletzt werden die jetzt überflüssigen Attribute xl und yl von S entfernt und die Ergebnisrelation gepackt“: ” Xo nl Y = packl (πAlle Attribute bis auf xl und yl (S)) Für mehrere gleichnamige, intervallwertige Attribut aus L kann analog vorgegangen werden: Jedes Attribut ist wie oben zunächst exklusiv für X und Y umzubenennen. Ebenso muss für jedes Intervallattribut die overlaps-Bedingung gelten und das zugehörige Intervall mit intersect berechnet werden. Abschließend ist für das entsprechend projizierte Ergebnis packL anzuwenden. Im Beispiel: εVT = XVT intersect YVT (σXVT overlaps YVT (ρVT = XVT (X) o n ρVT = YVT (Y )) = Name Müller Müller Schmidt Schmidt XVT [10, 15] [18, 20] [21, 55] [21, 55] YVT [10, 20] [10, 20] [22, 32] [45, 50] VT = XVT ∩ YVT [10, 15] [18, 20] [22, 32] [45, 50] Anmerkungen: • Nach dem letztgenannten Verfahren kann pack entfallen, wenn L nur ein einziges Attribut enthält und X und Y bereits gepackt“ sind. ” • Für den natürlichen Verbund von zwei gleichen Relationen ( Self Join“) gilt stets ” Xo nL X = X o n X. 2 O.B.d.A gibt es noch kein Attribut xl in X bzw. yl in Y . 49 6.2. Vermeidung von unpack und pack 6.2.5. Vereinigung Für die Vereinigung zweier Relationen gilt folgendes Distributivgesetz: unpackL (X) ∪ unpackL (Y ) = unpackL (X ∪ Y ) Damit kann das anfängliche unpack kann entfallen. X ∪L Y : = packL ( unpackL (X) ∪ unpackL (Y ) ) = packL (unpackL (X ∪ Y )) = packL (X ∪ Y ) 6.2.6. Differenz Die Differenz X −L Y := packL ( unpackL (X) − unpackL (Y ) ) kann wie der Verbund durch split ausgedrückt werden: X −L Y := packL ( splitL PX∪Y (X) − splitL PX∪Y (X) ) Beispiel: Mit den Beispielrelationen X und Y aus 6.2.4 ergibt sich: Name Meier Meier L L split PX∪Y (X) − split PX∪Y (X) = Schmidt Schmidt Schmidt Müller VT [6, 9] [10, 13] [21, 21] [33, 44] [51, 55] [56, 70] Differenz für genau ein Attribut Enthält L genau ein Attribut l, dann läßt sich X −l Y berechnen ohne auf split zurückgreifen zu müssen. Ähnlich dem in 6.2.4 beschriebenen Verfahren läßt sich zunächst ein Verbund von X und Y bilden: R := ρ l=xl (X) n ρ l=yl (Y ) Ein Tupel des Minuenden X, das keinen Verbundpartner im Subtrahenden Y hat, muss in der Ergebnisrelation vorkommen. Der obige Verbundoperator n ( Outer Join“) liefert ” daher alle Tupel aus X ggfs. zusammen mit Verbundpartnern aus Y . Der Operator n ist nicht Teil der in 3.1 definierten Relationenalgebra, weil hierfür ein Wert für undefiniert“ eingeführt werden müsste (siehe folgendes Beispiel). (In Oracle ist ” solch ein null-Wert jedoch vorhanden und n kann etwa Left Outer Join implementiert werden.) Zur Differenzbildung genügt es Tupel aus R zu betrachten für die XVT overlaps YVT gilt oder für die YVT nicht definiert ist: S := σxl ist undefiniert oder xl overlaps yl (R) 50 6.2. Vermeidung von unpack und pack Im Beispiel: σYVT ist undefiniert oder XVT overlaps YVT (ρVT = XVT (X) n ρVT = YVT (Y )) = Name Meier Müller Müller Schmidt Schmidt XVT [6, 13] [10, 15] [18, 20] [21, 55] [21, 55] YVT undefiniert [10, 20] [10, 20] [22, 32] [45, 50] Die Differenz kann nun mit Hilfe einer neuen Aggregationsfunktion minus berechnet werden: X −l Y = packl (ΓL# Alle Attribute aus L, minus(xl,yl) (S)) Auf die so erzeugten Partitionen wirkt die Aggregationsfunktion minus, die die Differenz zwischen xl-Wert und den entsprechenden yl-Werten bildet. Die so erzeugten Intervalle werden zusammen mit den anderen zugehörigen Attributen (aus L) ausgegeben. Algorithmus für minus: Der folgende Algorithmus für die Aggregationsfunktion minus verlangt eine aufsteigende Sortierung nach begin(xl) und (zweitranging nach) begin(yl). Für eine Gruppe G sind beginnend mit dem ersten Tupel folgende Schritte auszuführen: Für ein Tupel g ∈ G: 1. Setze b, e ∈ N auf den Wert des xl-Attributs: [b, e] := g(xl). 2. Solange e + 1 ≥ begin(g(xl)) gilt und g(yl) definiert ist: Setze e := end(g(xl)) falls e < end(g(xl)) ist. Ist b < begin(g(yl)), dann füge ein Tupel x mit x|L := g|L und x(l) := [b, begin(g(yl)) − 1] dem Ergebnis hinzu. Setze b := end(g(yl)) + 1 falls b ≤ end(g(yl)) ist. Fahre mit dem nächsten Tupel für g fort. 3. Sofern b ≤ e gilt, füge dem Ergebnis ein Tupel x mit x|L := g|L und x(l) := [b, e] hinzu. Fahre mit dem nächsten Tupel für g fort. Mit diesem Algorithmus wird kein abschließendes pack benötigt (vgl. den entsprechenden Algorithmus für pack in 6.3.1). Im Beispiel: Es ergibt sich folgender Ablauf des Algorithmus: 51 6.2. Vermeidung von unpack und pack Schritt 1 3 1 2 3 1 2 3 1 2 2 3 Name Meier Meier Müller Müller Müller Müller Müller Müller Schmidt Schmidt Schmidt Schmidt XVT [6, 13] [6, 13] [10, 15] [10, 15] [10, 15] [18, 20] [18, 20] [18, 20] [21, 55] [21, 55] [21, 55] [21, 55] YVT Ausgabe undefiniert – undefiniert (Meier, [6, 13]) [10, 20] – [10, 20] – [10, 20] – [10, 20] – [10, 20] – [10, 20] – [22, 32] – [22, 32] (Schmidt, [21, 21]) [45, 50] (Schmidt, [33, 44]) [45, 50] (Schmidt, [51, 55]) [b, e] [6, 13] [6, 13] [10, 15] [21, 15] [21, 15] [18, 20] [21, 20] [21, 20] [21, 55] [33, 55] [51, 55] [45, 55] 6.2.7. Durchschnitt Der Durchschnitt X ∩L Y := packL ( unpackL (X) ∩ unpackL (Y ) ) muss nicht durch X −L (X −L Y ) implementiert werden. Für den Durchschnitt als Spezialfall des Verbunds gilt: X ∩L Y = X o nL Y 6.2.8. Gruppierung und Aggregation Die Vermeidung von unpack Gruppierung und Aggregation ΓLA # b1 =α1 ,...,bn =αn (X) := packL ( ΓA # b1 =α1 ,...,bn =αn (unpackL (X)) ) ist ohne genauere Kenntnis über die benutzten Aggregationsfunktionen nicht möglich. Allgemein läßt sich die Materialisierung einer ungepackten“ Relation auf die durch die ” Gruppierung erzeugten Gruppen beschränken: Wird etwa die Ausgangsrelation X in die Gruppen G1 , G2 , . . . zerlegt, dann müssen stets nur jeweils unpackL (G1 ), unpackL (G2 ), . . . materialisiert werden – nicht unbedingt unpackL (X). Allerdings ist durch diese Vorgehensweise nichts gewonnen wenn A = ∅ gilt, denn dann ist die ganze Relation X die einzige Gruppe“. ” Ist die Gruppierung von den Attributen in L unabhängig (gilt also A ∩ L = ∅) ist es im Prinzip möglich ganz auf unpack zu verzichten, indem die Aggregationsfunktionen ersetzt werden: ΓLA # b1 =α1 ,...,bn =αn (X) = packL ( ΓA # b1 =α∗1 ,...,bn =α∗n (X) ) Eine Funktion αi wird hierbei durch eine Funktion αi∗ ersetzt, die das gleiche Ergebnis wie αi liefert auch wenn die übergebene Gruppe G nicht ungepackt“ ist. Das sollte mög” lich sein, denn aus G läßt sich (selbstverständlich) unpackL (G) konstruieren. Allgemein 52 6.2. Vermeidung von unpack und pack wird so die Materialisierung von unpackL (G) lediglich vom relationalen Operator auf die Aggregationsfunktion verschoben. Abhängig von der benutzten Funktion kann diese Verschiebung allerdings Vorteile bringen. Häufig benutzte Aggregationsfunktionen Sei G eine Gruppe aus der erzeugten Gruppierung und o.B.d.A. L = l1 , . . . , ln mit n ∈ N, dann können folgende Aggregationsfunktionen (vgl. 3.1.2) ersetzt werden: • count kann durch count∗ ersetzt werden, indem Gebrauch vom Intervalloperator count gemacht wird: X Y count∗ := { count(t(li ))} t∈G 1≤i≤n • Ähnlich kann sum durch sum∗ ersetzt werden: X Y sum∗ (a) := { t(a) · count(t(li ))} 1≤i≤n t∈G • avg∗ analog mit sum∗ und count∗ • Da Minimum und Maximum vom gepackten“ oder ungepackten“ Zustand einer ” ” Relation unabhängig sind, ist hier keine Ersetzung notwendig: min∗ (a) := min(a) und max∗ (a) := max(a). Diese Ersetzungen erlauben nun Gruppierung und Aggregation durchzuführen ohne dass eine Materialisierung von unpack(G) nötig wäre. Da Summe, Minimum und Maximum hier nicht für Intervalle definiert sind, kann das oben angegebene Attribut a kein intervallwertiges Attribut sein. Wollte man etwa eine Summe sum(τ (a)) über ein Intervallattribut a mittels einer Transformationsfunktion τ : I −→ N bilden (z.B. τ (I) := begin(I) für ein Intervall I), so wäre zunächst über εLneues Attribut=τ (X) ein neues Attribut für τ einzuführen, über das die Summe gebildet werden kann (vgl. etwa die Beispiele in 5.3.2). In diesem Fall gilt beispielsweise für sum: ΓLA # b=sum(a) (X) = ΓA # b=sum∗ (b) (εLb=τ (X)) Gilt A∩L 6= ∅, ist also die Gruppierung von den Attributen in L abhängig, ist es unnötig ganz X durch unpackL (X) bis hin zu Einheitsintervallen aufzuspalten. Eine Zerlegung in maximale nicht-überlappende Teilintervalle mittels des split-Operators reicht aus. Die Mengen PX sind hierfür entsprechend zu wählen (siehe 6.1). Dadurch wird es möglich die Materialisierung einer ungepackten“ Gruppe wieder auf die Aggregationsfunktion ” zu verschieben: ΓLA # b1 =α1 ,...,bn =αn (X) = packL ( ΓA # b1 =α∗1 ,...,bn =α∗n (splitLPX (X)) ) 53 6.3. Verbesserungen für pack Beispiel: Die Frage nach dem Durchschnittspreises pro Zeiteinheit aus 5.3.2 führt zu für P = folgendem Ergebnis, welches die durchgeführte Zerlegung mittels splitZeit P {1, 8, 15, 21, 26} erkennen läßt: Zeit [1, 7] [8, 14] [15, 20] [21, 25] [26, 30] Durchschnitt 285 260 270 255 240 6.3. Verbesserungen für pack Nicht nur die Operatoren der erweiterten Relationenalgebra basieren auf unpack, sondern auch pack selbst (vgl. 3.3.3): packL (X) := packln (packln−1 (. . . packl1 (unpackL (X)) . . . ) für L = l1 , . . . , ln 6.3.1. pack für genau ein Attribut Enthält L nur genau ein Attribut, wird also pack für ein intervallwertiges Attribut l ∈ L angewandt, ist nach Definition 3.3.2 kein unpack nötig. Ähnlich dem Algorithmus zur Berechnung der erweiterten Differenz für ein Attribut in 6.2.6), läßt sich packl (X) in einem Durchlauf berechnen: Hierfür sei X nach L gruppiert und jede dieser Gruppen nach begin(l) aufsteigend sortiert. Für eine solche Gruppe G genügt es folgenden Algorithmus auszuführen (beginnend mit dem ersten Tupel): Für ein Tupel g ∈ G: Setze b, e ∈ N auf den Wert des l-Attributs: [b, e] := g(l). Solange e + 1 ≥ begin(g(l)) gilt: Setze e := end(g(l)) falls e < end(g(l)) ist. Fahre mit dem nächsten Tupel für g fort. 3. Füge ein Tupel x mit x|L := g|L und x(l) := [b, e] dem Ergebnis hinzu. Fahre mit dem nächsten Tupel für g fort. 1. 2. Beispiel: Ist eine Relation X mit den zwei Attributen Name und VT gegeben: Name VT Müller [3,7] Müller [3,3] X:= Müller [8,8] Müller [9,12] Schmidt [1,1] Schmidt [3,3] 54 6.3. Verbesserungen für pack Dann wäre der Ablauf des beschriebenen Algorithmus für packVT folgender: Schritt 1 2 2 2 3 1 3 1 3 Name VT Müller [3, 7] Müller [3, 3] Müller [8, 8] Müller [9, 12] Müller [9, 12] Schmidt [1, 1] Schmidt [3, 3] Schmidt [3, 3] Schmidt [3, 3] Ausgabe [b, e] – [3, 7] – [3, 7] – [3, 8] – [3, 12] (Müller, [3, 12]) [3, 12] – [1, 1] (Schmidt, [1, 1]) [1, 1] – [3, 3] (Schmidt, [3, 3]) [3, 3] Pipelining Das angegebene Verfahren für pack liefert das Ergebnis tupelweise zurück. Nachgeschaltete Operationen müssen also nicht auf die vollständige Materialisierung der Ergebnisrelation warten; umgekehrt benötigt pack keine vollständige Relation als Eingabe um ein Ergebnistupel zu liefern (Pipelining). Beispielsweise wird so bei Erweiterung (6.2.2) und Selektion (6.2.3) die Materialisierung von ungepackten Tupeln“ auf das gerade be” trachtete beschränkt. 6.3.2. pack für mehr als ein Attribut Enthält L mehr als ein intervallwertiges Attribut, dann ist packL (X) abhängig von der Reihenfolge der Attribute in L (vgl. 3.3.3). Da unpackL (X) jedoch nicht von dieser Reihenfolge abhängig ist und pack für ein einziges Attribut ohne unpack auskommt, läßt sich zunächst ein unpack eliminieren: packL (X) : = packln (packln−1 (. . . packl1 ( unpackL (X) ) . . . )) = packln (packln−1 (. . . packl1 ( unpackl1 (unpackL−{l1 } (X)) ) . . . )) = packln (packln−1 (. . . packl1 ( unpackL−{l1 } (X) ) . . . )) Für das erste Attribut l1 aus L wird nun kein unpack mehr benötigt. Wie bereits bei Differenz und Gruppierung und Aggregation kann das verbleibende unpackL−{l1 } durch splitL−{l1 } ersetzt werden: packln (packln−1 (. . . packl1 ( unpackL−{l1 } (X) ) . . . )) L−{l1 } = packln (packln−1 (. . . packl1 ( splitPX (X) ) . . . )) Nun wird die Ausgangsrelation X nur noch bis auf maximale nicht-überlappende Teilintervalle zerlegt, wenn die Punktmengen für splitL−{l1 } wie in 6.1 gewählt werden. 55 6.3. Verbesserungen für pack Beispiel: S# [3, 5] Für X = [2, 4] [2, 4] [2, 4] P# [1, 5] [1, 4] (vgl. das Beispiel zu 3.3.3) [5, 6] [6, 9] liefert die Anwendung von splitP# PX anhand der Punktmenge PX = {1, 5, 6, 7} S# [3, 5] [3, 5] P# splitPX (X) = [2, 4] [2, 4] [2, 4] [2, 4] P# [1, 4] [5, 5] [1, 4] . [5, 5] [6, 6] [7, 9] S# P# [2, 5] [1, 4] Ausgehend von diesem Ergebnis ist packS# (splitP# (X)) = [2, 5] [5, 5] PX [2, 4] [6, 6] [2, 4] [7, 9] S# P# und damit packP# (packS# (splitP# (X))) = [2, 5] [1, 5] = packS#, P# (X). PX [2, 4] [6, 9] Umgekehrt ergibt splitS# PX mit PX = {3, 4} S# [3, 4] [5, 5] [2, 2] splitS# (X) = [3, 4] P [2, 2] [3, 4] [2, 2] [3, 4] P# [1, 5] [1, 5] [1, 4] [1, 4] , [5, 6] [5, 6] [6, 9] [6, 9] S# P# [2, 2] [1, 9] packP# (splitS# PX (X)) = [3, 4] [1, 9] [5, 5] [1, 5] P#, S# und damit pack S# (X) = pack P# (pack 56 (splitS# PX (X))) S# P# = [2, 4] [1, 9] . [5, 5] [1, 5] 6.4. Verbesserungen für unpack 6.4. Verbesserungen für unpack Sollte eine Materialisierung von unpackL (X) explizit nötig sein, kann dies auf der Grundlage von packL (X) folgendermaßen geschehen: Für alle x ∈ packL (X): Für alle l ∈ L: Für alle n ∈ x(l): Füge ein Tupel t mit t|L := x|L und t(l) := [n, n] dem Ergebnis hinzu. 6.5. Verbesserungen für split Die für den split-Operator benötigte Punktmenge kann einfacher berechnet werden als in angegeben 6.1 ist. Ein Pipelining auf Tupelebene wie für pack bzw. unpack ist zwar nicht möglich, trotzdem läßt sich durch Partitionierung der Ausgangsrelation der Umfang einer Materialisierung von Zwischenergebnissen vermindern. 6.5.1. Berechnung der Punktmenge In 6.1 wurde die Menge PX eingeführt mit der durch splitlPX (X) eine Relation X in maximale nicht-überlappende l-Intervalle zerlegt werden kann: PX := { begin(x(l)) : x ∈ X ∧ ∃ x̃ ∈ X ∧ x(l) 6= x̃(l) ∧ begin(x(l)) ∈ x̃(l) } ∪ { end(x(l)) + 1 : x ∈ X ∧ ∃ x̃ ∈ X ∧ x(l) 6= x̃(l) ∧ end(x(l)) ∈ x̃(l) } Dies ist jedoch auch mit einer einfacher zu berechnenden Punktmenge möglich: [ { begin(x(l)), end(x(l)) + 1 } P̃X := x∈X P̃X ⊃ PX ist eine Obermenge von PX , hat also eventuell nicht mehr die minimalen Anzahl von Elementen. Allerdings erzeugt splitlP̃ (X) immer noch Intervalle größtmöglicher X Länge3 . Liegt P̃X aufsteigend sortiert vor: P̃ = {p1 , . . . , pm } mit pi < pi+1 für alle 1 ≤ i < m, dann wird durch splitlP̃ (X) ein Tupel x ∈ X durch Tupel xi ersetzt, die sich nur im X Attribut l von x unterscheiden: xi (l) = [pi , pi+1 − 1] sofern x(l) overlaps xi (l) gilt. 3 Ist p̃ ∈ P̃X − PX dann existiert kein x ∈ X mit begin(x(l)) < p̃ ≤ end(x(l)). p̃ spaltet also kein ” Tupel aus X“. 57 6.5. Verbesserungen für split Anders formuliert läßt sich splitlP̃ (X) mit einer Relation QX die ein einziges Attribut X l und die Tupel xi (l) = [pi , pi+1 − 1] enthält, durch einen Verbund darstellen (vgl. 6.2.4): n ρ l=ql (QX )) splitlP̃X (X) = σxl overlaps yl (ρ l=xl (X) o Die Berechnung von P̃X benötigt einen Durchlauf über alle l-Intervalle von X. Um diesen Aufwand zu sparen, wäre es denkbar P̃X in gewisser Form für jede Relation vorzuhalten – etwa in Gestalt der Relation QX . Sinnvoll wäre auch die Indexierung der Tupel einer Relation X anhand der Punkte aus PX . 6.5.2. Pipelining und Partitionierung Häufig ist es sinnvoll splitL (X) nur für einzelne Partitionen zu materialisieren und X so Stück für Stück“ zu bearbeiten. Dieses Pipelining auf Basis einer Partitionierung ” kann somit bei Differenz, pack und Gruppierung & Aggregation angewandt werden: Bei X −L Y und pack für mehrere Attribute ist eine Partitionierung nach L möglich, bei ΓLA#... (X) (siehe 6.2.8) eine Partitionierung nach L ∩ A. Außerdem werden die Tupel von X eventuell feiner zerlegt als nötig wäre, wenn die Menge der zerlegenden Punkte für ganz X gebildet wird. Werden sie für jede Partition einzeln berechnet, können insgesamt weniger Tupel entstehen. Allerdings ist abzuwägen ob die zusätzliche Erzeugung von Punktmengen den Mehraufwand lohnt. Beispiel für X −L Y : Sind X und Y wie im Beispiel zur Differenz in 6.2.4, dann gibt es verschiedene Werte für das Attribut ’Name’ etwa ’Müller’ und ’Schmidt’. Daher sind für X die Partitionen Name VT Name VT Müller [10, 15] X1 = und X2 = zu betrachten, sowie Müller [18, 20] Schmidt [21, 55] Müller [56, 70] Name VT Name VT für Y die Partitionen Y1 = Müller [10, 20] und Y2 = Schmidt [22, 32] . Müller [33, 44] Schmidt [45, 50] Bei ’Müller’ ist PX1 ∪Y1 = {10, 16, 18}, was keine Veränderung für X1 bedeutet und für Y1 die Zerlegungen folgende Zerlegung liefert: Name Müller VT splitPX ∪Y (Y1 ) = Müller 1 1 Müller Müller VT [10, 15] [16, 17] [18, 20] [33, 44] Für Y2 bedeutet die Zerlegung durch PX2 ∪Y2 = {22, 33, 45, 51} keine Veränderung. Für X2 gilt: 58 6.5. Verbesserungen für split Id Schmidt Schmidt splitVT PX2 ∪Y2 (X2 ) = Schmidt Schmidt Schmidt VT [21, 21] [22, 32] [33, 44] [45, 50] [51, 56] Wie angedeutet ist hier zu beobachten, dass durch die partitionsbasierte Zerlegung insgesamt weniger Tupel entstehen: 3 + 5 + 4 + 2 = 14 Tupel von X1 , X2 , Y1 und Y2 im Gegensatz zu 9 + 7 = 16 Tupeln von X und Y im Beispiel von 6.2.4 (’Schulze’ und ’Meier’ nicht mitgezählt). Pipelining für X −L Y Ein Pipelining für X −L Y kann etwa durch folgenden Algorithmus geschehen: Partitioniere X in X1 , X2 , . . . und Y entsprechend in Y1 , Y2 , . . . nach L. Für jedes Xi : Erzeuge für jedes l ∈ L die Punktmenge PXi ∪Yi über Xi und Yi . Wende damit splitlPX ∪Y auf Xi und Yi an. i i Füge packL (Xi − Yi ) dem Ergebnis hinzu. Das abschließende pack kann entfallen, falls L genau ein Attribut enthält und der Minuend X in gepackter“ Form vorliegt. Für mehrere Attribute kann pack für jedes Attribut ” einzeln erfolgen (siehe 6.3.1). Zusätzliche Partitionierung nach L Wie schon bei unpackL (X) ist bei splitL (X) die Reihenfolge in der split für die einzelnen Attribute in L ausgeführt wird unwichtig. Ist allerdings splitl (X) bereits für ein l ∈ L ausgeführt worden, kann in den hier beschriebenen Fällen X zusätzlich nach diesem l partitioniert werden. Hierdurch entstehen bei der Anwendung von split für das nächste Attribut aus L wieder weniger Tupel. Das so erzeugte Ergebnis ist allerdings abhängig von der Reihenfolge der Liste L. Beispiel für X −S#, P# Y : S# Sei X := [1, 70] P# und Y := [4, 16] S# [2, 15] [60, 68] P# [4, 9] . [5, 19] 0 Mit PX∪Y = {2, 16, 60, 69} ergibt splitS# PX∪Y (Y ) = Y =: Y und 59 6.5. Verbesserungen für split S# [1, 1] [2, 15] splitS# PX∪Y (X) = [16, 59] [60, 68] [69, 70] P# [4, 16] [4, 16] =: X 0 . [4, 16] [4, 16] [4, 16] Für S#=[2, 15] ergibt sich die Punktmenge {4, 10} und für S#=[60, 68] die Menge {5, 17}. Für alle anderen Werte von S# ist die Punktmenge gemäß 6.1 leer. Nach der Anwendung von splitP# auf die S#-Partitionen ergibt sich: S# [1, 1] [2, 15] [2, 15] X= [16, 59] [60, 68] [60, 68] [69, 70] P# [4, 16] S# [4, 9] [10, 16] [2, 15] und Y = [4, 16] [60, 68] [4, 4] [60, 68] [5, 16] [4, 16] P# [4, 9] . [5, 16] [17, 19] 0 Ohne zusätzliche Partitionierung nach S# enthielte splitP# PX∪Y (X ) mit der Punktmenge PX∪Y = {4, 5, 10, 17} 20 Tupel (statt 7): [4, 16] zerlegt in 4 Intervalle pro 5 Tupel. 0 splitP# PX∪Y (Y ) enthielte 5 Tupel (statt 3). 60 7. Realisierung In diesem Kapitel wird eine exemplarische Realisierung der erweiterten Relationenalgebra beschrieben. Es soll eine Anfragesprache an eine temporale Datenbank mit einem einzigen Zeitstempel implementiert werden. Grundlage hierfür bildet das RDBMS Oracle in Version 9.2 ([OTN], [OraDoc]). Da eine vollständige Realisierung der erweiterten Relationenalgebra innerhalb des DBMS aus den im folgenden genannten Gründen nicht möglich ist, besteht dieses Kapitel aus drei Teilen: Zunächst wird in 7.1 erläutert welche Erweiterungen im DBMS vorgenommen werden. In 7.2 wird die Uebersetzung von Termen der erweiterten Algebra für das DBMS beschrieben. Abschließend wird in 7.3 eine Implementierung der Relationenalgebra als Anfragesprache vorgestellt. 7.1. Erweiterung des DBMS In Oracle ist keine relationale Algebra an sich vorhanden. Stattdessen können in der Structured Query Language (SQL) Anfragen an Tabellen gestellt werden ([OraSQL]). Zusätzlich ist es möglich in einer prozeduralen Sprache (Procedural Language/SQL – PL/SQL) Prozeduren und Funktionen für SQL-Objekte zu implementieren ([OraPLSQL]). Die Erweiterung der SQL-Syntax im Sinne der erweiterten Relationenalgebra ist in Oracle nicht möglich. Daher findet die Auswertung eines Terms der Algebra außerhalb des DBMS statt (siehe 7.3). Eine Zuordung von relationalen Operatoren zu SQL-Konstrukten ist jedoch wie folgt durchführbar: Sei X eine Relation mit den Attributen x1 , x2 , . . . und X die entprechende SQL-Tabelle mit den Spalten x1, x2,.... Ebenso sei Y die entsprechende Tabelle zur Relation Y . relationaler Operator: [Identität von] X πx1 ,x2 (X) εb=β (X) ρb=x1 σϕ (X) Xo nY X −Y X ∪Y X ∩Y Γx1 ,x2 # b=α (X) SQL-Konstrukt: select * from X SELECT x1, x2 from X SELECT β as b, x1, x2,... from X SELECT x1 as b, x2, x3,... from X select * from X WHERE ϕ select * from X NATURAL JOIN Y select * from X MINUS Y select * from X UNION Y select * from X INTERSECT Y select α as b from X GROUP BY x1, x2 61 7.1. Erweiterung des DBMS SQL unterscheidet sich jedoch von den in 3.1 beschriebenen Ausgangsstrukturen: • Eine Relation enthält ein Tupel nur höchstens einmal; in einer SQL-Tabelle kann im Gegensatz dazu eine Zeile mehrfach vorkommen. Um dies auszugleichen kann zu jeder erzeugten SQL-Anfrage explizit eine Duplikatelimination verlangt werden (select DISTINCT). • In SQL existiert ein expliziter Wert für undefiniert“: null. Dieser Wert erfährt ” durch die erweiterte Relationenalgebra keine spezielle Behandlung – insbesondere ist der null-Wert als Zeitstempel unzulässig. Im folgenden wird davon ausgegangen, dass die bei Operatoren der erweiterten Relationenalgebra verwendete Zeitstempel-Spalte keine null-Werte enthält. Für sonstige Spalten (auch intervallwertige!) wird der null-Wert jedoch geduldet. • Es gibt keinen Datentyp für Wahrheitswerte (etwa boolean) in SQL. Wahr“ wird ” also entsprechend durch die Zahl 1 und falsch“ durch 0 dargestellt – etwa where ” ” I_overlaps(Zeit1, Zeit2) = 1“ statt where I_overlaps(Zeit1, Zeit2)“. ” 7.1.1. Intervalle und Intervalloperatoren Um Anfragen in der erweiterten Relationenalgebra nach SQL zu übersetzen, muss ein Intervallbegriff zur Modellierung von Zeitstempeln im DBMS vorhanden sein. Im Oracle RDBMS existieren bereits Datentypen für Intervalle (interval year to month und interval day to second) und sogar ein Zeitstempel (timestamp). Der Zeitstempel entspricht einer bis auf Sekundenbruchteile genauen Datumsangabe, also eher einem wie in Kapitel 2 beschriebenen Zeitpunkt mit festgelegter Semantik. Entsprechend modellieren interval year to month und interval day to second Zeitspannen einer bestimmten Granularität. Solch eine Zeitspanne hat keinen Anfangszeitpunkt. Implementierung eines Datentyps für Intervalle Als Grundlage für die hier beschriebene Erweiterung erscheinen diese Datentypen ungeeignet. Deshalb wird ein neuer Datentyp für Intervalle erzeugt, wobei natürliche Zahlen die zugrundeliegenden Zeitpunkte darstellen: create or replace type interval_type as object ( b integer, -- Anfangszeitpunkt e integer, -- Endzeitpunkt -static function min return integer, -- kleinstmögliches b static function max return integer, -- größtmögliches e+1 -constructor function interval_type (b integer, e integer) return self as result, 62 7.1. Erweiterung des DBMS -map member function convert return integer ); Die map-Funktion dient vall auf die ganze Zahl gilt ein Intervall I nun I.e < J.e gilt. dem DBMS zum Vergleich von Intervallen: Sie bildet ein Interb * (max - min) + e ab. Durch den Vergleich dieser Zahlen kleiner“ als ein Intervall J falls I.b < J.b oder zumindest ” Nun wäre es natürlich möglich Schnittstellen zwischen interval-type und den bereits vorhandenen Typen herzustellen: Zum Beispiel könnte ein Operator I_begin_as_date zu einem interval-type-Intervall die zugehörige timestamp-Datumsangabe des Anfangszeitpunktes b liefern. Implementierung der Intervalloperatoren Ausgehend vom Datentyp interval-type lassen sich gemäß 3.2 Operatoren für Intervalle in PL/SQL implementieren. Jedem Operatornamen wird hierbei ein ’I_’ vorangestellt um Konflikte mit SQL- oder PL/SQL-Schlüsselwörtern zu vermeiden. Wie schon angedeutet liefern Vergleichsoperatoren 0 für falsch“ und 1 für wahr“ zurück. ” ” Beispiele: create or replace function I_count (i interval_type) return integer is begin return i.e - i.b +1; end I_count; create or replace function I_overlaps (i1 interval_type, i2 interval_type) return integer is begin if (i1.b <= i2.e) and (i2.b <= i1.e) then return 1; end if; return 0; end I_overlaps; create or replace function I_intersect (i1 interval_type, i2 interval_type) return interval_type is b integer := i1.b; e integer := i1.e; begin if I_overlaps(i1, i2) = 1 then if i2.b > b then b := i2.b; end if; 63 7.1. Erweiterung des DBMS if i2.e < e then e := i2.e; end if; return interval_type(b,e); end if; raise_application_error(-20979, ’illegal interval-intersection: ’ ||I_toString(i1)||’ intersect ’||I_toString(i2)); end I_intersect; Alle implementierten Intervalloperatoren sind in Anhang A aufgelistet. 7.1.2. Implementierung von count∗ , sum∗ und avg∗ Die in 6.2.8 beschriebenen erweiterten Aggregationsfunktionen count∗ , sum∗ und avg∗ könnten als gewöhnliche Aggregationsfunktionen mit zusätzlichem Zeitstempelargument implementiert werden. Ist R die Tabelle aus 5.3.1, so könnte select Produkt, avg(Preis, Zeit) from R group by Produkt den nach der Zeit gewichteten Durchschnittspreis pro Produkt liefern. Da jeder Aggregationsfunktion in Oracle jedoch nur genau ein Argument erlaubt ist, müssen solche Anfragen entsprechend mit dem Intervalloperator I_count umformuliert werden. Im Beispiel für avg∗ : select Produkt, sum(Preis * I_count(Zeit)) / sum(I_count(Zeit)) from R group by Produkt Die Gewichtung nach einer Intervallspalte i muss also vom Benutzer selbst vorgenommen werden: count∗ wird so zu sum( I_count(i) ), sum∗ (a) zu sum(a * I_count(i)) und avg∗ (a) zu sum(a * I_count(i)) / sum(I_count(Zeit)). 7.1.3. Implementierung von pack Um pack zu implementieren sind verschiedene Ansätze denkbar. Zwei Möglichkeiten werden im folgenden näher erläutert, wobei das letztgenannte Verfahren bei der Implementierung zur Anwendung kommt. Mengenwertige Aggregationsfunktionen Da bei der Definition der erweiterten Relationenalgebra in Kapitel 3 Aggregationsfunktionen die Mengen zurückliefern eine wichtige Rolle spielen, ist es naheliegend zu Versuchen pack auch so zu implementieren. Zum Beispiel ist packZeit (R) für die Relation R aus 5.3.1, definiert als (vgl. 3.3): packZeit (R) := ΓProdukt, Preis # Produkt, Preis, Zeit=collapse(Zeit) Mit einer entsprechend implementierten Aggregationsfunktion ließe sich die Anfrage Zeit πProdukt, Zeit (R) wie folgt in SQL umsetzen: 64 7.1. Erweiterung des DBMS select Produkt, pack(Zeit) from R group by Produkt, Preis Hierbei wurde außer Acht gelassen, dass die von pack gelieferte Menge auch in der Ergebnisrelation entsprechend entschachtelt“ werden muss. Dies läßt sich jedoch mit ” vom DBMS bereitgestellten Mechanismen durchführen. Dieses Konzept scheitert an der unvollkommenen Unterstützung von Mengen als Rückgabewert von selbstdefinierten Aggregationsfunktionen. Enthält die Rückgabemenge mehr als etwa 50 Intervalle, so wird die Ausführung der Aggregationsfunktion mit einem internen Speicherfehler seitens Oracle abgebrochen. Es scheint, dass die internen Puffer für umfangreichere Ergebnisse von Aggregationsfunktion zu klein dimensioniert sind. Eine Variante von Aggregationsfunktionen in Oracle sind die Windowing Aggregate ” Functions“ (siehe [OraDW]). Eine solche Funktion aggregiert für jedes Tupel der Gruppe einen Wert bezüglich einer Umgebung ( Fenster“) dieses Tupels. Somit wäre es also ” möglich – ohne eine Menge als Rückgabewert – mehr als einen einzigen Wert pro Gruppe zurückzuliefern. pack läßt sich allerdings nicht so implementieren, da nicht festgestellt werden kann, ob noch Tupel aus der Gruppe folgen (was aber nötig ist – vgl. Algorithmus in 6.3.1). (Es war möglich dieses Problem durch zweimalige Ausführung der Windowing Aggregate Function zu umgehen. Dieser Ansatz wurde jedoch nicht weiter verfolgt, weil eine Implementierung mittels Pipelined Table Functions als vielversprechender erschien.) Pipelined Table Functions Das Oracle-DBMS bietet mit einer sogenannten Table Function die Möglichkeit eine PL/SQL-Funktion zu implementieren, die eine Tabelle zurückliefert (siehe [OraDCD]). Da in PL/SQL kein Datentyp für SQL-Tabellen existiert, muss allerdings der Rückgabewert einer solchen Table Function explizit umgewandelt werden (mit Hilfe des TABLEOperators). Als Eingabe benötigt eine Table Function für pack ebenfalls eine SQLTabelle. Hierzu wird eine Anfrage in einem CURSOR-Objekt gekapselt. Zeit Beispiel: Sei R die Tabelle aus 5.3.1. Dann läßt sich πProdukt, Zeit (R) wie folgt übersetzen: select * from TABLE( pack( CURSOR( select Produkt, Zeit from R order by Zeit ) ) ) Zunächst sind die verwendeten Datentypen festzulegen: create or replace package PACK_pkg as type rowrec is record ( -- Datentyp einer Zeile produkt varchar2(20), zeit interval_type ); type rowrec_table is table of rowrec; -- Datentyp der Rückgabe type refcur is ref cursor return rowrec; -- Datentyp der Eingabe end PACK_pkg; 65 7.1. Erweiterung des DBMS Nun kann pack entsprechend dem Algorithmus aus 6.3.1 implementiert werden: function pack (relation PACK_pkg.refcur) return PACK_pkg.rowrec_table pipelined is tupel PACK_pkg.rowtype; packed PACK_pkg.rowtype; new_group boolean; begin fetch relation into tupel; if relation%found then packed := tupel; loop fetch relation into tupel; exit when relation%notfound; new_group := case when not packed.produkt = tupel.produkt then true when packed.zeit.e +1 < tupel.zeit.b then true else false end; if new_group then pipe row (packed); packed := tupel; else if packed.zeit.e < tupel.zeit.e then packed.zeit.e := tupel.zeit.e; end if; end if; end loop; pipe row (packed); end if; close relation; return; end pack; Durch das Pipelining für nachfolgende Operatoren kann die Materialisierung der gesamten Ergebnistabelle vermieden werden (pipe row (packed)). Pipelining für vorangehende Operatoren (fetch relation into tupel) erlaubt den Umfang von Zwischenergebnissen zu minimieren (etwa bei unpack; siehe unten). Der pack-Operator wird jedoch nicht nur für eine konkrete Relation benötigt, sondern für Relationen allgemein. Das Schema solcher Relationen ist auch nicht im Voraus bekannt, weshalb die verwendeten Datentypen nicht wie im oben gezeigten Code ange- 66 7.1. Erweiterung des DBMS geben werden können. Die Implementierung eines allgemeinen pack-Operators benötigt also Datentypen, deren konkrete Struktur erst zur Laufzeit festgelegt werden muss. Oracle bietet solche Typen an: Der Datentyp SYS_refcursor kapselt beliebige Anfragen ( Weakly Typed Cursor“) und mit dem Datentyp SYS.AnyDataSet lassen sich ” beliebige Mengen modellieren (vgl. Transient and Generic Types“ in [OraDCD]). Eine ” allgemeine Implementierung der pack-Funktion könnte also etwa diese Form haben: function pack (relation SYS_refcursor) return table of SYS.AnyDataSet pipelined is tupel SYS.AnyDataSet; packed SYS.AnyDataSet; new_group boolean; begin fetch relation into tupel; [...] end pack; Leider scheint es nicht möglich, die Struktur der Tupel die einem SYS_refcursor entstammen, zur Laufzeit zu bestimmen. Das ist jedoch nötig um den pack-Algorithmus durchzuführen: Im obigen Code wird durch fetch relation into tupel versucht ein konkretes Tupel aus relation der Variablen tupel zuzuweisen. Dies schlägt zur Laufzeit fehl, da tupel nicht den richtigen konkreten Typ hat. Um trotzdem beliebige Relationen packen“ zu können, muss also vor einer SQL-Anfrage ” die passende konkrete PL/SQL-Funktion für pack erzeugt werden (ggfs. auch mehrere Funktionen). Das bedeutet, dass in jedem Schritt bei der Auswertung eines Terms der erweiterten Relationenalgebra alle Attribute inklusive deren Datentypen bekannt sein müssen. Ist dies der Fall, kann anhand dieser Information eine konkrete packImplementierung erzeugt werden: Der Code für pack liegt nicht konkret vor, sondern nur als Schablone (Template, Makro,. . . ). In diese wird der konkrete Text zu Attributnamen und Datentypen einer Relation eingesetzt. Der komplette konkrete Code wird dann vor der eigentlichen Anfrage an das DBMS übergeben. Beispiel: Aus new_group := case $case_clause when packed.$pack_arg.e +1 < tupel.$pack_arg.b then true else false end; Zeit wird im konkreten Fall für πProdukt, Zeit (R) (vgl. oben): new_group := case when not packed.produkt = tupel.produkt then true when packed.zeit.e +1 < tupel.zeit.b then true else false end; 67 7.1. Erweiterung des DBMS Da die Auswertung der erweiterten Relationenalgebra im Evaluator-Teil der Implementierung (7.3) geschieht, findet dort auch die beschriebene Textersetzung statt. 7.1.4. Implementierung von unpack Eine Realisierung von unpack wird für Selektion und Erweiterung benötigt (siehe 6.2.3 und 6.2.2). unpack kann analog zu pack als Table Function nach dem Algorithmus aus 6.4 implementiert werden. Dies ermöglicht ein Pipelining wie in 7.1.3 beschrieben. Da der Algorithmus für pack (und unpack) eine sortierte (bzw. partitionierte) Eingabe erfordert (6.3.1), muss entweder unpack diese Sortierung liefern, oder das (gesamte!) Ergebnis von unpack erneut sortiert werden. Für die Selektion σϕZeitstempel ist ersteres möglich, weil lediglich Tupel bezüglich ϕ herausgefiltert werden. Bei der Erweiterung εZeitstempel ist das für allgemeines β nicht möglich, denn die Reib=β henfolge der Werte für b ist nicht bekannt. Um zusätzlich nach b zu sortieren kann es also notwendig sein, das gesamte ungepackte“ Ergebnis zu materialisieren! Denkbar wä” re pack, die Erweiterung und unpack als eine einzige Table Function zu implementieren um den Algorithmus aus 6.2.2 zu realisieren, wobei ggfs. β genauer festgelegt werden müsste. Dieser Ansatz wurde hier allerdings aus Zeitgründen nicht implementiert. 7.1.5. Implementierung von split Für split muss zunächst die Menge der zerlegenden Punkte gebildet werden. Ist R eine Tabelle entsprechend der Beispielrelation aus 5.3.1, dann kann dies folgendermaßen geschehen: select rownum as n, p from (select alias.Zeit.b as p from R alias UNION select alias.Zeit.e+1 as p from R alias) order by p asc; Die Zeilen der Ergebnistabelle entsprechen den p1 , . . . , pm aus 6.5.1, wobei die Spalte n den Index repräsentiert. (Ohne alias für R erkennt Oracle die Spalte Zeit nicht als intervallwertige Spalte.) Die Tabelle Q gemäß 6.5.1 wird entsprechend gebildet: select interval_type(B.p, E.p-1) as Zeit from (select rownum as n, p from (select alias.Zeit.b as p from R alias UNION select alias.Zeit.e+1 as p from R alias) order by p asc) B, (select rownum as n, p from (select alias.Zeit.b as p from R alias UNION 68 7.2. Übersetzung von Termen select alias.Zeit.e+1 as p from R alias) order by p asc) E where E.n = B.n+1; Schließlich ergibt sich split(R) aus einem Verbund mit Q: select Produkt, Preis, I_intersect(R.Zeit, Q.Zeit) as Zeit from R, Q where I_overlaps(R.Zeit, Q.Zeit) = 1; Die Verwendung einer Pipelined Table Function für split erscheint unsinnig, da für die Bildung der Punktmenge bzw. von Q die gesamte Ausgangsrelation zunächst komplett durchlaufen werden muss. Deshalb wird R explizit materialisiert, d.h. als SQL-Tabelle erzeugt (create table ... as (select ...)). Genauso wird split(R) gebildet und ebenfalls explizit materialisiert. Um eine allgemeine Form des split-Operators zu erhalten, wird analog zu pack eine Textersetzung für alle von der konkreten Relation abhängigen Teile des Verfahrens durchgeführt. Bemerkung: Eine Implementierung von split anhand beliebiger Punktmengen wäre nach diesem Verfahren zwar auch möglich, wird aber im weiteren nicht benötigt. Der einzige Fall bei dem split überhaupt benötigt wird, ist die Gruppierung nach dem Zeitstempel. 7.1.6. Temporale Metadaten Für die Auswertung von Termen der erweiterten Relationenalgebra müssen die Namen und Datentypen aller Attribute von jeder Relation in der Datenbank bekannt sein (vgl. 7.1.3). Im Oracle-DBMS ist diese Information in Form einer Metadatentabelle (USER_TAB_COLUMNS; siehe [OraDD]) vorhanden. Zusätzlich ist es sinnvoll zu wissen ob eine Relation gepackt“ vorliegt. Für manche ” Operatoren der erweiterten Relationenalgebra kann dann das abschließende pack entfallen (siehe Kapitel 6). Diese wird in einer entsprechenden Metadatentabelle namens USER_TEMPORAL_TABLES festgehalten: create table USER_TEMPORAL_TABLES ( table_name varchar2(30) not null, packed_on varchar2(30) not null ) 7.2. Übersetzung von Termen Die Operatoren der erweiterten Relationenalgebra können auf Basis von pack, split und anderen Table Functions in eine einzige SQL-Anfrage übersetzt werden. Wie in 7.1 69 7.2. Übersetzung von Termen beschrieben, müssen hierfür ggfs. vorher PL/SQL-Funktionen erzeugt und Relationen explizit materialisiert werden. Für jeden erweiterten Operator wird im folgenden das zugehörige Übersetzungsmuster angegeben. Es sei (o.B.d.A) X eine Relation mit den Attributen z, x1 und x2 und X die entprechende SQL-Tabelle mit den Spalten z, x1 und x2. Zu einer Relation Y sei Y die entsprechende SQL-Tabelle. z (bzw. z) sei der gemeinsame Zeitstempel von X und Y . 7.2.1. Projektion Wie schon im Beispiel für pack (7.1.3) angedeutet, läßt sich etwa πxz 1 ,x2 ,z (X) folgendermaßen implementieren: select * from TABLE( pack( CURSOR( SELECT x1, x2, z from X order by x1, x2, z ))) Das abschließende pack kann entfallen, wenn kein Zeitstempel nach der Projektion mehr übrig ist, etwa bei πxz 1 ,x2 (X): SELECT x1, x2 from X 7.2.2. Erweiterung Für die Erweiterung εzb=β (X) wird wie in 6.2.2 beschrieben unpack benötigt. unpack kann analog zu pack als Pipelined Table Funktion implementiert werden (siehe 7.1.4). Die entsprechende Anfrage lautet: select * from TABLE( pack( CURSOR SELECT β as b, x1, x2 from TABLE( unpack( CURSOR ( select * from X order by x1, x2, z ))) order by x1, x2, b, z ))) 7.2.3. Selektion Analog zur Erweiterung (siehe oben; vgl. 6.2.3) wird eine Table Function für unpack benutzt. Die Materialisierung einer ungepackten“ Relation kann hier durch Pipelining ” vermieden werden (vgl. 7.1.4). Die erweiterte Selektion σϕz (X) kann so formuliert werden: select * from TABLE( pack( CURSOR select * from TABLE( unpack( CURSOR ( select * from X order by x1, x2, z ))) WHERE ϕ ))) 70 7.2. Übersetzung von Termen 7.2.4. Verbund Der erweiterte Verbund wird nach dem in 6.2.4 beschriebenen Verfahren erzeugt. Hat etwa Y die drei Spalten z, x1 und y1 , dann wird der erweiterte Verbund X o nz Y durch folgende Anfrage gebildet: select x1, x2, y1, I_intersect(X.z, Y.z) as z from X JOIN Y ON (I_overlaps(X.z, Y.z) = 1 AND X.x1 = Y.x1) 7.2.5. Differenz Der in 6.2.6 angegebene Algorithmus kann wie pack ebenfalls als Table Function implementiert werden. Haben X und Y das gleiche Schema, dann wird die Differenz X −z Y folgendermaßen ausgedrückt: select * from TABLE( minus ( CURSOR ( select * from X LEFT OUTER JOIN Y ON ( (Y.z is null OR I_overlaps(X.z, Y.z) = 1) AND X.x1 = Y.x1 AND X.x2 = Y.x2 ) order by x1, x2, X.z, Y.z ))) 7.2.6. Vereinigung Die Vereinigung X ∪z Y benötigt lediglich ein abschließendes pack: select * from TABLE( pack ( CURSOR ( select * from X UNION Y ))) 7.2.7. Durchschnitt Haben X und Y das gleiche Schema, dann ist der Durchschnitt X ∩z Y ein Sonderfall des Verbunds (siehe oben). Deshalb wird hierfür keine eigene Übersetzung benötigt. 7.2.8. Gruppierung und Aggregation Wird nicht nach dem Zeitstempel gruppiert, ist beispielsweise für Γzx1 ,x2 # z=α (X) höchstens ein pack nötig (vgl. 6.2.8). select * from TABLE( pack ( CURSOR ( select α∗ as z from X GROUP BY x1, x2 order by x1, x2, z ))) Die erweiterten Aggregationsfunktionen α∗ sind wie in 7.1.2 beschrieben vom Benutzer entsprechend zu formulieren. Enthält die Ergebnisrelation keinen Zeitstempel, entfällt auch das abschließende pack. Für Γzx1 ,x2 # b=α (X) sind keine Table Functions mehr notwendig: 71 7.3. Implementierung des Evaluators select α∗ as b from X GROUP BY x1, x2 Wird jedoch nach z gruppiert, so ist für Γzz,x1 # b=α (X) die Anwendung von split notwendig. Sei also Xsplitted die durch das in 7.1.5 beschriebene Verfahren materialisierte Relation. Damit ergibt sich die Anfrage: select α∗ as b from Xsplitted GROUP BY x1, x2 Zum Abschluß wäre pack notwendig, wenn die Ergebnisrelation den Zeitstempel enthielte (vgl. oben). 7.3. Implementierung des Evaluators Um Anfragen auf Grundlage der erweiterten Relationenalgebra an ein temporales Datenbanksystem stellen zu können, wurde ein Programm namens Evaluator“ entwickelt. ” Der Evaluator1 wertet Ausdrücke einer abgeleiteten Anfragesprache aus indem SQL- und PL/SQL-Anweisungen erzeugt werden. Auf Wunsch werden diese vom DBMS ausgeführt und das Ergebnis dargestellt. Hier wird zunächst die Grammatik der zugrundeliegenden Anfragesprache vorgestellt und im Anschluß eine Übersicht über die Struktur des Programms gegeben. 7.3.1. Implementierung des Evaluators Um einen Term in der erweiterten Relationenalgebra auswerten zu können, wurde eine Anfragesprache auf Grundlage der Algebra entwickelt. Die implementierte Grammatik dieser Sprache ist in Anhang B in der Erweiterte Backus-Naur-Form (EBNF; [Wir77]) aufgeführt. Jedem relationalen Operator wird ein Schlüsselwort zugeordnet (etwa project für π). Für alle Operatoren bis auf die Umbenennung (rename – ρ) existiert eine temporale Variante mit vorangestelltem t“. Das Zeitstempelattribut kann über ein dem Schlüsselwort ” nachfolgendes ^[Attributname]“ angegeben werden (z.B. tproject^Zeit Produkt, ” Zeit (R)). Fehlt die explizite Angabe eines Zeitstempels, dann wird das intervallwertige Attribut benutzt, sofern dies eindeutig ist (tproject Produkt, Zeit (R) für R aus 5.3.1). Um Aggregationen, Erweiterungen und Selektionsbedingungen angeben zu können werden diese in geschweifte Klammern eingeschlossen. Der Text zwischen den Klammern wird ohne Weiterverarbeitung in die SQL-Anfrage eingebaut. Geschlossene geschweifte Klammern müssen quotiert werden: }“ → \}“. Zum Beispiel entspricht dem Term ” ” ΓZeit Produkt # b = sum∗ (Preis) (R) der Ausdruck 1 Man könnte den Evaluator als Übersetzer“ bezeichnen, weil ein Term der erweiterten Relationenal” gebra nach SQL (und PL/SQL) übersetzt wird. Da jedoch im Rahmen des Evaluators diese Übersetzung (auf Wunsch) vom DBMS interpretiert und das Ergebnis (also der Wert der Terms) dargestellt wird, scheint die Bezeichnung Interpreter“ auch berechtigt zu sein. ” 72 7.3. Implementierung des Evaluators tgroup by Produkt aggregate number { sum(Preis * I_count(Zeit)) } as b (R) Da Projektion, Umbenennung und Erweiterung alle auf select abgebildet werden, wurde project in der Anfragesprache erweitert: Umbenennung und Erweiterung sind jetzt auch in der Projektion möglich, z.B.: project Produkt, Preis, Zeit as VT (R) = rename Zeit as VT (R) tproject number {Preis * 10} as Preis10, Zeit (R) project entspricht also mehr einer SQL-select-Anweisung, als der in 3.1.2 definierten Projektion. Umbenennung und Erweiterung können nun als Spezialfälle der Projektion implementiert werden. Bei Verbund, Vereinigung, Differenz und Durchschnitt ist es möglich mehr als zwei Relationen anzugeben. Die Auswertung erfolgt dabei von links nach rechts, es gilt also minus(X,Y,Z) = minus(minus(X,Y), Z). (Die Auswertungsreihenfolge ist natürlich nur für die Differenz wichtig, denn Verbund, Vereinigung und Durchschnitt sind assoziative Operatoren.) Gross- und Kleinschreibung spielt für die Auswertung der Anfragesprache keine Rolle. Mehrere aufeinanderfolgende Leerzeichen2 werden zu einem einzigen Leerzeichen zusammengefasst. Die angegebene Grammatik ist eine kontextfreie LL(1)-Grammatik. Die Erkennung von Ausdrücken der Anfragesprache kann deshalb über die Methode des rekursiven Abstiegs erfolgen ( top-down“; vgl. z.B. [Wir96a]). ” 7.3.2. Ablauf der Anfrageauswertung Für die Benutzung des Programms gibt es zwei Möglichkeiten: Als Kommandozeilenparameter kann ein Ausdruck der Anfragesprache oder eine Datei übergeben werden. Der übergebene Ausdruck bzw. die Ausdrücke in der Datei werden dann übersetzt und die Übersetzung ausgegeben. Ohne Parameter startet der Evaluator in einem interaktiven Modus. Ähnlich zu sqlplus von Oracle können hier Ausdrücke direkt ausgewertet werden. Im interaktiven Modus oder in einer Datei ist jeder Ausdruck mit einem Semikolon ( ;“) abzuschliessen. ” Es ergibt sich der in Abbildung 7.1 skizzierte Ablauf: Zunächst werden die Metadaten (vgl. 7.1.6) der temporalen Datenbank ermittelt. Dann kann eine Anfrage in eine SQL-Anfrage übersetzt werden, wobei eventuell vorher PL/SQL-Funktionen erzeugt und Relationen explizit materialisiert werden müssen. Der gesamte Code wird dann entweder ausgegeben oder an das DBMS zur Auswertung übermittelt. Im letzten Fall kann das Ergebnis dieser Auswertung im interaktiven Modus dargestellt werden. 2 genauer: Whitespace Characters, also auch Tabulatoren und Zeilenwechselzeichen 73 7.3. Implementierung des Evaluators Zeit (Tabellennamen, Spaltennamen, Datentypen, ...) Metadaten Anfrage Evaluator Erweiterung Intervalle Übersetzung: Intervalloperatoren SQL (und PL/SQL) Ergebnis oder Tabellen und Metadaten Ergebnis Oracle DBMS Übersetzung Abbildung 7.1.: Ablauf der Anfrageauswertung 7.3.3. Struktur des Evaluators Der Evaluator ist als textbasiertes Programm in Perl realisiert ([WCS96a], [CPAN], [PerlDoc]). Die Struktur des Evaluators ist in 7.2 dargestellt. Das ausführbare Programm hat den Namen evaluator.pl. Je nach Aufruf wird die Benutzerschnittstelle (ui.pm) im interaktiven Modus gestartet oder zur Erzeugung einer lesbaren Übersetzung benutzt. Über das Modul db.pm werden Datenbankzugriffe abgewickelt. Die Erkennung und Übersetzung eines Ausdrucks geschieht im parser-Modul: Für jeden Operator der erweiterten Relationenalgebra sind Behandlungsroutinen vorhanden, die im handler-Modul zusammengefasst sind. Diese Routinen nutzen die Schablonen für pack, unpack, split und minus (vgl. 7.1.3), welche durch das Modul templates gekapselt werden. evaluator.pl ui.pm parser.pm db.pm temporal project restrict join union ... handler.pm regular project restrict join union ... pack split templates.pm unpack minus Abbildung 7.2.: Struktur des Evaluators 74 8. Ausblick For an indefinite time I clung to the machine as it swayed and vibrated, quite unheeding how I went, and when I brought myself to look at the dials again I was amazed to find where I had arrived. (H.G. Wells, The Time Machine) Mit dieser Arbeit wurde ein Konzept zur relationalen Modellierung zeitabhängiger Daten geschaffen. Die Definition der erweiterten Relationenalgebra erlaubt die Formulierung von zeitlichen Zusammenhängen im relationalen Kontext. Durch die Implementierung einer Anfragesprache für temporale Datenbanken auf Grundlage eines bestehenden DBMS konnte die Realisierbarkeit eines temporalen DBMS belegt werden. Von diesem Standpunkt aus zeichnen sich weitere Möglichkeiten ab: Änderungsoperationen Einfügen, Löschen und Ändern läßt sich folgendermaßen mit der erweiterten Relationenalgebra ausdrücken: Sei R eine Relation und t ein Tupel passend zum Schema von R. Weiterhin sei T eine Relation deren Inhalt nur aus dem Tupel t besteht. • Einfügen von t in R (tinsert^[Zeitstempel]): R ∪Zeitstempel T • Löschen von t aus R (tdelete^[Zeitstempel]): R −Zeitstempel T • Verändern von t in R (tupdate^[Zeitstempel]): löschen und verändert einfügen Eine Verbesserung kann erreicht werden, indem diese Änderungsoperationen durch bereits bestehende insert-, update- und delete-Befehle implementiert werden statt durch Vereinigung und Differenz (siehe [DDL2003a] Anhang A). Zusätzlich kann die Überwachung von zeitlichen Integritätsbedingungen an diese Änderungsoperationen gekoppelt werden ( Trigger“; siehe auch [GL95c]). ” Vollständige Integration in ein bestehendes DBMS Um ein temporales DBMS zu schaffen, ist eine Implementierung der erweiterten Algebra in ein bestehendes DBMS notwendig. Die Funktionalität des Evaluators müßte dazu vollständig in das DBMS integriert werden. Wegen der erläuterten Probleme mit allgemeinen pack und split Operatoren war dies hier nicht zu realisieren. Kombination mit räumlichen Datenmodellen Eine Integration von zeitlichen Konzepten in räumliche Modelle ist häufig wünschenswert. In einem raumzeitlichen Datenmodell können beispielsweise bewegte Objekte dargestellt werden (vgl. z.B. [KPS2003b] oder [Kle2003b]). 75 8. Ausblick Anfrageoptimierungen Anfragen an temporale Datenbanken bieten ein großes Optimierungspotential. Zusätzlich zur Optimierung der klassischen relationalen Operatoren gibt es Verbesserungsmöglichkeiten für Anfragen mit zeitlichem Bezug. Algebraische Optimierungen Die Verschiebung von Operatoren innerhalb einer Anfrage kann durch die hier implementierte SQL-Übersetzung beeinträchtigt werden: Eine Verschiebung über einen splitOperator hinweg ist nicht möglich, weil hier ein Zwischenergebnis explizit materialisert wird. Theoretisch bilden Pipelined Table Functions keine solche Hürde. Praktisch wird die Eingaberelation einer Table Function in einem Cursor-Objekt gekapselt. Es ist anzunehmen, dass keine Verschiebung von Operatoren über diese Grenze hinweg stattfindet. Eine Verschiebung folgender Art wäre dann nicht mehr möglich: restrict {Bedingung} ( tminus(X, Y) ) => tminus( restrict {Bedingung} (X), restrict {Bedingung} (Y) ) Eine Implementierung der Operatoren ohne diese Probleme würde eine schnellere Ausführung temporaler Anfragen bedeuten. Indexe Die Unterstützung der zeitlichen Dimension durch eigene Indexe wurde in dieser Arbeit nicht betrachtet. Eine sortierte Speicherung temporaler Relationen verspricht bereits einen Geschwindigkeitsgewinn, weil die durch Table Functions realisierten Algorithmen stets eine Sortierung nach dem Zeitstempel (und eine Partitionierung nach den restlichen Attributen) verlangen. Zur Indexierung könnten räumliche Indexe (R-, R∗ -, RSS-Bäume, . . . ) benutzt werden (vgl. [Loc2001a], [Sch2003c], [Kle2003b]): Die benötigte zweite Dimension wäre in bitemporalen Datenbanken durch den zweiten Zeitstempel gegeben. Sonst kann ein thematisches Attribut oder ein Wert ohne konkrete Semantik als zweite Dimension dienen (etwa ein festes Intervall oder nochmal der Zeitstempelwert). Anhand der Intervallgrenzen der Zeitstempel einer Relation kann auch ein B∗ -Baum aufgebaut werden (siehe [TCG93a] Kapitel 18). Dies entspricht der Vorhaltung der für split benötigten Punktmenge in einer Baumstruktur. 76 A. Intervalle und Intervalloperatoren Deklaration des Intervalldatentyps: create or replace type interval_type as object ( b integer, -- Anfangszeitpunkt e integer, -- Endzeitpunkt -static function min return integer, -- kleinstmögliches b static function max return integer, -- größtmögliches e+1 -constructor function interval_type (b integer, e integer) return self as result, -map member function convert return integer ); Name und Signatur jedes implementierten Intervalloperators: -- Operatoren für ein einziges Intervall: I_begin I_end I_count I_toString I_contains (i (i (i (i (n interval_type) return integer interval_type) return integer interval_type) return integer interval_type) return string integer, i interval_type) return integer -- Vergleichsoperatoren: -- Signatur "(i1 interval_type, i2 interval_type) return integer" I_included_in I_includes I_before I_begins I_meets I_included_in_neq I_includes_neq I_after I_ends I_overlaps I_merges -- intervallwertige Operatoren: -- Signatur "(i1 interval_type, i2 interval_type) return interval_type" I_union I_intersect I_minus 77 B. Grammatik der Anfragesprache Der aus der erweiterten Relationenalgebra entwickelten Anfragesprache liegt folgende Grammatik zugrunde: B.1. Terminalsymbole B.1.1. Schlüsselwörter rename as [t]project [t]extend [t]restrict [t]group by aggregate [t]join [t]minus [t]union [t]intersect B.1.2. Bezeichner und Werte table column datatype anything Mit table sind alle Tabellennamen des zugrundeliegenden Datenbankschemas gemeint. column soll eine Spalte bezeichnen und datatype einen Datentyp. anything ist eine beliebige Zeichenfolge, wobei geschlossene geschweifte Klammern zu quotieren sind: }“ → \}“. Dies entspricht den α, β, ϕ in Aggregation, Erweiterung und Selektion (vgl. ” ” 7.2). B.1.3. Sonderzeichen , ^ ( ) { } B.2. Grammatik term = listop "(" term_list ")" | monop "(" term ")" | table term_list = term { "," term } B.2.1. monop monop = project | rename | extend | restrict | group rename = "rename" rename_arg 78 B. Grammatik der Anfragesprache project extend restrict group = = = = ( ( ( ( "tproject" "textend" "trestrict" "group" pack_on pack_on pack_on pack_on | | | | "project" ) "extend" ) "restrict") "group" ) project_arg extend_arg restrict_arg group_arg B.2.2. monop-Argumente rename_arg project_arg extend_arg group_arg restrict_arg = = = = = renaming { "," projection { "," extension { "," "by" column_list pass_through renaming } projection } extension } "aggregate" extension projection renaming extension = column | renaming | extension = column " " naming = datatype " " pass_through " " naming B.2.3. listop listop = ( "t" listop_token pack_on | listop_token ) "(" term_list ")" listop_token = ( "join" | "minus" | "union" | "intersect" ) B.2.4. sonstiges pass_through = "{" anything "}" column_list = column { "," column } naming = ( "as" | " " ) column pack_on = [ "^" column ] 79 Literaturverzeichnis [All83a] J. F. Allen. Maintaining Knowledge about Temporal Intervals. Communications of the ACM, 26(11):832–843, 1983. [Bei2001a] F. Beier. Objekt-relationale Realisierung einer temporalen Datenbanksprache. Diplomarbeit, Institut für Informatik, Universität Hannover, Hannover, 2001. [BJW2000a] C. Bettini, S. Jajodia, S. X. Wang. Time Granularities in Databases, Data Mining, and Temporal Reasoning. Springer-Verlag, Berlin, 2000. [CPAN] Comprehensive Perl Archive Network, 2004. org/. URL http://www.cpan. [Dat2000a] C. Date. An Introduction to Database Systems, Seventh Edition. AddisonWesley, Reading, MA, 7 Auflage, 2000. [DD2003] C. Date, H. Darwen. An Overview and Analysis of TSQL2, 2003. URL http://www.thethirdmanifesto.com/. [DDL2003a] C. Date, H. Darwen, N. A. Lorentzos. Temporal Data and the Relational Model. Morgan Kaufmann, San Francisco, 2003. [DB2000a] A. Descartes, T. Bunce. Programming the Perl DBI. Database Programming with Perl. O’Reilly & Associates, Sabastopol, CA, 2000. [EJS98a] O. Etzion, S. Jajodia, S. Sripada (Herausgeber). Temporal Databases: Research and Practice. LNCS 1399. Springer-Verlag, Berlin, 1998. [Fag79] R. Fagin. Normal forms and relational database operators. In Proceedings of the 1979 ACM SIGMOD International Conference on Management of Data, Seiten 153–160, 1979. [GL95c] M. Gertz, U. W. Lipeck. “Temporal” Integrity Constraints in Temporal Databases. In J. Clifford, A. Tuzhilin (Herausgeber), Recent Advances in Temporal Databases – Proceedings of the International Workshop on Temporal Databases, Sept. 1995, Workshops in Computing, Seiten 77–92. Springer-Verlag, Berlin, 1995. 80 Literaturverzeichnis [Kle2003b] C. Kleiner. Modeling Spatial, Temporal and Spatio-Temporal Data in Object-Relational Database Systems. Doktorarbeit, Hannover, 2003. [KPS2003b] M. Koubarakis, B. Pernici, H. Schek, M. Scholl, B. Theodoulidis, N. Tryfona, T. Sellis, A. Frank. Spatio-Temporal Databases – The CHOROCHRONOS Approach. Springer Verlag, Berlin, 2003. [Kuh97a] B. I. Kuhn. Entwicklung einer Anfragesprache für temporale Datenbanken: Semantik, Ausdrucksfähigkeit und graphische Benutzerschnittstelle. Diplomarbeit, Hannover, 1997. [Loc2001a] U. Löckmann. Erweiterung eines objektrelationalen Datenbankmanagementsystems um verallgemeinerte Suchbäume als Indexstrukturen. Diplomarbeit, Institut für Informatik, Universität Hannover, Hannover, 2001. [OraAD] Oracle Database Application Developer’s Guide – Fundamentals, 2004. [OraDCD] Oracle Data Cartridge Developer’s Guide, 2004. [OraDD] Oracle Database Catalog Views and Data Dictionary, 2004. [OraDoc] Oracle Database 10g Release 1 (10.1) Documentation Library, 2004. URL http://otn.oracle.com/pls/db10g/db10g.homepage. [OraDW] Oracle Database Data Warehousing Guide, 2004. [OraPLSQL] Oracle PL/SQL User’s Guide and Reference, 2004. [OraSQL] Oracle Database SQL Reference, 2004. [OTN] Oracle Technology Network, 2004. URL http://otn.oracle.com/. [PerlDoc] Online Perl Documentation, 2004. perl5.8.4/pod/perl.html. [Rei99a] C. Reinhard. Realisierung einer temporalen Erweiterung von SQL auf einem objekt-relationalen Datenbankmanagementsystem. Diplomarbeit, Hannover, 1999. [Sch2003c] O. Schweer. Erweiterung eines objektrelationalen Datenbankmanagementsystems um räumliche Verbunde. Diplomarbeit, Institut für Informationssysteme, Universität Hannover, Hannover, 2003. [Sno95a] R. T. Snodgrass (Herausgeber). The TSQL2 Temporal Query Language. The Kluwer Int. Series in Engineering and Computer Science. Kluwer Academic Publishers, Boston, 1995. 81 URL http://www.perldoc.com/ Literaturverzeichnis [TCG93a] A. Tansel, J. Clifford, S. Gadia, S. Jajodia, A. Segev, R. Snodgrass (Herausgeber). Temporal Databases: Theory, Design, and Implementation. Database Systems and Applications Series. Benjamin/Cummings, Redwood City, CA, 1993. [WCS96a] L. Wall, T. Christiansen, R. L. Schwartz. Programming Perl (2nd Edition). O’Reilly & Associates, Cambridge, 2 Auflage, 1996. [Wel95] H. Wells. The Time Machine, 1895. [Wir77] N. Wirth. What can we do about the unnecessary diversity of notation for syntactic definitions? Communications of the ACM, 20(11):822–823, 1977. [Wir96a] N. Wirth. Grundlagen und Techniken des Compilerbaus. Addison-Wesley (Deutschland), Bonn, 1996. [ZCF97a] C. Zaniolo, S. Ceri, C. Faloutsos, R. Snodgrass, V. Subrahmanian, R. Zicari. Advanced Database Systems. The Morgan Kaufmann Series in Data Management. Morgan Kaufmann Publishers, San Francisco, 1997. 82 Erklärung Hiermit versichere ich, dass ich die vorliegende Arbeit und die zugehörige Implementierung selbständig verfasst und dabei nur die angegebenen Quellen und Hilfsmittel verwendet habe. Hannover, 27. Juli 2004 Christian Stahlhut 83