1 Structured Query Language - SQL Andreas Schmidt 1.1 Übersicht Ende der 60er Jahre entwickelte E.F. Codd bei IBM das relationale Datenmodell [Cod70]. Es ist streng formal aufgebaut, basierend auf der Relationenalgebra und dem Relationenkalkül. Das Modell besticht sowohl durch seine Einfachheit, als auch durch seine Exaktheit. Codd beschreibt in seinem Modell sowohl die strukturellen Eigenschaften, Integritätsaspekte als auch eine Reihe von Operationen auf den Daten. Ein Großteil der sich heute produktiv im Einsatz befindlichen Datenbanksysteme basieren auf diesem Modell. Bekannte Vertreter sind etwa Oracle, der Microsoft SQL-Server, sowie die Open Source Produkte MySQL und PostgreSQL. SQL stellt die Anfrage und Datendefinitionssprache für relationale Datenbanksysteme dar. SQL ist deskriptiv, d.h. der Anwender formuliert welche Informationen er haben möchte, nicht jedoch, wie er an diese Informationen kommt. SQL ist mengenorientiert. • • • 1.2 Grundlegende Konzepte des Relationenmodells Bevor auf SQL als Anfrage- und Datendefinitionssprache für relationale Datenbanken eingegangen wird, werden im Folgenden einige elementare Kenntnisse des Relationenmodells vermittelt, die für das spätere Verständnis von SQL hilfreich sind. Eine ausführliche und detailierte Einführung in das Modell findet sich in [SKS01]. Die Einführung soll anhand der beiden in Abbildung 1.1 dargestellten Relationen country und city erfolgen. Abb. 1.1: Struktur relationaler Datenbanken 2 1 SQL 1.2.1 Struktur Zentrale Komponente in Codds Modell ist die Relation . Eine Relation besteht aus einer Anzahl von Tupeln, welche die Datensätze repräsentieren. Die Struktur einer Relation (und somit auch der in ihr enthaltenen Tupel) wird durch das Relationenschema vorgegeben. Ein Relationenschema besitzt einen eindeutigen Namen und wird durch eine Menge von Attributen beschrieben, die jeweils einen bestimmten Wertebereich (Domäne) besitzen. Es beschreibt somit eine Struktur, vergleichbar einem Struct in C/C++ oder einem Record in Pascal. Dabei ist aber zu beachten, dass die Werte der Attribute atomar sein müssen, d.h. keine mengenwertigen oder strukturierten Daten repräsentieren dürfen. Die Gesamtheit aller Relationenschemata bildet dann das Datenbankschemata. Anschaulich wird eine Relation als Tabelle dargestellt. Die Attribute des Relationenschemas werden zu den Spaltenbezeichnern, die Zeilen der Tabelle repräsentieren die Tupel und die Werte in den Tabellenfeldern entsprechen denen der Wertebereiche ihrer Attribute. Bei der Abbildung von Relationen auf Tabellen sind jedoch zwei Punkte zu beachten, die aus der Tatsache herrühren, dass es sich bei einer Relation um eine mathematische Menge von Tupeln handelt: 1. In einer Menge gibt es keine Reihenfolge, das heißt die Reihenfolge der Datensätze (Zeilen) in den Tabellen ist willkürlich. 2. Mengen erlauben keine Duplikate, d.h. es darf keine zwei Datensätze in einer Tabelle geben, die identisch sind. 1.2.2 Integrität Das Relationenmodell kennt weiterhin den Begriff des Primärschlüssels. Dabei handelt es sich um ein oder mehrere Attribute eines Relationenschemas, so dass es in dieser Relation keine zwei Tupel geben kann, die für dieses Attribut oder Attributkombination identische Attributwerte besitzen. Mittels des Primärschlüssels ist es somit möglich, einzelne Tupel zu identifizieren. Dieser Sachverhalt wird im Relationenmodell dazu genutzt um Beziehungen zwischen Datensätzen herzustellen. In Abbildung 1.1 sind die Attribute, die den Primärschlüssel bilden unterstrichen. Da es in einer Menge von Datensätzen keine Duplikate gibt ist gewährleistet, dass es für jede Relation einen Primärschlüssel gibt. Spätestens durch die Hinzunahme aller Attribute eines Relationenschemas lässt sich ein Primärschlüssel konstruieren. Aufbauend auf dem Primärschlüssel existiert das Konzept des Fremdschlüssels mit dem sich Beziehungen zwischen Datensätzen modellieren lassen. Ein Fremdschlüssel ist ein Attribut oder eine Attributkombination, die auf den Primärschlüssel einer Relation1 verweist. Dabei muss gesichert sein, dass der Wert des Fremdschlüssels immer auf ein existierendes Tupel verweist, d.h. dass der Attributwert oder die Attributwertkombination des Fremdschlüssels identisch mit einem existierenden Primärschlüsselwert ist. Dieser Sachverhalt wir als referentiellen Integrität eines Datenbanksystems bezeichnet. In Abbildung 1.1 sind zwei Fremdschlüsselbeziehungen zu sehen. Zum einen besitzt die Relation country einen Fremdschlüssel, bestehend aus den Attributen code, capital 1 dabei kann es sich um eine beliebige andere- oder aber auch um die selbe Relation handeln. 1.3 Organisation der Sprache 3 und province, welcher die Hauptstadt des Landes referenziert. Beispielhaft ist dies am Datensatz der Schweiz dargestellt. Zum zweiten besitzt die Relation city den Fremdschlüssel country, welcher das Land identifiziert in dem sich die Stadt befindet. Dies ist beispielhaft anhand der Städte Frankreichs angedeutet. Weitere mögliche Integritätsbedingungen sind die Einschränkung des Wertebereichs für einzelne Attribute, das Erzwingen von Einträgen für bestimmte Attribute und das Erzwingen der Eindeutigkeit von Attributwerten innerhalb einer Relation. 1.2.3 Operationen Codd formuliert eine Reihe von Operationen auf den Relationen. Im einzelnen handelt es sich dabei um Restriktion, Projektion, Kartesisches Produkt, Join, Vereinigung, Durchschnitt und Differenz. All diese Operationen besitzen eine oder mehrere Relationen als Eingabe und liefern eine Relation als Ausgabe zurück [Dat05]. Dies bedeutet insbesondere, dass die Ausgabe einer Operation, stets wieder als Eingabe einer anderen Operation dienen kann, was die Formulierung geschachtelter relationaler Ausdrücke erlaubt. In Abschnitt 1.6.2 wird die Umsetzung obiger Operationen auf SQL-Statements beschrieben. 1.3 Organisation der Sprache SQL hat inzwischen eine Reihe von Standardisierungsrunden durchlaufen. Das American National Standards Institute (ANSI) setzte 1986 den ersten Standard (SQL1 oder SQL-86). Weitere Standardisierungen fanden 1989 und 1992 (bekannt als SQL2 bzw. SQL-92) statt, sowie zuletzt die als SQL3 (SQL-99) bekannt gewordene Aktualisierung des Standards im Jahr 1999. SQL ist weiterhin auch von der International Standard Organisation (ISO) anerkannt. Im SQL2 Standard werden drei Anweisungsklassen definiert, die auch die Grundlage für die Strukturierung in diesem Kapitel bilden. • Datenmanipulationssprache (Data Manipulation Language, DML): Diese Klasse enthält Sprachelemente zur Bearbeitung von Daten. Operationen sind das Einfügen (insert), Löschen (delete), Modifizieren (update) und Abfragen (select) von Daten. Die zur DML gehörenden Operatoren werden zur Laufzeit einer Datenbankanwendung eingesetzt, um auf die Daten in den zuvor mittels der DDL (s.u.) angelegten Tabellen zuzugreifen. • Datendefinitionssprache (Data Definition Language, DDL): Die Sprachelemente in dieser Klasse erlauben das Anlegen und Modifizieren der Datenstrukturen. Typische Vertreter sind die Sprachelemente create table, alter table, etc. Die Befehle dieser Klasse werden hauptsächlich zur Entwicklungszeit einer Datenbankanwendung eingesetzt. Sie erlauben die Definition der Datenstrukturen und Tabellen, auf die dann zur Laufzeit mittels den Befehlen der DML lesend und schreibend zugegriffen wird. 4 • 1 SQL Datensteuerungssprache (Data Control Language, DCL): Die Sprachelemente in dieser Klasse erlauben das Anlegen und Modifizieren von Benutzern und Rollen sowie die Vergabe von Zugriffs- und Systemrechten. Typische Vertreter sind die Befehle create user zum Anlegen von neuen Benutzern sowie grant und revoke mit denen bestimmte Berechtigungen vergeben und wieder entzogen werden können. 1.4 Datentypen In Abschnitt 1.2.1 wurde gesagt, das jede Spalte einer Relation einen bestimmten Wertebereich hat. Diese Wertebereiche werden in SQL durch Datentypen festgelegt. Unabhängig vom konkreten Datenbanksystem2 lassen sich die Datentypen in folgende Kategorien untergliedern: • Zeichenketten: Erlauben das Speichern von beliebigen Kombinationen von Zeichen. Es wird zwischen variabel langen Datentypen (varchar(<size>), CLOB3 ) und Datentypen mit fester Länge (char(<size>)) unterschieden. Bei Datentypen mit fester Länge wird die Zeichenkette am Ende mit Leerzeichen aufgefüllt, bei Datentypen variabler Länge wird nach dem letzten Zeichen abgeschnitten. Zum Teil kann eine Obergrenze, wie viel Zeichen maximal gespeichert werden dürfen, angegeben werden, teils sind die Grenzen fest vorgegeben. Weiterhin gibt es Varianten obiger Typen die mit verschiedenen nationalen Zeichensätzen umgehen können (z.B. nvarchar<size>). • Numerisch: Es können sowohl ganze Zahlen als auch Fließkommazahlen gespeichert werden. Die Typen unterscheiden sich in ihrer Genauigkeit und Stellenzahl. Beim Datentyp float kann bei der Definition beispielsweise die Anzahl der Stellen vor und nach dem Komma mit angegeben werden. Vertreter numerischer Datentypen sind integer, smallint, numeric, float, real und double precision. • Zeitlich: Es gibt Datentypen zum Speichern von Datum (Date) und Uhrzeit (Time), mit und ohne Zeitzone. Weiterhin gibt es den Datentyp Interval, der eine Zeitdauer aufnimmt. • Binär: Der Datentyp BLOB4 speichert beliebige binäre Zeichenketten. Die Größe liegt dabei meist im Gigabyte Bereich. • Boolean: Datentyp zur Aufnahme der Werte TRUE und FALSE. • Bit-String: Datentyp zur Aufnahme binärer Daten, mit fester oder variabler Länge. Es gibt den speziellen Wert NULL für alle Datentypen. Der Wert NULL für ein Attribut sagt aus, dass für dieses Attribut kein Wert bekannt ist. Er ist auf keinen Fall mit dem Wert 0 für numerische Typen oder dem leeren String für Zeichenketten zu verwechseln. Eine Besonderheit ist, dass der Wert NULL von sich selbst verschieden ist, dh. es gilt NULL 6= NULL5 . 2 aus historischen Gründen unterscheiden sich die Datentypen der Hersteller zum Teil. Character Large Object 4 Binary Large Object 5 Dies bedeutet insbesondere auch, dass die Bedingung A = A nur für Attributwerte ungleich NULL wahr ist. 3 1.5 Syntax 5 Wie bereits erwähnt, findet man die hier beschrieben Datentypen nicht unbedingt so in den konkreten Datenbanksystemen wieder, sondern diese weichen von der Namensgebung und/oder der Semantik oft etwas vom Standard ab. Eine Abbildung der im SQL3 Standard vorgeschlagenen Datentypen auf konkreten Datentypen einer Reihe populärer Datenbanksysteme findet sich in [KK01]. In SQL3 wurde weiterhin festgelegt, dass sich aus den vorgegebenen Datentypen eigene (auch strukturierte) Datentypen ableiten lassen. Diese können dann ebenso wie die vom System vorgegebenen Datentypen verwendet werden. Auf diesen Punkt soll hier jedoch nicht weiter eingegangen werden, der interessierte Leser sei auf [Tür03] verwiesen. 1.5 Syntax Die Syntax von SQL ist relativ einfach. Die Befehle der DML, DDL und DCL beginnen stets mit einem oder mehreren Schlüsselwörtern, welche den konkreten Befehl spezifizieren. Es können beliebige Einrückungen und Umbrüche innerhalb eines Befehls getätigt werden. Werden mehrere Befehle hintereinander geschrieben, so werden die einzelnen Befehle mit einen Strichpunkt (;) beendet. 1.5.1 Groß-/Kleinschreibung Schlüsselwörter können beliebig groß- oder kleingeschrieben werden. Genauso verhält es sich mit Bezeichnern für Tabellen6, Indexe, Attributen, etc. 1.5.2 Literale Werte für Zeichenketten und Datumsangaben werden in einfache Hochkommas (’), numerische Literale werden ohne Hochkommas geschrieben. Beispiel: insert into city (name, country, province, population) values (’Karlsruhe’, ’D’, ’Baden Württemberg’, 277011) 1.5.3 Kommentare SQL kennt zwei Arten von Kommentaren . Zum einen gibt es die aus vielen anderen Sprachen bekannten Kommentarklammern /*. . . */, die Kommentare über mehrere Zeilen enthalten können. Beispiel: /* : Smiff * Autor * Version : 1.22 */ 6 Bei MySQL spielt die Groß-/Kleinschreibung z.T. eine Rolle, da die einzelnen Tabellen auf Dateien mit dem Namen der Tabelle abgebildet werden. Unterscheidet das zugrunde liegende Betriebssystemen zwischen Groß- und Kleinschreibung, so muss die Schreibweise konsistent sein. 6 1 SQL Weiterhin gibt es noch Kommentare, die nur bis ans Ende der aktuellen Zeile reichen. In SQL werden diese durch zwei hintereinander geschriebene Minuszeichen eingeleitet. Beispiel: -- ab hier kommt die Definition der Foreign Keys 1.6 Data Manipulation Language Im folgenden Abschnitt soll nun die Data Manipulation Language (DML) von SQL vorgestellt werden, d.h. der Teil, der sich mit Anfragen und Modifikationen an bestehenden Datenbanken beschäftigt. Die DML stellt die Umsetzung der Operationen der Relationenalgebra (Abschnitt 1.2.3) dar. Die verschiedenen Konzepte werden zuerst kurz abstrakt dargelegt und dann anschließend an einem oder mehreren Beispielen verdeutlicht. Die Beispiele setzen alle auf der Mondial-Datenbank [Mon99] auf, die unter der angegebenen URL für Trainings- und Lehrzwecke frei verfügbar ist. Bevor nun im Folgenden mit der Einführung in die DML begonnen wird, werden zuerst noch die im Rahmen der Beispiele benutzten Tabellen aus der Mondial-Datenbank vorgestellt. 1.6.1 Mondial Datenbank Die gesamte Mondial-Datenbank umfasst ca. 25 Tabellen, von denen hier aber nur wenige benötigt werden. In Abbildung 1.2 ist ein Ausschnitt des Abhängigkeitsschema der Mondial-Datenbank dargestellt. Die Kästen repräsentieren die Tabellen mit ihren Attributen. Bei unterstrichenen Attributen handelt es sich um Primärschlüssel, grau unterlegte Attribute stellen Fremdschlüssel dar. Abb. 1.2: Auszug aus Abhängigkeitsschema der Mondial-Datenbank 1.6 Data Manipulation Language 7 In der oberen Hälfte der Abbildung sieht man die Tabellen province, country und city und ihre Beziehungen zueinander. Der Primärschlüssel der Tabelle city besteht aus den drei Attributen name, province und country. Teile des Primärschlüssels (province, country) bilden weiterhin einen Fremdschlüssel auf die Provinz, in der sich die Stadt befindet. Dadurch wird eine 1 : nBeziehung7 zwischen einer Provinz und den darin liegenden Städten realisiert. Die übrigen Attribute geben die Einwohnerzahl (population) und die Geo-Koordinaten der Stadt (longitude, latitude) an. Der Primärschlüssel der Tabelle province besteht aus den Attributen name und country. country wiederum ist ein Fremdschlüssel auf das Schlüsselattribut code der Tabelle country und stellt den Bezug zwischen einer Provinz und dem zugehörigen Land her. Tabelle country besitzt neben dem Primärschlüssel code einen dreiteiligen Fremdschlüssel (code, capital, province), der auf den entsprechenden Hauptstadt-Datensatz in der Tabelle city verweist. Hierbei handelt es sich um die Umsetzung einer 1 : 1Beziehung8 zwischen einem Land und der zugehörigen Hauptstadt. In der unteren Hälfte des Abhängigkeitsschemas sieht man die beiden Tabellen river und geo_river. Tabelle river benutzt den Namen des Flusses (Attribut name) als Primärschlüssel. Die nächsten 3 Attribute (river, lake, sea) sind jeweils Fremdschlüssel und geben an, ob der Fluss in einen anderen Fluss (river), einen See (lake) oder in ein Meer (sea) mündet. Die Beziehung zwischen Provinzen und Flüssen wird über die Tabelle geo_river hergestellt, die hier ausschnittsweise wiedergegeben wird: River Country Province ... ... ... Donau RO Braila Donau RO Tulcea Donau D Baden Württemberg Donau A Lower Austria Drin AL Albania Elbe D Niedersachsen Elbe D Hamburg Fulda D Hessen Gambia WAG Gambia Gambia SN Tambacounda ... ... ... 447 Datensätze Hierbei handelt es sich um eine so genannte Beziehungstabelle. Beziehungstabellen werden in relationalen Datenbanken zur Modellierung von n : m- Beziehungen eingesetzt. 7 Jede Stadt gehört zu genau einer Provinz, umgekehrt kann eine Provinz mehrere Städte haben. Dass eine Stadt maximal Hauptstadt eines Landes sein kann, wird durch die Formulierung eines unique-Constraints (siehe Abschnitt 1.7.1) erreicht. 8 8 1 SQL Während sich 1 : 1- und 1 : n-Beziehungen, wie zuvor gesehen, problemlos durch die Hinzunahme eines Fremdschlüssels9 modellieren lassen, wird bei n : m-Beziehungen eine zusätzliche Tabelle benötigt. Eine Beziehungstabelle besteht aus mindestens zwei Fremdschlüsseln, die auf die in Beziehung stehenden Tabellen verweisen. Konkret besteht die Tabelle geo_river aus einem Fremdschlüssel auf den betreffenden Fluss (Attribut river) und einem Fremdschlüssel auf die Provinz (Attribute province, country) die der Fluss durchfließt. Somit ist es möglich, dass ein Fluss mehrere Provinzen durchfließen kann, umgekehrt kann eine Provinz auch von mehreren Flüssen durchflossen werden. Ein Auszug aus Tabelle geo_river ist in nachfolgender Tabelle dargestellt. Im Folgenden sollen nun anhand obiger Tabellen die DML Befehle vorgestellt werden. Hinweis. Es wird empfohlen, die Beispiele an einem realen System nachzuvollziehen und mit ihnen etwas zu “spielen”. Dazu gibt es auf der Webseite [Smi05] einen web basierten Zugang zu einer Mondial-Datenbank, einschließlich einer Sammlung aller im Folgenden vorgestellten Statements10 . Damit lassen sich die Anfragen sofort am Datenbestand nachvollziehen und variieren, ohne lokal ein Datenbanksystem installieren zu müssen. 1.6.2 Select-Statement Als erstes soll nun im Folgenden das select-Statement eingeführt werden. Es ist für die Extraktion von Informationen aus der Datenbank zuständig. Das einfachste select-Statement ist die Ausgabe aller Daten einer Tabelle. Dazu wird in der select-Klausel mittels dem Wildcardzeichen * signalisiert, dass alle Spalten ausgegeben werden sollen. In der from-Klausel wird die Tabelle angegeben. Das Ergebnis sind dann alle Datensätze, die sich in der betreffenden Tabelle befinden. Name ... River Lake ... ... Mekong Orinoco select * from river Werra Weser Fulda Weser ... South China Sea 4500 Atlantic Ocean 2736 218 Pacific Ocean ... Length 292 Columbia River ... Sea ... ... ... 1952 ... 132 Datensätze Projektion. Sollen nicht alle Spalten einer Tabelle ausgegeben werden, so kann man in der select-Klausel die Attribute festlegen, die ausgegeben werden sollen. 9 der Fremdschlüssel kommt in die Tabelle, deren Datensätze jeweils mit maximal einem anderen Datensatz in Beziehung stehen. 10 nur select-Statement, Datenmanipulationen sind nicht erlaubt. 9 1.6 Data Manipulation Language select name, capital from country name capital Albania Tirane Greece Athens Macedonia Skopje ... ... 195 Datensätze Sortierung. Soll die Ausgabe sortiert werden, so wird die order-Klausel verwendet, welche ans Ende des select-Statements angefügt wird. Die Klausel nimmt eine Liste von Spalten entgegen, nach denen dann sortiert werden soll. Wird mehr als ein Attribut angegeben, so erfolgt die Sortierung der Priorität nach absteigend von links nach rechts. Es können entweder Namen der Attribute nach denen sortiert werden soll angegeben werden oder es können Spaltennummern (beginnend bei 1) angegeben werden, die sich auf die Position der Attribute in der select-Klausel beziehen. Standardmäßig wird aufsteigend (asc) sortiert, soll absteigend sortiert werden so ist (desc) hinter dem Attribut bzw. der Spaltennummer anzugeben. Im folgenden Beispiel werden der Name und die Länge der Flüsse aus Tabelle river absteigend sortiert nach der Länge der Flüsse ausgegeben. select name, length from river order by length desc name length Amazonas 6518 Jangtse-Kiang 6100 Parana 4700 Mekong 4500 ... ... 132 Datensätze Bedingungen. Bisher wurden immer alle Datensätze einer Tabelle ausgegeben. Will man jedoch nur Datensätze, die eine bestimmte Bedingung erfüllen ausgeben, so benötigt man die where-Klausel, welche die Formulierung von Bedingungen ermöglicht. Bedingungen werden auf den Attributen formuliert und können mit not, and, or zu komplexeren Bedingungen zusammengesetzt werden11. Zusätzlich können Klammern eingesetzt werden, so dass sich beliebige Ausdrücke formulieren lassen. Als Operatoren stehen unter anderem die “üblichen Verdächtigen” (=, <>12 , <, <=, >, >=) zur Verfügung. Der Vergleich erfolgt entweder mit einem Literal oder einem anderen Attribut. Im folgenden Beispiel werden alle Flüsse ausgegeben, die in den Atlantischen Ozean (Literal ’Atlantic Ocean’) münden. 11 12 not hat dabei die höchste Bindungspriorität, gefolgt von and und or 6= 10 1 SQL name select from where order name, sea, length river sea=’Atlantic Ocean’ by name sea length Amazonas Atlantic Ocean 6518 Connecticut River Atlantic Ocean 660 Cuanza Atlantic Ocean Cunene Atlantic Ocean ... ... ... 19 Datensätze Eine weitere Möglichkeit ist die Formulierung von Bedingungen mittels Mustervergleich. Dazu gibt es in SQL die Operatoren like und not like . Die beiden Sonderzeichen ’%’ und ’_’ stellen dabei so genannte Wildcards dar. ’%’ steht für eine beliebige Anzahl von Zeichen, ’_’ steht für exakt ein Zeichen. Die Bedingung stadtname like ’%burg’ liefert beispielsweise alle Städte zurück, die auf “burg” enden, so z.B. Hamburg, Salzburg, Sankt Petersburg. Neuere Versionen einzelner Datenbanksysteme erlauben zudem die Suche mittels regulären Ausdrücken [Fri00], einer mächtigen Sprache zur Formulierung von Textmustern. Diese Funktionalität ist jedoch nicht standardisiert und soll hier auch nicht weiter vertieft werden. Weiterhin gibt es noch den Operator between , der zur Bereichsüberprüfung eingesetzt werden kann. Beispiel: einwohner between 1000 and 20000 gibt alle Datensätze zurück, deren Einwohnerzahl zwischen 1000 und 20000 Einwohner liegt. Um ein Attribut gegenüber eine Menge von Werten zu testen kann man entweder mehrere Bedingungen mittels or verbinden oder den in-Operator benutzen. Beispiel: code in (’F’,’ES’,’I’,’GR’) gibt alle Länder zurück, deren code entweder ’F’, ’ES’, ’I’ oder ’GR’ ist. Der in-Operator wird uns später auch noch bei den Subqueries (Seite 14) begegnen, wo statt der Liste von Literalen ein vollständiges select-Statement stehen kann. Umgang mit NULL. Eine besondere Bedeutung spielt, wie in Abschnitt 1.4 bereits angesprochen, der Attributwert NULL. Da NULL auch von sich selbst verschieden ist, führt die Formulierung folgender Bedingung immer zu FALSE: attributname = NULL Aus diesem Grund stellt SQL zwei spezielle Operatoren zur Verfügung: • is null : testet ob der Wert eines Attributs NULL ist. • is not null : testet ob der Wert eines Attributs ungleich NULL ist. Das folgende Beispiel wählt alle Flüsse aus, die in einen See münden und länger als 1000 Kilometer sind. 11 1.6 Data Manipulation Language name select from where and name,lake,length river lake is not null length > 1000 lake length Kura Caspian Sea 1364 Rhein Bodensee 1320 Volga Caspian Sea 3531 Amudarja Ozero Aral 1415 ... ... ... 6 Datensätze Duplikatseliminierung. Je nach Anfrage ist es möglich, dass identische Zeilen im Ergebnis erscheinen. Wünscht man dies nicht, so kann dies durch Angabe des Schlüsselwortes distinct innerhalb der select-Klausel unterbunden werden. So führt die nahe liegende Lösung auf die Frage nach “allen Flüssen die Zuflüsse besitzen” nicht zum gewünschten Ergebnis, da viele Flüsse mehrere Zuflüsse haben und somit in der Ergebnismenge mehrfach auftreten. river Amudarja select from where order river river river is not null by 1 Amur Amur Busira ... 57 Datensätze Erst durch die Hinzunahme von distinct erscheint das erwünschte Ergebnis. river Amudarja select from where order distinct river river river is not null by 1 Amur Busira Dalaelv ... 27 Datensätze Spaltenaliase. Sollen die Überschriften der Spalten verändert werden, so werden Spaltenaliase eingesetzt Spaltenaliase werden in der select-Klausel hinter der jeweiligen Spalte und dem Schlüsselwort as angegeben. Das folgende Beispiel zeigt die Daten der Tabelle river mit deutschen Spaltenüberschriften. Der zweite Spaltenalias bestehen aus mehr als einem Bezeichner und muss deshalb in doppelten Anführungszeichen (") stehen. 12 1 SQL Flussname select name as Flussname, round(length/1.609) as "Länge (Meilen)" from river where length is not null order by 2 desc Länge (Meilen) Amazonas 4051 Jangtse-Kiang 3791 Parana 2921 Mekong 2797 ... ... 90 Datensätze Man sieht weiterhin, dass es hiermit auch möglich ist, berechnete Spalten (Umrechnung des Wertes der Spalte length von Kilometer nach Meilen)13 einen sinnvollen Namen zu geben. Die order-Klausel gibt schließlich an, dass die Ausgabe absteigend nach der 2. Spalte sortiert erfolgen soll. Aggregatsfunktionen. SQL stellt eine Reihe von Funktionen zur Verfügung, welche als Eingabe eine Tupelmenge (Menge von Datensätzen oder Menge von Einzelwerten) erwarten, als Ausgabe jedoch nur einen einzigen Wert zurückliefern. Im Unterschied zu normalen Funktionen (siehe Tabelle 1.6.2) arbeiten diese Funktionen somit tupel übergreifend und werden deshalb als Aggregatsfunktionen bezeichnet. SQL kennt folgende Aggregatsfunktionen: • count(<Attributbezeichner>) bzw. count(*) : Zählt die Anzahl der Elemente in der übergebenen Menge und liefert deren Anzahl zurück. Es können entweder komplette Datensätze (symbolisiert durch *) oder einzelne Attribute (Angabe des Attributnamens) an die Funktion übergeben werden. Im Fall, dass count(...) mit einem Attributnamen aufgerufen wird ist wichtig zu wissen, dass nur Nicht-NULL Werte gezählt werden. Sollen keine Duplikate gezählt werden, so kann mit count(distinct ...) vor dem Zählen eine Duplikatseleminierung durchgeführt werden. • sum(<Attributbezeichner>) : Summiert die einzelnen Werte der übergebenen Zahlenmenge auf. Das Attribut muss numerisch sein. • avg(<Attributbezeichner>) : Liefert den Durchschnitt der einzelnen Werte der übergebenen Zahlenmenge. Das Attribut muss numerisch sein. • stddev(<Attributbezeichner>)14 : Liefert die Standardabweichung der einzelnen Werte der übergebenen Zahlenmenge. Das Attribut muss numerisch sein. • min(<Attributbezeichner>) : Liefert den kleinsten Wert der einzelnen Werte der übergebenen Wertemenge. • max(<Attributbezeichner>) : Liefert den größten Wert der einzelnen Werte der übergebenen Wertemenge. Das folgende Beispiel ermittelt die Länge des längsten Flusses: 13 Die Grundrechenarten (+, −, ∗, /) sowie die Funktion round(...) sind in SQL standardmäßig verfügbar. Eine Übersicht über weitere Funktionen findet sich in Tabelle 1.1 auf Seite 21. 14 Beim MS SQL-Server heißt diese Funktion stdev 13 1.6 Data Manipulation Language Länge select max(length) as Länge from river 6518 1 Datensatz Gruppierungen. So wie die Aggregatsfunktionen im obigen Beispiel angewandt wurde, liefert sie für ein select-Statement genau einen Datensatz zurück. Indem wir die Datensätze zuvor gruppieren, kann die Aggregatsfunktion auf jede Gruppe einzeln angewandt werden. Um eine Gruppierung durchzuführen, benötigt man die group by-Klausel. Der Klausel folgt eine Liste von Attributen, nach denen gruppiert werden soll. Die Gruppierung erfolgt dann derart, dass alle Datensätzen, deren Werte bei den in der group byKlausel spezifizierten Attributen übereinstimmen, zusammengefasst werden. Das folgende Beispiel in Abbildung 1.3 zählt für jeden Fluss in der Datenbank, wieviele Zuflüsse er hat. Dazu werden in einem ersten (internen15) Schritt, alle Datensätze deren Werte in der Spalte river übereinstimmen in eine Gruppe zusammengefasst (Mitte). Anschließend wird dann für jede Gruppe gezählt, wieviele Datensätze in ihr existieren und diese Information, absteigend sortiert nach der Anzahl der gruppierten Datensätze sowie anschlisSSend den Namen der Quellflüsse, ausgegeben (Rechts). Abb. 1.3: Gruppieren nach Flüssen mit Aggregation 15 die mittlere Ausgabe dient lediglich der Verdeutlichung der Gruppierung, wird aber nicht ausgegeben 14 1 SQL Bedingungen auf Aggregaten. Als nächstes sollen jetzt auf den Aggregaten Bedingungen formuliert werden. So sollen beispielsweise nur die Flüsse ausgegeben werden, die drei oder mehr Zuflüsse besitzen. Schaut man sich das obige SQL-Statement an, so erkennt man, dass dies nicht in der where-Klausel passieren kann, da zum Zeitpunkt der Auswertung der where-Klausel das Aggregat noch nicht errechnet ist, sondern hier nur entschieden wird, welche Datensätze überhaupt der Gruppierung zugeführt werden (nur solche die in der Spalte river einen Wert stehen haben). Aus diesem Grund existiert die having-Klausel, welche es dann in einem nachgeschalteten Schritt erlaubt, Bedingungen auf den Ergebnissen von Aggregatsfunktionen zu formulieren. Im konkreten Beispiel sind das die Flüsse, für die die Spalte count(*) einen Wert größer zwei liefert, d.h. der Fluss drei oder mehr Zuflüsse hat. river select river, count(*) as "Anzahl Zuflüsse" from river where river is not null group by river having count(*) > 2 order by 2 desc Anzahl Zuflüsse Zaire 7 Ob 4 Nile 3 Kwa 3 White Nile 3 Zambezi 3 6 Datensätze Das Statement gleicht dem zuvor mit dem einzigen Unterschied, dass in der neu hinzugekommenen having-Klausel eine Bedingung formuliert wurde, in der eine Aggregatsfunktion vorkommt. Subquery. Mittels einer Subquery können mehrere SQL-Statements kombiniert werden. Dazu wird in der where-Klausel einfach statt eines Literals ein in Klammer stehendes select-Statement eingesetzt. So kann die Frage nach allen Flüssen, die eine überdurchschnittliche Länge haben, wie folgt beantwortet werden: name length Amazonas 6518 Jangtse-Kiang 6100 Parana 4700 Mekong 4500 ... ... 34 Datensätze Zuerst wird hierbei das Ergebnis des inneren, grau unterlegten, select-Statement ermittelt. Anschließend wird dann die äußere Anfrage mit dem Ergebnis der inneren Anfrage ausgeführt. Bei der Benutzung der Operatoren <, >, =, <> ist es notwendig, dass das innere Statement genau einen Datensatz zurückliefert. Dies ist oft dann der Fall, wenn eine 1.6 Data Manipulation Language 15 Bedingung auf dem Primärschlüssel formuliert wird, bzw. wenn wie im Beispiel eine Aggregatsfunktion eingesetzt wird. Eine andere Möglichkeit besteht darin, ein Attribut gegenüber einer Menge von Werten zu vergleichen. Dazu wird der auf Seite 10 eingeführte in-Operator benutzt. Statt der konstanten Literale wird einfach ein select-Statement in die Klammer geschrieben. Das folgende Beispiel gibt alle Ländernamen aus, die von der Donau durchflossen werden. Dazu werden in einem ersten Schritt innerhalb der grau unterlegten Subquery alle Ländercodes extrahiert, durch welche die Donau entsprechend den Informationen aus der Tabelle geo_river (siehe Seite 7) fließt. Das äußere Statement nimmt diese Menge entgegen und gibt dann für alle Länder deren code sich in der von der Subquery ermittelten Menge befinden, den Landesnamen (name) aus. name Austria Bulgaria Germany Hungary ... 7 Datensätze Im obigen Beispiel konnte die innere Abfrage unabhängig von der äußeren Abfrage ausgeführt werden, man spricht in diesem Fall von einer unkorrelierten Subquery. Es ist jedoch auch möglich, die innere Abfrage mit der Äußeren zu verzahnen. Dies wird dadurch realisiert, indem in der where-Klausel der inneren Anfrage auf ein Attribut der äußeren Anfrage zugegriffen wird. In diesem Fall kann die innere Anfrage nicht mehr unabhängig von der Äußeren ausgeführt werden, sondern muss für jedes Tupel der äußeren Anfrage erneut ausgeführt werden. Man spricht in diesem Fall von einer korrelierten oder verzahnt geschachtelten Anfrage [HS00]. Das folgende Beispiel zeigt eine derart verzahnte Abfrage. Die Anfrage soll alle Flüsse zurückliefern, die durch mehr als 3 Länder fließen. name Amazonas Amudarja Dnepr Donau ... 12 Datensätze Die zusätztlich hier neu eingeführte Punkt -Notation dient zur eindeutigen Unterscheidung der Attribute in den verschiedenen Tabellen. So gibt es beispielsweise sowohl in der Tabelle river, als auch in der Tabelle country ein Attribut name, die durch Voranstellen des Tabellennamens auseinander gehalten werden können. 16 1 SQL Es gilt allgemein als guter Stil mit der Punktnotation zu arbeiten, da man dadurch schneller erkennen kann, zu welcher Tabelle das jeweilige Attribut gehört. Die Notation wird auch von allen relationalen Datenbanksystemen unterstützt. Das eigentlich Interessante an der grau unterlegten Subquery befindet sich in der whereKlausel, die lautet: where geo_river.river = river.name In der Subquery ist das Attribut river.name jedoch unbekannt, da in der from-Klausel nur die Tabelle geo_river eingebunden wird. river.name stammt jedoch aus der äußeren Query (durch Pfeil angedeutet), in der die Tabelle river eingebunden ist und ist somit deren Attribute auch in der inneren Anfrage sichtbar sind. Die Abfrage läuft nun wie folgt ab: 1. Die äußere Anfrage untersucht den ersten Datensatz, konkret einen Fluss. 2. Der Name des Flusses (Attribut river.name) wird jetzt an die Subquery übergeben, die daraufhin untersucht, wie viele Datensätze mit unterschiedlichem Landescode (count(distinct geo_river.country)) für diesen Flussnamen existieren. 3. Die Anzahl der Datensätze wird an die äußere Anfrage zurückgegeben und in der where-Klausel mit dem Literal 3 verglichen. Ist der zurück gelieferte Wert größer oder gleich, so wird der Name des Flusses ausgegeben. 4. Anschließend wird von der äußeren Anfrage der nächste Datensatz gelesen und es wird mit Schritt (2) fortgefahren. Ähnlich wie der in-Operator funktioniert der exists -Operator. Im Unterschied zum in-Operator wird der exists-Operator ohne Attribut und Operator eingesetzt. Er überprüft lediglich, ob eine Subquery mindestens einen Datensatz zurückliefert. Ist das der Fall liefert exists TRUE zurück, andernfalls FALSE. Der exists-Operator wird fast ausschließlich mit korrelierten Subqueries eingesetzt. Beispielsweise lässt sich die letzte Anfrage nach den Flüssen, die durch drei oder mehr Länder fließen auch mittels des exists-Operators und einer having-Klausel in der grau unterlegten korrelierten Unterabfrage formulieren: name Amazonas Amudarja Dnepr Donau ... 12 Datensätze Mengenoperationen. SQL stellt eine Reihe von Mengenoperationen zur Verfügung, mittels denen die Ergebnisse von zwei select-Statement kombiniert werden können. Im einzelnen handelt es sich um folgende Operationen: • union : Vereinigung der Tupel beider Anfragen. 1.6 Data Manipulation Language • • 17 except : Es werden alle Tupel des ersten select-Statement ausgegeben, die nicht im Ergebnis des zweiten select-Statement stehen16. intersect : Es werden alle Tupel ausgegeben, die sich sowohl im Ergebnis der erstenals auch der zweiten SQL-Anweisung befinden17. Diese Operationen arbeiten als reine Mengenoperationen , d.h. es findet jeweils eine Duplikatseliminierung der Ergebnisrelation statt. Sollen Duplikate erhalten bleiben, so muss man auf die Operationen union all, except all18 bzw. intersect all18 zurückzugreifen. Bei den Operationen ist darauf zu achten, dass die Tupel der beiden Ergebnismengen eine kompatible Struktur besitzen. Kompatibel heißt in diesem Fall, dass die Anzahl Spalten in den beiden select-Klauseln identisch sein muss und die jeweiligen Datentypen der Attribute kompatibel19 sind. Das folgende Beispiel vereinigt alle Seen und Meere, die Zuflüsse haben. (select sea as "Gewässer" from river where sea is not null) union (select lake as "Gewässer" from river where lake is not null) order by 1 Joins. Bisher kamen die angezeigten Ergebnisse stets aus einer Tabelle. Zwar kann mithilfe von Subqueries auf Daten mehrerer Tabellen zugegriffen werden, Anfragen wie: “Gib alle Flüsse mit ihren Längen und den Namen der durchflossenen Ländern aus” konnten bisher aber nicht beantwortet werden, da hierbei die benötigten anzuzeigenden Daten aus verschiedenen Tabellen stammen. Mit Hilfe von Joins können Daten aus verschiedenen Tabellen miteinander in Bezug gesetzt und zusammen ausgegeben werden. Betrachten wir noch einmal die Daten aus den Tabellen river, geo_river und country. Die Tabelle river enthält die Flussdaten, während die Tabelle country Daten zu den einzelnen Ländern bereithält. Die beiden Tabellen haben keinerlei Bezug zueinander. Der Bezug kann aber über die Tabelle geo_river hergestellt werden. Die Spalte geo_river.river ist ein Fremdschlüssel auf den Primärschlüssel der Tabelle river (Attribut name), während das Attribut geo_river.country ein Fremdschlüssel auf den Primärschlüssel der Tabelle country (Attribut code) darstellt. Abbildung 1.4 verdeutlicht diesen Zusammenhang graphisch. Will man beispielsweise wissen, durch welche Länder die einzelnen Flüsse fließen, so muss man die beiden Fremdschlüsselbeziehung der Tabelle geo_river verfolgen und dabei die Schlüssel-/Fremdschlüsselattribute jeweils gleichsetzen. Konkret muss im obigen Beispiel gelten: 16 Oracle, PostgreSQL nicht beim MS SQL-Server und MySQL nicht beim SQL-Server und MySQL 18 nur DB2 19 Numerische Datentypen sind untereinander kompatibel und alle zeichenkettenbasierten Datentypen sind ebenfalls untereinander kompatibel 17 18 1 SQL river.name = geo_river.river und geo_river.country = country.code (Linien links in Abbildung 1.4) (Linien rechts in Abbildung 1.4) Abb. 1.4: Beziehung zwischen Flüssen und Ländern Die im SQL-89 Standard festgelegte Syntax für einen Join (Theta -Notation) , lässt sich dann auch relativ einfach aus obiger Überlegung ableiten, indem in der from-Klausel einfach alle Tabellen aufgeführt werden, die für die Anfrage relevant sind und in der where-Klausel die Bedingungen, welche zwischen den Schlüsseln-/Fremdschlüsseln gelten müssen, formuliert werden. So lautet die Anfrage, die für alle Flüsse über 2000 Kilometer Länge, den Namen, die Länge, sowie die Namen der durchflossenen Länder ausgeben soll, wie folgt: select distinct river.name as Fluss, river.length as Länge, country.name as Land from country, river, geo_river where river.name = geo_river.river and geo_river.country=country.code and river.length > 2000 order by 2 desc Fluss Länge Amazonas 6518 Brazil Amazonas 6518 Colombia Amazonas 6518 Peru Jangtse-Kiang 6100 China Parana 4700 Argentina ... ... Land ... 49 Datensätze distinct muss in diesem Fall benutzt werden, da in der Tabelle geo_river die Beziehungen zu den einzelnen Provinzen angegeben sind, hier aber nur die Länder interessieren. Ein Nachteil der Join-Syntax im SQL-89 Standard ist, dass man auf den ersten Blick nicht erkennen kann, was Schlüssel-/Fremdschlüsselvergleiche sind und wobei es sich um echte Bedingungen (fluss.laenge > 2000) handelt. 19 1.6 Data Manipulation Language Aus diesem Grund wurde im SQL-2 Standard von 1992 eine etwas andere Notation für Joins vorgeschlagen, die so genannte ANSI -Notation. Neu bei der ANSI-Notation ist die join-Klausel. Die Join Klausel tritt nach einer from- oder join-Klausel auf20, erwartet einen Tabellennamen und erlaubt anschließend (nach dem Schlüsselwort on) die Formulierung einer oder mehrerer Schlüssel/Fremdschlüsselbedingungen für die zuvor angegebenen Tabellen. So lautet etwa die zuvor gestellte Anfrage in ANSI-Notation: select distinct river.name as Fluss, river.length as Länge, country.name as Land from country join geo_river on country.code=geo_river.country join river on geo_river.river = river.name where river.length > 2000 order by 2 desc Fluss Länge Amazonas 6518 Brazil Amazonas 6518 Colombia Amazonas 6518 Peru Jangtse-Kiang 6100 China Parana 4700 Argentina ... ... Land ... 49 Datensätze Bei dieser Notation wird die Trennung zwischen der eigentlichen Bedingung und den Schlüssel-/Fremdschlüsselbedingungen im Anschluss an die beiden join-Klauseln deutlich. Neben dem klassischen Join mit Schlüssel-/Fremdschlüsselbedingung unterstützen die Datenbanken noch eine Reihe weitere Arten von Joins, die im Folgenden vorgestellt werden sollen. • Cross Join (Kartesisches Produkt): Jeder Datensatz der linken Tabelle (A) wird mit jedem Datensatz der rechten Tabelle (B) kombiniert. Das Ergebnis ist eine Ergebnistabelle mit n-Zeilen21 und m-Spalten22. Der Cross-Join kommt in seiner Reinform relativ selten vor Er entspricht dem klassischen Join ohne Formulierung von Schlüssel-/Fremdschlüsselbedingungen. Die folgende Abbildung zeigt exemplarisch die Konstruktion des kartesischen Produktes aus zwei Tabellen. Abb. 1.5: Kartesisches Produkt von 2 Tabellen 20 Die join-Klausel kann mehrfach auftreten, die erste join-Klausel folgt jedoch immer der from-Klausel. 21 n = Anzahl_Datenstze(A) ∗ Anzahl_Datenstze(B) 22 m = Anzahl_Attribute(A) + Anzahl_Attribute(B) 20 • 1 SQL Left Outer Join: Wie ein normaler Join, bei dem zusätzlich aber auch noch alle die Datensätze aus der linken23 Tabelle ausgegeben werden24, für die es auf der rechten Seite keine passenden Datensätze gibt. Die Spalten aus der rechten Tabelle enthalten dann im Ergebnis NULL Werte. Ein Beispiel für einen Outer-Join ist folgende Anfrage: “Gib alle Länder zusammen mit ihren Flüssen aus” name select distinct country.name, geo_river.river from country left join geo_river on country.code=geo_river.country order by country.name, geo_river.river river ... ... Albania White Drin Algeria Andorra Angola Casai ... ... 319 Datensätze • • • Man kann sehen, dass “Algerien” und “Andorra” im Ergebnis auftauchen, obwohl für sie keine Flüsse in der Datenbank hinterlegt sind. Right Outer Join: Wie ein normaler Join, bei dem zusätzlich aber auch noch alle die Datensätze aus der rechten Tabelle ausgegeben werden24 für die es auf der linken Seite keine passenden Datensätze gibt. Die Spalten aus der linken Tabelle enthalten dann im Ergebnis NULL Werte. Full Outer Join: Wie ein normaler Join, bei dem zusätzlich alle Datensätze sowohl aus der linken- als auch der rechten Tabelle ausgegeben für die es keine passenden Datensätze gibt. Die Spalten aus der linken bzw. rechten Tabelle enthalten dann im Ergebnis NULL Werte. Self Join: Wie ein normaler Join, allerdings wird zweimal die selbe Tabelle genutzt. Ein Beispiel dazu findet sich auf Seite 21. Aliasnamen für Tabellen. Esgibt in SQL die Möglichkeit Aliasnamen für Tabellen zu vergeben. Aliasnamen werden in der from- und join-Klausel durch Anhängen des Aliasnamen direkt hinter den Tabellennamen vergeben. Anschließend kann im Statement der Aliasname anstelle des Tabellennamens verwendet werden. Notation: <tabellenname> <aliasname> Ein Vorteil liegt darin, dass man im Allgemeinen kurze und/oder aussagekräftige Aliasnamen vergeben kann und so zu einer kompakteren und übersichtlicheren Schreibweise bei Benutzung der Punkt-Notation kommt. Es gibt jedoch auch Situation, in der die Benutzung von Aliasen zwingend notwendig ist. Beispielsweise, wenn für alle Städte der USA Namensvettern in anderen Ländern gesucht werden sollen. Die benötigten Informationen liegen zwar alle in der Tabelle city. Allerdings ist es nicht so einfach die Anfrage zu formulieren, da die where-Klausel es lediglich erlaubt Bedingungen auf Datensatzebene25 zu formulieren. Um zum Ziel zu 23 im select-Statement zuerst genannten Tabelle sofern sie eine eventuell zusätzlich angegebene where-Klausel erfüllen. 25 d.h. es wird zu einem Zeitpunkt genau ein Datensatz untersucht. 24 21 1.6 Data Manipulation Language kommen muss man die Tabelle city mit sich selber vergleichen, d.h. es muss für jeden Datensatz aus der Tabelle city untersucht werden, ob es einen weiteren Datensatz in der selben Tabelle gibt, der den gleichen Namen (Attribut name) aber eine andere Länderkennung (Attribut country) hat. Das komplette Statement lautet dann folgendermaßen: name select from join on where and order c1.name, c2.country city c1 city c2 c1.name=c2.name c1.country <> c2.country c1.country=’USA’ by 1,2 country Albany AUS Alexandria ET Alexandria RO Birmingham GB ... ... 18 Datensätze Funktionen. SQL definiert eine Reihe von Funktionen die im Rahmen der DML Statements eingesetzt werden können. So gibt es numerische, zeichenkettenbasierte Funktionen, ebenso wie eine Reihe von Konvertierungs- und Datumsfunktionen. Die Funktionen können in der select-, where-, order- und having-Klausel eingesetzt werden. Da im Bereich der Funktionen noch etwas Wildwuchs in Bezug auf Standardisierung herrscht, sollen in Tabelle 1.1 lediglich einige exemplarische Funktionen aufgezählt werden, ohne jedoch auf deren genaue Syntax-/Aufrufkonventionen einzugehen. Der Leser sei an dieser Stelle auf die Literatur des konkreten Datenbanksystems verwiesen um einen umfassenden Überblick über die dort implementierten Funktionen zu erhalten. Tabelle 1.1: Funktionen in SQL Kategorie Vertreter Numerische Funktionen Trigonometrische Funktionen sin(...), cos(...), tan(...), asin(...), acos(...), atan(...), . . . Rundungsfunktionen round(...), ceil(...), floor(...), . . . Logarithmische- und Exponentialfunktionen ln(...), log(...), exp(...), . . . Potenz- und Wurzelfunktionen sqrt(...), power(...), pow(...), . . . sonstige numerische Funktionen sign(...), abs(...), . . . Zeichenketten basierte Funktionen ASCII Funtionen ascii(...), ord(...), char(...), . . . Groß-/Kleinschreibung upper(...), lower(...), initcap(...), . . . Leerzeichen am Beginn/Ende der Zeichenkette entfernen trim(...), ltrim(...), rtrim(...), . . . Zeichenketten durchsuchen/-ersetzen substr(...), instr(...), position(...), replace(...), patindex(...), translate(...), . . . sonstige Zeichenkettenfunktion concat(...), encrypt(...), format(...), length(...), left(...), . . . 22 1 SQL Kategorie Vertreter Datumsfunktionen Datumsinformationen: day(...), month(...), year(...), curdate(...), sysdate(), get_date(), . . . Konvertierungsfunktionen Zeichenkette -> Datum/Zeit to_date(...), extract(...), . . . Zeichenkette -> Zahl to_number(...), extract(...), . . . Unterabfragen/geschachtelte Ausdrücke. Auf Seite 3 wurde beschrieben, dass das Ergebnis einer relationalen Operationen stets wieder eine Relation ist, was insbesondere auch die Formulierung geschachtelter Anfragen erlaubt. Übertragen auf SQL bedeutet dies, dass in der from-Klausel auch ein select-Statement stehen kann. Einschränkend muss jedoch gesagt werden, dass an dieser Stelle keine korrelierten Abfragen (siehe Seite 15) erlaubt sind, d.h. das select-Statement in der from-Klausel darf keine Attribute aus der äußeren Query verwenden. Die Anfrage im folgenden Beispiel gibt alle Länder aus, deren Anteil der Stadtbevölkerung mindestens 50% der Gesamtbevölkerung beträgt. Dazu wird ein Join zwischen der Tabelle country und dem in Klammer stehenden select-Statement (Tabellenalias u) formuliert. name ratio Liechtenstein 0.89 Singapore 0.75 Australia 0.67 Qatar 0.61 South Korea 0.59 ... ... 8 Datensätze Das in der join-Klausel stehende select-Statement liefert pro Land (Spalte country) die berechnete Summe aller Einwohner der Städte (Spaltenalias population) zurück. Dieses Ergebnis wird dann mittels des Joins mit der Tabelle country um den ausgeschriebenen Landesnamen (Attribut country.name) ergänzt und nur die Länder ausgegeben, deren Anteil der Stadtbevölkerung (u.population) an der Gesamtbevölkerung (Spalte country.population) bei über 50% liegt. Diese Art von Anfrage wird auch als Instant View26 bezeichnet. Weiterhin ist es auch möglich, dass ein select-Statement in der select-, orderoder where-Klausel auftritt. Im Unterschied zum vorherigen Fall sind hierbei auch korrelierte Subqueries erlaubt, allerdings muss das Statement genau einen Wert zurückliefern. So kann die letzte Anfrage nach “allen Ländern mit mehr als 50% Stadtbevölkerung” auch folgendermaßen formuliert werden: 26 Ein View ist eine virtuelle, auf einer Anfrage basierenden Tabelle und wird auf Seite 29 vorgestellt. 1.6 Data Manipulation Language 23 name ratio Liechtenstein 0.89 Singapore 0.75 Australia 0.67 Qatar 0.61 South Korea 0.59 United Kingdom 0.56 Antigua and Barbuda 0.55 Bahamas 0.54 8 Datensätze Das korrelierte Subquery in der select-Klausel summiert dabei alle Einwohner in den Städten des aktuellen Landes (c.code) auf. Zur Formulierung der Bedingung, dass nur die Länder ausgegeben werden sollen, deren Anteil bei über 50% liegt, wird das in der select-Klausel stehende select-Statement in der where-Klausel wiederholt27. Soweit zu den verschiedenen, auf den Operationen der Relationenalgebra basierenden, Varianten des select-Statement. In den folgenden drei Abschnitten sollen jetzt, ebenfalls auf Basis der Mondial-Datenbank, die insert-, update- und delete-Statements vorgestellt werden. 1.6.3 Insert-Statement Das insert-Statement erlaubt das Einfügen von Tupeln in eine Tabelle. Es stehen zwei Varianten zur Auswahl: insert into <tabellenname> (<attributliste>) values (<werteliste>) Bei dieser ersten Variante wird genau ein Datensatz in die angegebene Tabelle eingetragen. Die Attributliste beinhaltet dabei Attribute aus der angegebenen Tabelle. Es ist möglich weniger Attribute anzugeben, als für die Tabelle definiert worden sind28 . Wichtig ist hierbei, dass die Anzahl der hinter der insert-Klausel stehenden Attribute identisch ist mit der Anzahl der Werte in der values-Klausel und die Datentypen übereinsteimmen. Das folgende Beispiel zeigt den Eintrag der Stadt Karlsruhe in die Tabelle city. insert into city (name, country, province, population) values (’Karlsruhe’, ’D’, ’Baden Württemberg’, 277011) Es ist jedoch auch möglich, mehrere Datensätze mittels einem insert-Statement einzutragen. Dazu wird das insert-Statement mit einem select-Statement kombiniert. Die sich im select-Statement qualifizierenden Datensätze werden dann in die im 27 das geschachtelte select-Statement wird aber deshalb nicht mehrfach ausgeführt, sondern der Optimierer des Datenbanksystems erkennt, dass die Anfrage in der select-Klausel und where-Klausel identisch sind und führt sie deshalb nur einmal aus 28 Es können alle die Attribute weggelassen werden, die bei der Definition der Tabelle (siehe Seite 27) nicht als NOT NULL definiert wurden. 24 1 SQL insert-Statement angegebene Tabelle eingetragen. Hierbei müssen die in der insertKlausel angegebenen Attribute in Anzahl und Datentyp mit den Attributen des selectStatement übereinstimmen. insert into <tabellenname> (<attributliste>) (<select-Anweisung>) Das folgende Beispiel trägt alle Datensätze aus der Tabelle terra_stadt29 in die Tabelle city ein. insert into city (name, country, province, population, longitude, latitude) (select name, l_id, lt_id, einwohner, laenge, breite from terra_stadt) 1.6.4 Update-Statement Das update-Statement dient dazu, Datensätze die sich bereits in einer Tabelle befinden zu modifizieren. Dabei können ein oder mehrere Datensätze auf einmal modifiziert werden. Die Syntax ist wie folgt: update <tabellenname> set <attribut1>=<wert1> [,<attribut2>=<wert2>] ... where <bedingungen> Die Bedingungen entsprechen denen der where-Klausel des select-Statement. So setzt das folgende Statement die Koordinaten der Stadt Karlsruhe auf (8.4, 49.0): update city set longitude=8.4, latitude=49.0 where country=’D’ and province=’Baden Württemberg’ and name=’Karlsruhe’ Neben Literalen können auch Berechnungen oder korrelierte Subqueries (die genau einen Wert zurückliefern) eingesetzt werden. So erhöht das folgende Beispiel die Einwohnerzahl aller italienischen Städte um 5%: update city set population = round(population*1.05) where country=’I’ 29 Die Terra-Datenbank [DR90] wurde am Institut für Programmstrukturen und Datenorganisation der Universität Karlsruhe entwickelt und dort im Datenbankpraktikum eingesetzt. Die TerraDatenbank diente der Mondial-Datenbank als Vorbild und z.T auch als Datenquelle [Mon99]. 1.7 Data Definition Language 25 Das letzte Beispiel nutzt eine korrelierte Subquery um aus der Gesamtfläche der Länder pro Kontinent, die Gesamtfläche der einzelnen Kontinente zu berechnen und in die Tabelle continent einzutragen30. 1.6.5 Delete-Statement Das delete-Statement ist für das Löschen einer oder mehrerer Datensätze einer Tabelle verantwortlich. Die allgemeine Syntax ist wie folgt: delete from <tabellenname> where <bedingungen> Die Bedingungen entsprechen denen der where-Klausel des select-Statement. So lautet der Befehl um alle Städte zu löschen, für die keine vollständigen Koordinateninformationen in der Tabelle city abgelegt sind: delete from city where longitude is null or latitude is null 1.7 Data Definition Language Im Gegensatz zur DML, bei der die Syntax größtenteils über die Herstellergrenzen hinweg identisch ist, ist dies bei der DDL nicht ganz der Fall. So besitzen die Produkte der einzelnen Hersteller zumeist unterschiedliche Bezeichnungen für die zur Verfügung stehenden Datentypen und ihre Syntax (selten auch die Semantik) weicht an einzelnen Stellen voneinander ab. Da auf Platzgründen nicht auf die verschiedenen konkreten Implementierungen eingegangen werden kann, wird hier stellvertretend die DDL der Oracle-Datenbanken vorgestellt. Die dabei vermittelten Konzepte sind aber auf die anderen relationalen Systeme übertragbar. 30 Bei der Tabelle encompasses handelt es sich um eine Beziehungstabelle, welche angibt, welches Land mit welchem Prozentsatz zu einem bestimmten Kontinent gehört. 26 1 SQL Bei der Entwicklung eines konkreten Datenbankschemas ist dann auf die entsprechenden Manuals des jeweiligen Herstellers bzw. die einschlägigen Fachliteratur zurückzugreifen 31 . 1.7.1 Tabellen Primäre Aufgabe der DDL ist es, die Strukturen in welche die Datensätze abgelegt werden, vorzugeben. Die Datensätze werden in so genannten Tabellen abgelegt, welche die SQL-spezifische Umsetzung der Relationen des Relationenmodells darstellen. Eine Tabelle besitzt eine feste Anzahl an Attributen, die alle einen, für diese Tabelle eindeutigen, Bezeichner und Datentyp besitzen. Neben der Aufgabe der reinen Speicherung spielen konsistenzsichernde Maßnahmen zur Wahrung der Integrität (siehe auch Abschnitt 1.2.2) der Daten eine entscheidende Rolle. So können bei der Definition der Tabellen so genannte Constraints32 angegeben werden. Diese Constraints formulieren Bedingungen, die von den Datensätzen erfüllt werden müssen. Erfüllt ein Datensatz diese Bedingungen nicht so wird er von der Datenbank abgewiesen, bzw. die Ausführung der entsprechenden DML-Operation wird vom System abgelehnt. Constraints lassen sich folgenden Kategorien zuordnen: • Primärschlüssel (Primary Key): Definition eines oder mehrere Attribute einer Tabelle, deren Wert(e) den Datensatz eindeutig identifiziert. Der Wert des Primärschlüssels (oder eines Teils davon) darf niemals NULL sein. • Fremdschlüssel (Foreign Key): Definition eines oder mehrere Attribute einer Tabelle, die auf einen anderen Datensatz (in der gleichen oder einer anderen Tabelle) verweisen. Ist ein Fremdschlüssel definiert, so überwacht das Datenbanksystem, dass der Datensatz auf den verwiesen wird auch existiert. Dies wird als referentielle Integrität bezeichnet. Soll ein Datensatz gelöscht werden, auf den andere Datensätze mittels Fremdschlüssel verweisen, so wird dies vom Datenbanksystem abgelehnt, da sonst die referentielle Integrität verletzt würde. Es ist jedoch möglich für den Fremdschlüsseldatensatz ein spezielles Verhalten beim Löschen/Ändern des referenzierten Datensatzes zu definieren. So kann festgelegt werden, dass der verweisende Datensatz dann ebenfalls gelöscht werden soll (on delete cascade), bzw. dass der Wert des Fremdschlüssels auf NULL gesetzt werden soll (on delete set null). • Eindeutigkeit (unique): Definition eines oder mehrere Attribute einer Tabelle, deren Werte für alle Datensätze unterschiedlich sein müssen. Werden mehrere Attribute definiert, so müssen die Attributwertkombinationen unterschiedlich sein. • Nicht Null (not null): Wird eine Attribut als NOT NULL definiert, so muss jeder Datensatz für dieses Attribut einen Wert ungleich NULL besitzen. • Wertüberprüfung (check): Jedes Attribut hat einen bestimmten Datentyp der die möglichen Werte vorgibt (Zeichenketten bestimmter Länge, Zahlen mit vorgegebener Stellenzahl/Präzision, Datumstypen, etc.). Mittels des Check-Constraints kann 31 Es soll an dieser Stelle auch nicht verschwiegen werden, dass Kenntnisse des Datenbankdesigns eine noch wichtigere Rolle spielen. Ein empfehlenswertes Buch zum Thema Datenbankdesign ist [Her03]. 32 zu deutsch: Beschränkungen 1.7 Data Definition Language 27 dieser Wertebereich noch weiter eingeschränkt werden, indem beispielsweise mögliche diskrete Einzelwerte vorgegeben werden oder bei numerischen Typen ein Interval angegeben wird, in dem sich die möglichen Werte des Attributs befinden müssen. Im Folgenden sollen jetzt die wichtigsten Statements zum Anlegen/Modifiziern eines Datenbankschemas vorgestellt werden: Create Table. Das create table-Statement dient zum Anlegen einer neuen Tabelle. Neben der Struktur der Tabelle können auch die Constraints mit angegeben werden. Syntax: create table <tabellenname> ( <attributname 1> <typ> [<constraints>], ... <attributname n> <typ> [<constraints>] [,<constraints>] ) Im Folgenden soll das Anlegen der beiden Tabellen country und city der MondialDatenbank, einschließlich der Definition der Constraints, gezeigt werden33: Hinweis: Bei Oracle nutzt man statt dem Datentyp varchar, den oracle-spezifischen Datentyp varchar2. create table country ( code varchar2(3), name varchar2(32) not null unique, capital varchar2(35), province varchar2(32), area int check (area >= 0), population int check (population >= 0), primary key(code) ); create table city ( name varchar2(35), country varchar2(4), province varchar2(32), population int check (population >= 0), primary key (name, province, country), foreign key (country) references country(code) ); Das erste create table-Statement legt die Tabelle country an. Das Attribut code wird als Primärschlüssel definiert, der durch eine maximal 3 Zeichen lange Zeichenkette repräsentiert wird. Der Wert des Attributs name kann aus maximal 32-Zeichen bestehen, zusätzlich wird gefordert, dass dieses Attribut immer einen Wert haben muss und weiterhin, dass dieser Wert für alle Datensätze in der Tabelle eindeutig sein muss, d.h. es darf beispielsweise keine zwei Datensätze mit dem Namen “Neuseeland” geben. 33 Hinweis: Wird mehr als ein Statement angegeben, so sind die einzelnen Statements durch einen Strichpunkt (;) voneinander zu trennen, damit der SQL-Parser erkennen kann wo die einzelnen Statements enden. 28 1 SQL Die nächsten beiden Attribute (capital, province) geben die Hauptstadt des Landes an. Sie bilden zusammen mit dem Primärschlüssel der Tabelle (code) einen Fremdschlüssel auf eine Stadt in der Tabelle city. Da zum Zeitpunkt des Anlegens der Tabelle country, die Tabelle city noch nicht existiert, kann der Fremdschlüssel an dieser Stelle noch nicht formuliert werden, sondern dies wird nach dem Anlegen der Tabelle city durch ein alter table-Statement (s.u.) nachgeholt. Die letzten beiden Attribute der Tabelle (area, population) sind numerisch (Integer) und geben die Fläche des Landes sowie seine Einwohnerzahl an. Als zusätzliche Konsistenzbedingung ist hier angegeben, dass sowohl die Fläche als auch die Einwohnerzahl größer gleich 0 sein müssen. Das zweite create table-Statement zum Anlegen der Tabelle city ist ähnlich aufgebaut. Im Unterschied zur Tabelle country besteht der Primärschlüssel aber aus drei Attributen (name, province, country). Das Attribut country ist weiterhin ein Fremdschlüssel auf die Tabelle country. Da die Tabelle bereits existiert, kann der entsprechende Foreign Key Constraint auch sofort formuliert werden. Alter Table. Nachdem nun die Tabelle city angelegt worden ist, kann der Foreign Key Constraint, für die Hauptstadt eines Landes, gesetzt werden. Dies geschieht durch ein alter table-Statement , bei dem sowohl Attribute hinzugefügt und gelöscht, als auch neue Constraints eingerichtet und ebenfalls wieder gelöscht werden können. Das folgende Codefragment zeigt die Hinzunahme von zwei weiteren Attributen einschließlich Constraints zur Tabelle city, als auch in einem zweiten alter tableStatement die Formulierung des Fremdschlüssels für die Hauptstadt eines Landes in der Tabelle country. Das dritte alter table-Statement ist dafür verantwortlich, dass jedes Land eine andere Hauptstadt hat. Dies wird dadurch realisiert, indem für die drei Attribute capital, province und code gefordert wird, dass sie unique sind, d.h. jede Attributwertkombination nur einmal vorkommen darf. In den beiden letzten Statements wird weiterhin von der Möglichkeit Gebrauch gemacht, dem Constraint einen Namen zu geben. Wird bei der Definition eines Constraints kein Name angegeben, so wird vom Datenbanksystem ein Name generiert Es empfiehlt sich jedoch einen (aussagekräftigen) Namen für ein Constraint anzugeben, da dieser im Falle der Verletzung einer Regel mit angegeben wird und man so auf den ersten Blick erkennen kann, wo der Fehler liegt. alter table city add ( longitude float check ((longitude >= -180) and (longitude <= 180)), latitude float check ((latitude >= -90) and (latitude <= 90)) ); alter table country add ( constraint fk_country_city_capital foreign key (capital, province, code) references city(name, province, country) ); alter table country add ( constraint uq_country_capital unique (capital, province, code) ); 1.7 Data Definition Language 29 Das Löschen von Constraints erfolgt durch Angabe des Namens des Constraints. Das Löschen des eben angelegten Fremdschlüssels sieht demnach folgendermaßen aus: alter table country drop constraint fk_country_city_capital Im Folgenden noch zwei Beispiele für das Löschen einzelner Attribute einer Tabelle bzw. das Löschen einer kompletten Tabelle: alter table country drop column area; drop table city; Der Versuch eine Tabelle, die durch Fremdschlüssel referenziert wird, zu löschen scheitert. Entweder man löscht erst das Constraint oder man fügt die Schlüsselworte cascade constraints hinten an das drop table-Statement an, das vor dem Löschen der Tabelle erst die auf sie verweisenden Fremdschlüssel-Constraints löscht. Beispiel: drop table country cascade constraints Es gibt bei vielen Datenbanksystemen noch die Möglichkeit, eine Tabelle basierend auf dem Ergebnis eines select-Statement anzulegen. Die Struktur der Tabelle wird durch die Attribute des select-Statement vorgegeben. Zusätzlich werden aber auch noch alle Datensätze, welche durch das select-Statement ausgewählt wurden in die neu angelegte Tabelle eingetragen. Das Statement ist demnach eine Mischung aus einem DDLund einem DML-Statement. Diese Feature ist speziell bei der Reorganisation bestehender Datenbanken äußerst hilfreich. Notation: create table <tabellenname> as <select-statement> 1.7.2 Views Ein View34 ist eine virtuelle Tabelle. Anders als bei einer Tabelle werden die zugrunde liegenden Daten aber nicht physikalisch gespeichert, sondern ihnen liegt eine Berechnungsvorschrift in Form eines select-Statement zugrunde. Views können in DMLStatements genauso wie Tabellen eingesetzt werden. Weiterhin lassen sich Views auch auf Basis von bereits existierenden Views definieren. Die Datensätze der Views repräsentieren immer den aktuellen Zustand in der Datenbank, da sie die Daten zum Zeitpunkt der Benutzung35 des Views aus den zugrunde liegenden Tabellen/Views extrahieren. Views besitzen eine Reihe von interessanten Einsatzgebieten, die im folgenden kurz angesprochen werden sollen [HS00]. 34 35 zu deutsch: Sicht und nicht zum Zeitpunkt des Anlegens des Views 30 1 SQL Views können in select-Statements genauso wie Tabellen eingesetzt werden. Dadurch lassen sich oft benötigte und/oder komplexe Anfragen in Form von Views vordefinieren, so dass konkrete Anfragen dann auf dem View aufsetzen können und somit die Anfrage entsprechend einfacher zu formulieren ist. • Views erlauben die Realisierung einer klaren Schnittstelle oberhalb der materialisierten Tabellen. Für verschiedene Benutzergruppen lassen sich so verschiedene Sichten einrichten, die genau auf deren Bedürfnisse zugeschnitten sind. • Durch die Formulierung von Bedingungen innerhalb des select-Statement, können Zugriffsbeschränkungen formuliert werden. Einzelne Spalten oder ganze Datensätze sind so beispielsweise für bestimmte Benutzergruppen ausgeblendet bzw. werden nur in aggregierter Form präsentiert. Die Regelung des Zugriffs einzelner Benutzer- oder ganzer Benutzergruppen auf Tabellen oder Views erfolgt durch die DCL und wird in Abschnitt 1.8 vorgestellt. Das folgende Beispiel zeigt das Löschen und die anschlieSSende Neudefinition des Views Hauptstaedte: • drop view hauptstaedte; create as select from join on and and view hauptstaedte city.* city country city.name = country.capital city.province = country.province city.country = country.code; Anschließend kann der View dann für beliebige Anfragen benutzt werden, wie z.B. hier bei der Frage nach allen Hauptstädten zwischen dem 20. Grad nördlicher- und dem 20. Grad südlicher Breite. select from where order * hauptstaedte latitude between -20 and 20 by name Views sind unter bestimmten Bedingungen auch änderbar, d.h. es können Daten neu eingetragen, modifiziert und auch wieder gelöscht werden. Die Änderungen werden dann an den dem View zugrunde liegenden Tabellen durchgeführt. Dies ist jedoch nicht immer möglich. So ist leicht einzusehen, dass bei einem View der eine Aggregatsfunktion wie avg(...) enthält, diese Spalte nicht änderbar sein kann, da die Änderung auf die n-Eingabedatensätze der Aggregatsfunktion propagiert werden müßte. Andere Operationen die i.a. Probleme bereiten sind die Duplikatseliminierung mit distinct, Mengenoperatoren wie union, intersect, etc., Joins, Subqueries in der select-Klausel und der Einsatz der group by-Klausel. Ist ein View änderbar, so kann durch die Angabe “with check option” am Ende der View-Definition noch festgelegt werden, dass nur solche Änderungen (neueintragen, modifizieren) vorgenommen werden dürfen, die anschließend im View auch sichtbar sind, sprich, der Datensatz muss die Bedingungen der where-Klausel erfüllen. 1.7 Data Definition Language 1.7.3 31 Indexe Ein Index ist ein Datenbankobjekt, das den Zugriff auf die Daten beschleunigen soll. So werden vom Datenbanksystem aus bereits Indexe auf Primär-, Fremdschlüssel- und Unique-Attribute gesetzt. Das Prinzip eines Index beruht darauf, dass bei der Suche nach einem oder mehreren Datensätzen nicht die gesamte Tabelle sequentiell in den Hauptspeicher geladen werden muss, was insbesondere bei großen Datenmengen eine sehr zeitaufwendige Operation ist, sondern es eine geeignete Datenstruktur gibt, die nach wenigen Schritten den Ort auf der Festplatte bestimmen kann, auf der sich der/die gesuchten Datensätze befinden. Durch das Anlegen von Indizes kann der Lesezugriff zwar beschleunigt werden, der schreibende Zugriff wird jedoch dabei langsamer, da nicht nur die eigentlichen Daten geändert werden müssen, sondern eben auch die zugehörigen Zugriffsstrukturen. In SQL wird ein Index auf ein oder mehrere Attribute einer Tabelle gelegt. Die Syntax zum Anlegen eines Index lautet wie folgt: create index <name> on <tabelle>(<attribut 1>, ..., <attribut n>) Das Gebiet der Anfrageoptimierung in Datenbanksystemen ist äußerst komplex und füllt viele Bücher. So spielt insbesondere auch die Kenntnis des internen Aufbaus und der Funktionsweise eines konkreten Datenbanksystems eine entscheidende Rolle um nichttriviale Optimierungen vorzunehmen. Der interessierte Leser sei hier auf [SB02] und [GUW99] verwiesen. 1.7.4 Automatisch generierte Primärschlüssel In der Mondial-Datenbank wird der Primärschlüssel jeweils aus einem oder mehreren Attributen des jeweiligen Objekttyps gebildet. So wird der Primärschlüssel einer Stadt (city) aus der Landes ID (country), dem Provinznamen (province) und dem Namen der Stadt name) gebildet. Als Beispiel für einen Primärschlüssel bestehend aus nur einem Attribut, sei die Tabelle Fluss angeführt, deren Primärschlüssel aus dem Name des Flusses (Attribut name) besteht. Das hat eine Reihe von Konsequenzen: • Es ist nicht möglich einen Fluss einzugeben der den selben Namen hat wie ein bereits vorhandener Fluss36 . Dieses Problem kann dadurch gelöst werden, indem weitere Attribute (z.B. die Koordinaten der Quelle) mit in den Primärschlüssel aufgenommen werden. • Ein Fremdschlüssel auf einen Datensatz dessen Primärschlüssel aus n-Attributen besteht, besteht ebenfalls aus n-Attributen. Um diese Probleme zu vermeiden führt man oft künstliche Schlüsselattribute ein, so genannte Surrogate Keys. Ein Schlüsselattribut hat in der Semantik der Anwendung keine Bedeutung und dient lediglich dazu eindeutige Schlüsselattributwerte bereitzustellen und damit auch Fremdschlüssel, bestehend aus lediglich einem Attribut, zu ermöglichen. 36 So gibt es beispielsweise sowohl in Rheinland Pfalz als auch in Bayern, Hessen und Sachsen einen “Schwarzbach”. 32 1 SQL Datenbanksysteme unterstützen den Entwickler bei der Generierung eindeutiger Attributwerte. So gibt es in Oracle so genannte Sequenzen, die fortlaufend neue numerische Werte erzeugen, die dann den betreffenden Spalten zugewiesen werden können. Beispiel: create sequence seq_mountain; create table mountain ( id integer, name varchar2(32), height float, primary key (id) ); insert into mountain (id, name, height) values(seq_mountain.nextval, ’Feldberg’,1493); Jeder Aufruf von seq_mountain.nextval liefert einen um eins erhöhten Wert zurück, der dann mittels des insert-Statement eingetragen wird. Der zuletzt zurückgelieferte Wert kann mittels seq_mountain.currval erfragt werden. Ein anderes verbreitetes Konzept ist, den Primärschlüssel bei der Definition als künstlichen Primärschlüssel zu charakterisieren, der vom Datenbanksystem automatisch mit eindeutigen Werten versorgt wird. Beispielsweise kann man in MySQL dem (numerischen) Primärschlüssel einer Tabelle die Eigenschaft auto_increment zuweisen. In diesem Fall wird beim Anlegen eines neuen Datensatzes für den Primärschlüssel kein Wert angegeben, sondern dieser wird vom Datenbanksystem gesetzt. Beispiel: create table mountain ( id int auto_increment, name varchar(32), height float, primary key (id) ); insert into mountain (name, height) values(’Feldberg’,1493); Der zuletzt von der Datenbank vergebene Wert kann mittels select last_insert_id() erfragt werden. 1.8 Data Control Language Datenbanken sind Mehrbenutzersysteme. Dies bedeutet zum einen, dass das System entsprechende Konzepte für eine Benutzer- und Rollenverwaltung bereitstellen muss, und zum zweiten auch die Koordination des Zugriffes mehrerer Benutzer auf den Datenbestand. 1.8 Data Control Language 33 Die Koordination des Zugriffs wird in Datenbanken mittels Transaktionen gelöst. Hierbei handelt es sich um ununterbrechbar ablaufende Operation, die die Datenbank durch eine Reihe von Einzelschritten von einem konsistenten Zustand in einen neuen, nicht notwendigerweise verschiedenen, konsistenten Zustand überführen. Während auf Transaktionen hier nicht weiter eingegangen wird37, sollen im Folgenden die grundlegenden Konzepte der Benutzer- und Zugriffsverwaltung vorgestellt werden. 1.8.1 Benutzer und Rollen Ein Datenbankmanagementsystem verwaltet im allgemeinen n-Benutzer und m-Datenbanken. In den meisten Systemen können Rollen definiert werden, mittels denen Benutzer mit gleichem Profil zusammenfasst werden können. Der Vorteil hierbei ist, dass die speziellen Rechte nicht jedem Benutzer direkt zugeteilt werden müssen, sondern zentral der Rolle zugeordnet werden können und dann die Benutzer diese Rolle zugewiesen bekommen. Ein Beispiel für das Anlegen einer neuen Rolle und eines neuen Benutzers in Oracle lautet wie folgt: create role student; create user student_123 identified by sm03r3br03d default tablespace users quota 20 M on users; Der Account student_123 hat das Passwort sm03r3br03d und kann maximal 20 Megabyte im Tablespace38 users an Informationen ablegen. Allerdings darf er sich zu diesem Zeitpunkt noch nicht mal am Server anmelden. Als nächstes wird deshalb der Rolle student das Recht eingeräumt sich am Server anzumelden (create session) und auch im eigenen Schema Tabellen anzulegen (create table). grant create session, create table to student Diese Rechte werden als Systemprivilegien bezeichnet. Oracle39 verfügt über ca. 140 verschiedenen Systemprivilegien, die eine feingranulare Rechtevergabe erlauben. Die nächste Anweisung im folgenden Codeblock weist dem Account student_123 die Rolle student zu, der damit automatisch alle Rechte der Rolle student erwirbt. Es ist jedoch auch möglich einem User direkt bestimmte Rechte zuzuordnen. grant student to student_123 37 eine detaillierte Vorstellung verschiedener Transaktionskonzepte findet sich in [GR92]. Bei Oracle werden alle Daten innerhalb von Tablespaces abgelegt. Ein Tablespace besteht aus einer oder mehreren Dateien auf Betriebssystemebene. Beim Anlegen eines Tablespaces wird der Platz auf der Festplatte durch die Dateien bereits fest allokiert, so dass er dann ausschließlich dem Datenbanksystem zur Verfügung steht. 39 Version 9i 38 34 1 SQL Um Benutzer und Rollen anzulegen, muss man entweder die Rolle dba besitzen, oder die entsprechenden Systemprivilegien (create role, create user) besitzen. Alle Tabellen und Datensätze die ein User anlegt, befinden sich in seinem privaten Schema, auf das andere Benutzer keinen Zugriff haben. Der User ist aber in der Lage, anderen Benutzern oder auch Rollen bestimmte Rechte auf seinen Daten einzuräumen. Diese Rechte werden als Objektprivilegien bezeichnet. So kann beispielsweise ein Benutzer der Rolle Student das Recht geben, die Daten in seiner Tabelle city zu lesen (Objektprivileg select) und neue Datensätze einzufügen (Objektprivileg insert). grant select, insert on city to student Es ist auch jederzeit mittels des revoke Befehls möglich, Rechte wieder zu entfernen. Um beispielsweise der Rolle student das Recht Datensätze einzufügen wieder zu nehmen, schreibt man einfach: revoke insert on city from student Analog können auch die Systemprivilegien widerrufen werden. Nachdem einem anderen User oder einer Rolle das Recht zugebilligt wurde auf die Daten einer Tabelle in einem fremden Schema zuzugreifen, bleibt nur noch die Frage zu klären, wie der Zugriff auf die fremden Daten erfolgt. Dies geschieht, indem dem Tabellennamen, der Name des Users dem die Tabelle gehört, vorangestellt wird. So erfolgt der lesende Zugriff auf die Tabelle city des Users mondial so: select * from mondial.city where einwohner < 20000 1.9 Übungsaufgaben 1. 2. 3. 4. 5. 6. 7. 8. Welche Städte liegen auf dem Äquator? Welche Organisationen haben ihren Sitz in Wien? Welche Hauptstädte sind zugleich Sitz von Organisationen? Welche Organisationen haben ihren Sitz in Asien (Ausgabe nach Name sortiert)? Wie heißt die größte Insel? Gib alle Länder ohne Berge aus. Gib die Namen aller Hauptstädte, sortiert nach Name aus. Gib die Namen und Einwohnerzahl aller Hauptstäde, absteigend sortiert nach Einwohnerzahl aus. 9. Gib die Stadt mit den meisten Einwohnern aus. 10.Welcher Fluss fließt durch die meisten Länder? 1.10 Frei verfügbare Software 35 11.Wie lang ist die Außengrenze von Deutschland? 12.Wieviele Inseln gehören zu den kleinen Antillen? 13.Welche Inselgruppen bestehen aus sechs Inseln? 14.Welches ist die Inselgruppe mit den meisten Inseln? 15.Wieviele Protestanten gibt es auf der Welt? 16.Gib die Religionen zusammen mit ihren Anhängern, absteigend sortiert nach Anzahl Anhänger aus 17.Welche Religion hat die geringste Anzahl Anhänger? 18.Wie heißt der höchste Berg Amerikas? 19.Ordne jedem See die am ehesten von der Größe her passende Insel zu und gib die Paare aus. 20.Transformiere die Mondial-Datenbank40 von natürlichen Primärschlüsseln nach künstlichen Primärschlüsseln. 1.10 Frei verfügbare Software Es gibt eine Reihe von frei verfügbaren relationalen Datenbanken, Entwicklungswerkzeuge und Frontends dazu. Vor allem PostgreSQL und MySQL haben inzwischen eine große Verbreitung gefunden und sind mit kommerziellen Systemen durchaus vergleichbar. Da aber gerade im Umfeld der Frontends und Werkzeuge immer neue Produkte erscheinen und alte nicht weiter unterstützt werden, wird unter [Smi05] eine aktuell gehaltene Übersicht gegeben. 1.11 Bibliografie und Webliografie Allgemeine Literatur A. Heuer, G. Saake: Datenbanken: Konzepte und Sprachen; mitp-Verlag; 2. Auflage, 2000. Kevin Kline, Daniel Kline: SQL in a Nutshell; O’Reilly, 2001. A. Silberschatz, H. F. Korth, S. Sudarshan: Database Systems Concepts; McGraw-Hill, 4. Auflage, 2001 Spezielle Literatur [Cod70] E.F. Codd: A Relational Model of Data for Large Shared Data Banks; CACM 13, No. 6, June 1970. [Dat05] C.J. Date: Database In Depth - Relational Theory for Practioners. O’Reilly, 2005. [DR90] M. Dürr und K. Radermacher: Einsatz von Datenbanksystemen. Berlin, Heidelberg: Springer-Verlag, 1990. 40 am Beispiel der Tabellen country und city 36 1 SQL [Fri00] Jeffrey Friedl: Mastering Regular Expressions; O’Reilly, 2002. [Gen04] J. Gennick: SQL kurz & gut. O’Reilly, 2004. [GR92] J. Gray, A. Reuter: Transaction Processing : Concepts and Techniques; Morgan Kaufmann, 1992 [GUW99] H. Garcia-Molina, J.D. Ullman, J.D. Widom: Database System Implementation, Prentice Hall, 1999 [Her03] M. J. Hernandez: Database Design for Mere Mortals, Addison-Wesley, 2003. [HS00] A. Heuer, G. Saake: Datenbanken: Konzepte und Sprachen; mitp-Verlag; 2. Auflage, 2000. [KK01] Kevin Kline, Daniel Kline: SQL in a Nutshell; O’Reilly, 2001. [Mil03] Cary Millsap: Optimizing Oracle Performance; O’Reilly, 2003 [SB02] D. Shasha, P. Bonnet: Database Tuning: Principles, Experiments, and Troubleshooting Techniques; Morgan Kaufmann, 2002 [SKS01] A. Silberschatz, H. F. Korth, S. Sudarshan: Database Systems Concepts; McGraw-Hill, 4. Auflage, 2001 [Mon99] W. May: Information Extraction and Integration with F LORID: The M ONDIAL Case Study, Universität Freiburg, Institut für Informatik, 1999 http://www.dbis.informatik.uni-goettingen.de/Mondial/ [Smi05] A. Schmidt: SQL, http://www.smiffy.de/sql/, 2005 [Tür03] C. Türker: SQL:1999 & SQL:2003 – Objektrelationales SQL, SQLJ & SQL/XML. dpunkt.verlag, 2003.