Kapitel 3 - Buecher.de

Werbung
Kapitel 3
Syntax und Verwendung von SQL
in MySQL
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
MySQL – was es hat und was es nicht hat
Namensregeln in MySQL
Datenbanken anlegen, verwerfen und auswählen
Tabellen anlegen, löschen, indizieren und bearbeiten
Informationen über Datenbanken und Tabellen ermitteln
Datensätze suchen
Kommentare
Lösungsvorschläge
Ausführung einer UNION-Operation
Funktionen, die MySQL nicht unterstützt
192
194
196
198
212
213
219
220
224
228
192
MySQL – was es hat und was es nicht hat
Für die Kommunikation mit dem MySQL-Server sollten Sie SQL fließend
beherrschen. Beispielsweise erlauben Ihnen die Funktionen des mysql-Clients,
SQL-Anweisungen zur Ausführung an den Server zu senden. Aber auch für die
Entwicklung von Programmen, die die von Ihrer Programmiersprache bereitgestellte MySQL-Schnittstelle nutzen, müssen Sie SQL beherrschen, weil Sie auch
hier mit dem Server kommunizieren, indem Sie ihm SQL-Anweisungen übergeben.
In Kapitel 1 finden Sie eine kurze Einführung in die Arbeitsweise von MySQL.
Das vorliegende Kapitel baut auf den dort bereitgestellten Informationen auf und
beschreibt einige der von MySQL implementierten SQL-Bereiche genauer. Hier
wird erklärt, wie man unter Berücksichtigung der Namensregeln und der Einschränkungen bei der Groß-/Kleinschreibung auf Elemente der Datenbank verweist. Außerdem werden einige der wichtigeren SQL-Anweisungen beschrieben,
beispielsweise zum Anlegen und Entfernen von Datenbanken, Tabellen und Indizes. Anweisungen für die Suche von Daten mit Hilfe von Joins sowie Anweisungen, die Informationen über Ihre Datenbanken und Tabellen liefern. Dabei werden insbesondere auch Erweiterungen vorgestellt, die MySQL zusätzlich zum
Standard-SQL unterstützt.
3.1
MySQL – was es hat und was es nicht hat
Die SQL-Anweisungen von MySQL können in mehrere Kategorien eingeordnet
werden, wie in Abbildung 3.1 gezeigt. In diesem Kapitel geht es um die Anweisungen aus den ersten vier der hier gezeigten Kategorien. Bei einigen der
MySQL-Dienstprogramme handelt es sich im Grunde um Kommandozeilenschnittstellen für bestimmte SQL-Anweisungen. Beispielsweise ist mysqlshow
eine Schnittstelle zur Anweisung SHOW COLUMNS. Solche Entsprechungen werden
in diesem Kapitel ebenfalls beschrieben.
Anweisungen, die hier keine Berücksichtigung finden, werden zum Teil in anderen Kapiteln beschrieben. Beispielsweise beschäftigt sich Kapitel 11 mit den
Anweisungen GRANT und REVOKE zur Einrichtung von Benutzerberechtigungen.
Die Aufrufsyntax für alle Anweisungen finden Sie in Anhang D. Darüber hinaus
sollten Sie im MySQL Reference Manual nach weiteren Informationen suchen,
insbesondere nach Änderungen in neueren MySQL-Versionen.
Der letzte Abschnitt des Kapitels beschreibt, was es in MySQL nicht gibt, d.h.,
welche Funktionen ihm fehlen. Dabei handelt es sich um Dinge, die in einigen
anderen Datenbanken unterstützt werden, nicht aber in MySQL. Unter anderem
sind das Sub-Selects, Transaktionen, referenzielle Integrität, Trigger, gespeicherte
Prozeduren und Sichten. Bedeutet das, dass MySQL gar kein »richtiges« Datenbanksystem ist? Einige Leute sind dieser Meinung, aber man sieht ja, dass das
Fehlen dieser Funktionen in MySQL viele Anwender nicht daran hindern konnte,
es einzusetzen. Viele Anwendungen brauchen nämlich diese Funktionalität gar
nicht. In anderen Fällen gibt es Möglichkeiten, fehlende Funktionen durch andere
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
193
zu ersetzen. Weil beispielsweise kein kaskadenförmiges Löschen möglich ist,
führen Sie beim Löschen von Datensätzen aus einer Tabelle eine zusätzliche
Anfrage in Ihrer Anwendung aus. Dass Transaktionen nicht unterstützt werden,
muss nicht unbedingt eine Rolle spielen. Statt dessen könnte es für Ihre Bedürfnisse ausreichen, wenn Sie die von MySQL gebotene Möglichkeit nutzen, Anweisungen durch Einschluss in LOCK TABLES- und UNLOCK TABLES-Anweisungen in
nicht zu unterbrechende Gruppen zusammenzufassen.
(Das eigentliche Problem ist hier nicht das Fehlen von Transaktionen, sondern
vielmehr das Fehlen eines automatischen Rückgängigmachens (Rollback) aller
Anweisungen, falls eine der Transaktionen fehlschlägt. Wenn Sie Anwendungen
einsetzen, in denen beispielsweise komplexe Finanztransaktionen auszuführen
sind, deren Anweisungen entweder vollständig oder überhaupt nicht ausgeführt
werden sollen, sollten Sie besser eine Datenbank mit Commit/Rollback-Fähigkeit
auswählen, wie beispielsweise Progress.) Einige Funktionen fehlen nur, weil sie
noch nicht implementiert worden sind. Beispielsweise gibt es zum Zeitpunkt der
Drucklegung dieses Buches noch keine Sub-Selects, aber für Version 3.24, die
Ihnen möglicherweise bereits vorliegt, sind sie bereits geplant.
Datenbanken anlegen, löschen und auswählen
CREATE DATABASE
DROP DATABASE
USE
Tabellen und Indexe erzeugen, ändern und löschen
ALTER TABLE
CREATE INDEX
CREATE TABLE
DROP INDEX
DROP TABLE
Informationen über Datenbanken, Tabellen und Anfragen erhalten
DESCRIBE
EXPLAIN
SHOW
Informationen aus Tabellen auswählen
SELECT
Informationen in Tabellen modifizieren
DELETE
INSERT
LOAD DATA
OPTIMIZE TABLE
REPLACE
UPDATE
Administrative Anweisungen
FLUSH
GRANT
KILL
REVOKE
Diverse Anweisungen
CREATE FUNCTION
DROP FUNCTION
LOCK TABLES
SET
UNLOCK TABLES
Abb. 3.1: Die in MySQL unterstützten SQL-Anweisungen
194
3.2
Namensregeln in MySQL
Namensregeln in MySQL
Jede SQL-Anweisung verweist in irgendeiner Form auf eine Datenbank oder
einen Datenbankeintrag. Dieser Abschnitt beschreibt die Syntaxregeln für Verweise auf Datenbanken, Tabellen, Spalten, Indizes und Aliase. Namen unterliegen
den Regeln zur Groß-/Kleinschreibung, die hier ebenfalls erklärt werden.
3.2.1
Verweise auf Datenbankelemente
Falls Sie für den Verweis auf Datenbankelemente Namen verwenden, sind Sie auf
die in Namen erlaubten Zeichen und die erlaubte Länge beschränkt. Das Format
der Namen hängt außerdem vom jeweiligen Kontext ab:
•
Erlaubte Zeichen in Namen. Bestehen aus alphanumerischen Zeichen des auf
dem Server verwendeten Zeichensatzes sowie den Zeichen _ und $. Namen
dürfen mit jedem zulässigen Zeichen beginnen, auch mit einer Ziffer. Sie dürfen jedoch nicht ausschließlich aus Ziffern bestehen, weil sie dann nicht mehr
von Zahlen unterschieden werden können. Einen Namen mit einer Ziffer zu beginnen ist eher ungewöhnlich. Falls Sie solche Namen verwenden, seien Sie
insbesondere bei Namen mit E oder e vorsichtig, weil diese Zeichen zu mehrdeutigen Ausdrücken führen können. 23e + 14 ist Spalte 23e plus 14, aber was
ist 23e+14? Ist es dasselbe oder ist es womöglich eine Zahl in wissenschaftlicher Notation?
•
Namenslänge. Namen für Datenbanken, Tabellen, Spalten und Indizes dürfen
bis zu 64 Zeichen umfassen. Aliasnamen können bis zu 256 Zeichen umfassen.
•
Namensqualifikationen. Um auf eine Datenbank zu verweisen, geben Sie einfach nur ihren Namen an:
USE db_name
SHOW TABLES FROM db_name
Um auf eine Tabelle zu verweisen, gibt es zwei Möglichkeiten. Ein voll qualifizierter Tabellenname besteht aus einem Datenbanknamen und einem Tabellennamen:
SHOW TABLES FROM db_name.tbl_name
SELECT * FROM db_name.tbl_name
Ein nicht weiter qualifizierter Tabellenname verweist auf eine Tabelle in der
(aktuellen) Standarddatenbank. Falls samp_db die Standarddatenbank ist, sind die
beiden folgenden Anweisungen äquivalent:
SELECT * FROM member
SELECT * FROM samp_db.member
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
195
Um auf eine Spalte zu verweisen, gibt es drei Möglichkeiten: voll qualifiziert,
teilweise qualifiziert oder nicht qualifiziert. Ein voll qualifizierter Name (also db_
name.tbl_name.spalten_name) beinhaltet alle Informationen. Ein teilweise qualifizierter Name (also tbl_name.spalten_name) verweist auf die im Kontext verwendete Tabelle. Die beiden folgenden Anfragen verweisen auf dieselben Spaltennamen, aber der Kontext in der FROM-Klausel gibt an, aus welcher Tabelle die
Spalten ausgewählt werden sollen:
SELECT last_name, first_name FROM president
SELECT last_name, first_name FROM members
In der Regel ist es nicht erforderlich, voll qualifizierte Namen anzugeben, obwohl
es natürlich auch nicht verboten ist. Wenn Sie mit einer USE-Anweisung eine
Datenbank auswählen, wird diese zur Standarddatenbank und in jedem nicht qualifizierten Tabellenverweis implizit angenommen. Wenn Sie eine SELECT-Anweisung verwenden, die nur auf eine einzige Tabelle verweist, wird diese Tabelle für
jeden Spaltenverweis innerhalb der Anweisung implizit vorausgesetzt. Namen
müssen nur qualifiziert werden, wenn eine Tabelle oder Datenbank nicht aus dem
Kontext erkannt werden kann. Hier einige Situationen, in denen es zu Mehrdeutigkeiten kommen könnte:
3.2.2
•
Anfragen, die auf Tabellen aus mehreren Datenbanken verweisen. Verweise
auf Tabellen, die sich nicht in der Standarddatenbank befinden, müssen im Format db_name.tbl_name erfolgen, so dass MySQL weiß, in welcher Datenbank
es die Tabelle suchen soll.
•
Anfragen, die eine Spalte aus mehreren Tabellen auswählen, wobei mehrere
Tabellen eine Spalte desselben Namens enthalten.
Groß-/Kleinschreibung in SQL-Anweisungen
Die Regeln zur Groß-/Kleinschreibung in SQL-Anweisungen variieren abhängig
von der Anweisungskomponente und auch davon, worauf Sie verweisen und unter
welchem Betriebssystem der Server ausgeführt wird:
•
SQL-Schlüsselwörter und Funktionsnamen. Bei Schlüsselwörtern und
Funktionsnamen wird die Groß-/Kleinschreibung nicht berücksichtigt. Sie
können groß, klein oder kombiniert geschrieben werden. Die folgenden Anweisungen sind äquivalent:
SELECT NOW()
select now()
sElEcT nOw()
•
Datenbank- und Tabellennamen. Datenbanken und Tabellen in MySQL entsprechen den Verzeichnissen und Dateien des auf dem Server-Host zu Grunde
liegenden Dateisystems. Die Groß-/Kleinschreibung für Datenbank- und Tabellennamen ist deshalb davon abhängig, wie das Betriebssystem auf diesem
196
Datenbanken anlegen, verwerfen und auswählen
Host Dateinamen behandelt. Ein Server unter UNIX berücksichtigt bei Datenbank -und Tabellennamen die Groß-/Kleinschreibung, unter Windows dagegen wird sie nicht berücksichtigt.
Achten Sie auf diese Eigenschaft, wenn Sie auf einem UNIX-Server eine
Datenbank einrichten, die möglicherweise irgendwann auf einen WindowsServer portiert wird. Wenn Sie zwei Tabellen namens abc und ABC anlegen,
können diese später auf der Windows-Maschine nicht mehr unterschieden
werden. Diese Situation können Sie vermeiden, indem Sie sich bei der Vergabe von Datenbank- und Tabellennamen für Groß- oder Kleinschreibung entscheiden. Das Portieren der Datenbank auf einen anderen Server bereitet dann
hinsichtlich der Namensschreibweise keine Probleme.
•
Spalten- und Indexnamen. Spalten- und Indexnamen berücksichtigen die
Groß-/Kleinschreibung nicht. Die folgenden Anfragen sind äquivalent:
SELECT name FROM student
SELECT NAME FROM student
SELECT nAmE FROM student
•
Aliasnamen. Aliase berücksichtigen die Groß-/Kleinschreibung. Sie können
einen Alias beliebig schreiben (Großbuchstaben, Kleinbuchstaben oder kombiniert), müssen aber überall in der Anfrage bei dieser Schreibweise bleiben.
Unabhängig davon, ob für einen Datenbank-, Tabellen- oder Aliasnamen zwischen Groß-und Kleinschreibung unterschieden wird, müssen Sie innerhalb einer
Anfrage bei einer einzigen Schreibweise bleiben. Das gilt nicht für SQL-Schlüsselwörter, Funktionsnamen oder Spalten- und Indexnamen, deren Schreibweise
innerhalb einer Anfrage variieren darf. Natürlich ist die Anfrage leichter lesbar,
wenn Sie eine konsistente Schreibweise verwenden, statt beliebig zu wechseln
(SelECt NamE FrOm ...).
3.3
Datenbanken anlegen, verwerfen und auswählen
MySQL unterstützt drei Anweisungen auf Datenbankebene: CREATE DATABASE
zum Anlegen von Datenbanken, DROP DATABASE zum Verwerfen von Datenbanken
und USE für die Auswahl einer Standarddatenbank.
3.3.1
Die Anweisung CREATE DATABASE
Es ist ganz einfach, eine Datenbank anzulegen: Sie geben sie einfach in einer CREATE DATABASE-Anweisung an:
CREATE DATABASE db_name
Der Name muss zulässig sein, die Datenbank darf noch nicht existieren, und Sie
müssen über die Berechtigung verfügen, sie anzulegen.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
3.3.2
197
Die Anweisung DROP DATABASE
Das Löschen einer Datenbank ist genau so einfach wie das Anlegen, vorausgesetzt, Sie besitzen die entsprechenden Berechtigungen:
DROP DATABASE db_name
Die Ausführung der Anweisung DROP DATABASE sollte wohl überlegt sein. Sie
löscht die Datenbank und alle darin enthaltenen Tabellen. Nachdem Sie eine
Datenbank gelöscht haben, ist sie für immer verloren. Mit anderen Worten, probieren Sie diese Anweisung nicht einfach aus, um zu sehen, wie sie funktioniert.
Falls Ihr Administrator regelmäßig Sicherungen vornimmt, können Sie die Datenbank möglicherweise wieder herstellen. Aber mit Sicherheit wird Ihr Administrator nicht erfreut sein, wenn Sie ihm sagen »Ah, ich habe ein bisschen mit DROP
DATABASE herumgespielt, um zu sehen, was passiert, und ... äh... könnten Sie die
Datenbank bitte für mich wiederherstellen?«
Beachten Sie, dass eine Datenbank als Verzeichnis im Datenverzeichnis dargestellt wird. Wenn Sie Dateien in diesem Verzeichnis ablegen, bei denen es sich
nicht um Datenbanktabellen handelt, werden diese durch die Anweisung DROP
DATABASE nicht gelöscht. In diesem Fall wird auch das eigentliche Datenbankverzeichnis nicht gelöscht.
3.3.3
Die Anweisung USE
Die Anweisung USE macht eine Datenbank zur (aktuellen) Standarddatenbank für
eine bestimmte Verbindung zum Server:
USE db_name
Sie brauchen Zugriffsberechtigungen für eine Datenbank, damit Sie sie auf diese
Weise auswählen können. Es ist nicht erforderlich, eine Datenbank auszuwählen,
um ihre Tabellen nutzen zu können, weil Sie auch in der Form db_name.tbl_name
darauf verweisen können. Es ist jedoch viel bequemer, auf Tabellen zu verweisen,
ohne den Datenbanknamen dafür angeben zu müssen.
Die Auswahl einer Standarddatenbank bedeutet nicht, dass diese während der
gesamten Verbindung die Standarddatenbank bleiben muss. Sie können beliebig
viele USE-Anweisungen ausführen und zwischen den Datenbanken wechseln, falls
Sie die entsprechenden Zugriffsberechtigungen besitzen. Außerdem werden Sie
durch die Auswahl einer Standarddatenbank nicht auf diese beschränkt, so dass
Sie nur noch Tabellen aus dieser Datenbank verwenden könnten. Mit Hilfe qualifizierter Tabellennamen können Sie weiterhin auf Tabellen in anderen Datenbanken zugreifen.
Nachdem eine Verbindung zum Server abgebaut worden ist, vergisst dieser, welche Standarddatenbank verwendet wurde. Wenn Sie also eine neue Verbindung
einrichten, weiß der Server nicht mehr, welche Datenbank Sie zuvor benutzt
198
Tabellen anlegen, löschen, indizieren und bearbeiten
haben. Das wäre auch nicht besonders sinnvoll, weil MySQL Multithreading
unterstützt und mehrere Verbindungen von einem einzigen Benutzer verwalten
kann, der diese in beliebiger Reihenfolge einrichten und abbauen kann. In dieser
Umgebung wäre nicht klar, was »die zuvor ausgewählte Datenbank« bedeuten
könnte.
3.4
Tabellen anlegen, löschen, indizieren und bearbeiten
Mit Hilfe der Anweisungen CREATE TABLE, DROP TABLE und ALTER TABLE können
Sie in MySQL Tabellen anlegen, löschen und ihre Struktur ändern. Für diese
Anweisungen gibt es MySQL-spezifische Erweiterungen, die sie noch praktischer
machen. Die Anweisungen CREATE INDEX und DROP INDEX ermöglichen Ihnen,
Indizes für bereits existierende Tabellen anzulegen oder zu entfernen.
3.4.1
Die Anweisung CREATE TABLE
Mit CREATE TABLE werden Tabellen angelegt. Die vollständige Syntax für diese
Anweisung ist relativ kompliziert, weil es so viele Optionen gibt, aber in der Praxis ist ihre Anwendung relativ einfach. Beispielsweise waren alle in Kapitel 1
gezeigten CREATE TABLE-Anweisungen völlig unkompliziert.
Ironischerweise entsteht ein Großteil dieser zusätzlichen Komplexität durch die
Klauseln, die MySQL zuerst parst und dann verwirft! Mehr darüber erfahren Sie
in Anhang D. Lesen Sie im Abschnitt über CREATE TABLE nach und beachten Sie,
wie viel Syntax den Klauseln REFERENCES, CONSTRAINT und CHECK gewidmet ist.
Diese Klauseln betreffen Fremdschlüssel, referenzielle Integrität und Eingabewertbeschränkungen. MySQL unterstützt diese Funktionen nicht, parst aber die
Syntax, damit Sie Tabellendefinitionen, die in anderen Datenbanksystemen angelegt wurden, einfacher nutzen können. (Sie können diesen Code einfacher nutzen
und müssen weniger Änderungen daran vornehmen.) Wenn Sie Ihre eigenen,
ganz neuen Tabellenbeschreibungen entwickeln, können Sie diese Klauseln vollständig vergessen. Ich werde sie in diesem Abschnitt mit keinem Wort mehr
erwähnen.
CREATE TABLE muss mindestens den Tabellennamen sowie eine Spaltenliste ange-
ben, z.B.:
CREATE TABLE my_table
(
name CHAR(20),
age INT NOT NULL,
weight INT,
sex ENUM('F','M')
)
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
199
Neben den Spalten der Tabelle können Sie auch angeben, ob ein Index für die
Tabelle angelegt werden soll. Sie brauchen den Index jedoch nicht beim Anlegen
der Tabelle einzurichten, sondern können ihn später ergänzen. Das ist sinnvoll,
wenn Sie die Tabelle erst mit sehr vielen Daten füllen wollen, bevor Sie Anfragen
dafür ausführen. Die Indizes beim Einfügen der einzelnen Daten zu aktualisieren
ist viel zeitaufwändiger, als die Daten in eine nicht-indizierte Tabelle zu laden und
den Index dafür später anzulegen.
Kapitel 1 hat die grundlegende Syntax für die Anweisung CREATE TABLE bereits
erklärt. Kapitel 2 hat sich mit den Spaltentypen beschäftigt. Ich setze voraus, dass
Sie diese Kapitel gelesen haben, werde diese Dinge hier also nicht wiederholen.
Statt dessen geht es im restlichen Abschnitt um einige wichtige Erweiterungen
der Anweisung CREATE TABLE, die in MySQL 3.23 eingeführt wurden, und die
Ihnen beim Anlegen von Tabellen eine ausreichende Flexibilität bieten:
•
Angaben zum Speichertyp der Tabelle
•
Anlegen der Tabelle nur dann, falls diese noch nicht existiert
•
Temporäre Tabellen, die automatisch verworfen werden, nachdem die ClientSitzung beendet ist
•
Die Möglichkeit, eine Tabelle einfach durch Auswahl der darin darzustellenden Daten zu erzeugen
Bezeichner für den Tabellenspeichertyp
Vor MySQL 3.23 verwendeten alle vom Benutzer angelegten Tabellen die Speichermethode ISAM. In MySQL 3.23 können Sie explizit Tabellen mit drei unterschiedlichen Typen anlegen, indem Sie hinter der Spaltenlistenkomponente der
CREATE TABLE-Anweisung TYPE = typ angeben. typ kann MYSIAM, ISAM oder HEAP
sein, beispielsweise:
CREATE TABLE my_tbl (i INT, c CHAR(20)) TYPE = HEAP
Mit ALTER TABLE können Tabellen von einem Typ in einen anderen umgewandelt
werden:
ALTER TABLE my_tbl TYPE = ISAM
ALTER TABLE my_tbl TYPE = MYISAM
ALTER TABLE my_tbl TYPE = HEAP
Es ist wahrscheinlich nicht sinnvoll, eine Tabelle in den Typ HEAP umzuwandeln,
falls diese Ihren nächsten Serverabsturz überleben soll. HEAP-Tabellen werden
im Arbeitsspeicher verwaltet und gehen verloren, sobald der Server heruntergefahren wird oder ausfällt.
200
Tabellen anlegen, löschen, indizieren und bearbeiten
Diese drei Tabellentypen haben die folgenden Eigenschaften:
•
MyISAM-Tabellen. Das Speicherformat MyISAM ist seit Version 3.23 Standard in MySQL:
– Die Dateien können größer sein als bei der ISAM-Speichermethode, falls
Ihr Betriebssystem größere Dateien erlaubt.
– Daten können in einem maschinenunabhängigen Format gespeichert werden, wobei das unterste Byte am Anfang steht. Das bedeutet, Sie können
Tabellen von einer Maschine auf eine andere kopieren, auch wenn beide
unterschiedliche Architekturen aufweisen.
– Numerische Indexwerte belegen weniger Speicher, weil dabei das oberste
Byte zuerst abgelegt wird. Indexwerte sind in der Regel in den unteren
Bytes schneller, deshalb unterliegen die oberen Bytes eher einer Komprimierung.
– Die AUTO_INCREMENT-Verarbeitung ist besser als für ISAM-Tabellen. Die
Details werden in Kapitel 2 im Abschnitt »Folgen« erklärt.
– Einige Einschränkungen hinsichtlich des Index wurden gelockert. Beispielsweise können Sie einen Index für Spalten mit NULL-Werten und auch
für BLOB- und TEXT-Typen anlegen.
– Für eine verbesserte Integritätsprüfung der Tabellen wurde ein Flag eingeführt, das gesetzt wird, wenn die Tabelle von myisamchk überprüft wird. Mit
myisamchk -fast überspringen Sie Überprüfungen für Tabellen, die seit der
letzten Überprüfung nicht geändert wurden, wodurch administrative Aufgaben beschleunigt werden. Die Tabellen weisen außerdem ein Flag auf,
das angibt, ob sie korrekt geschlossen wurden. Wird der Server nicht korrekt heruntergefahren oder stürzt die Maschine ab, kann das Flag genutzt
werden, um Tabellen zu erkennen, die beim Neustarten des Servers überprüft werden müssen.
•
ISAM-Tabellen. Das Speicherformat ISAM ist das vor MySQL 3.23 verwendete Format, das heute jedoch weiterhin zur Verfügung steht. Im Allgemeinen
sollten statt ISAM besser MyISAM-Tabellen verwendet werden, weil sie weniger Einschränkungen aufweisen. Irgendwann werden ISAM-Tabellen nicht
mehr unterstützt, weil sie durch das MyISAM-Format ersetzt werden.
•
HEAP-Tabellen. Das Speicherformat HEAP erzeugt Tabellen im Speicher,
die Zeilen fester Länge aufweisen und damit sehr schnell sind. Außerdem sind
sie flüchtig, das heißt, sie gehen verloren, sobald der Server heruntergefahren
wird oder ausfällt. Im Gegensatz zu temporären Tabellen, die mit CREATE TEMPORARY TABLE angelegt werden, sind HEAP-Tabellen für andere Clients sichtbar. HEAP-Tabellen weisen einige Einschränkungen auf, die es für MyISAMund ISAM-Tabellen nicht gibt:
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
201
– Indizes werden nur für Vergleiche mit = und <=>verwendet.
– Für indizierte Spalten sind keine NULL-Werte erlaubt.
– Es sind keine BLOB- und TEXT-Spalten erlaubt.
– Es können keine AUTO_INCREMENT-Spalten verwendet werden.
Vorsorgliches Anlegen von Tabellen
Um eine Tabelle nur dann anzulegen, wenn sie noch nicht existiert, verwenden Sie
CREATE TABLE IF NOT EXISTS. Diese Anweisung ist sinnvoll für Anwendungen, die
nicht davon ausgehen, dass die von ihnen benötigten Tabellen bereits angelegt wurden, und die deshalb einfach immer versuchen, diese zu erstellen. Die Klausel IF
NOT EXISTS ist insbesondere für Scripts praktisch, die Sie mit mysql als Batch-Jobs
ausführen. In diesem Kontext funktioniert eine normale CREATE TABLE-Anweisung
nicht wie gewünscht. Bei der ersten Ausführung der Anwendung werden alle Tabellen korrekt angelegt, aber bei der zweiten Ausführung tritt ein Fehler auf, weil
die Tabellen bereits existieren. Bei der Angabe von IF NOT EXISTS gibt es kein Problem. Bei der ersten Ausführung der Anwendung werden die Tabellen wie zuvor
angelegt. Bei der zweiten und jeder folgenden Ausführung können die Tabellen
zwar wiederum nicht angelegt werden, aber es wird kein Fehler erzeugt. Auf diese
Weise kann die Ausführung fortgesetzt werden, nachdem der Versuch, die Tabellen
anzulegen, fehlgeschlagen ist.
Temporäre Tabellen
Mit CREATE TEMPORARY TABLES legen Sie temporäre Tabellen an, die nach Beenden
der Sitzung automatisch entfernt werden. Das ist praktisch, weil Sie dann nicht
jedes Mal explizit eine DROP TABLE-Anweisung ausführen müssen, um die Tabelle
löschen, und die Tabelle bleibt nicht zurück, falls Ihre Sitzung nicht ordnungsgemäß beendet wird. Wenn Sie beispielsweise eine Anfrage in einer Datei abgelegt
haben, die Sie mit mysql ausführen, und nicht auf das Beenden warten wollen,
können Sie das Script während der Ausführung problemlos unterbrechen, und der
Server entfernt alle temporären Tabellen.
In älteren Versionen von MySQL gab es keine echten temporären Tabellen, außer
in der Hinsicht, dass Sie sie für sich selbst als temporär betrachten konnten. In
Anwendungen, die solche Tabellen benötigen, müssen Sie darauf achten, sie
irgendwann zu löschen. Falls Sie das vergessen und in dem Client ein Fehler auftritt, der zum vorzeitigen Beenden führt, bleibt die temporäre Tabelle erhalten, bis
irgend jemand das bemerkt und sie entfernt.
Temporäre Tabellen sind nur für die Clients sichtbar, die sie angelegt haben. Sie
können dieselben Namen wie bereits existierende permanente Tabellen haben.
Das ist kein Fehler, und die permanente Tabelle wird dadurch auch nicht gestört.
Angenommen, Sie legen in der Datenbank samp_db die temporäre Tabelle member
an. Die ursprüngliche member-Tabelle wird verborgen (man kann nicht mehr darauf zugreifen) und verweist auf member als temporäre Tabelle. Wenn Sie jetzt die
202
Tabellen anlegen, löschen, indizieren und bearbeiten
Anweisung DROP TABLE member ausführen, wird die temporäre Tabelle entfernt und
die ursprüngliche member-Tabelle erscheint wieder. Falls Sie die Verbindung zum
Server abbauen, ohne die temporäre Tabelle zu entfernen, erledigt der Server das
automatisch für Sie. Wenn Sie das nächste Mal eine Verbindung einrichten, ist die
member-Tabelle wieder sichtbar.
Der Mechanismus, Namen zu verbergen, funktioniert nur auf einer Ebene. Das
bedeutet, dass Sie nicht zwei temporäre Tabellen desselben Namens anlegen können.
Tabellen aus SELECT-Ergebnissen anlegen
Eines der Schlüsselkonzepte relationaler Datenbanken ist, dass alles als Tabelle
mit Zeilen und Spalten dargestellt wird, und dass das Ergebnis jedes SELECT ebenfalls wieder eine Tabelle mit Zeilen und Spalten ist. Häufig ist die aus einem
SELECT resultierende »Tabelle« einfach nur ein Konstrukt aus Zeilen und Spalten,
die nach oben geblättert werden, sobald Sie weiterarbeiten. Vor MySQL 3.23
mussten für das Speichern der SELECT-Ergebnisse für weitere Anfragen spezielle
Vorkehrungen getroffen werden:
1. Ausführung einer DESCRIBE- oder SHOW COLUMNS-Anfrage, die die Typen der
Tabellenspalten ermittelt, aus denen Sie Informationen erhalten wollen.
2. Anlegen einer Tabelle, wobei explizit die Namen und Typen der oben ermittelten Spalten angegeben werden.
3. Ausführung einer INSERT...SELECT-Anfrage nach dem Anlegen der Tabellen,
um die Ergebnisse zu ermitteln und sie in die Tabelle einzufügen.
Mit MySQL 3.23 hat sich alles geändert. Durch die Anweisung CREATE
TABLE...SELECT werden alle diese Dinge überflüssig, und aus den Ergebnissen
beliebiger SELECT-Anfragen können dynamisch neue Tabellen angelegt werden.
Das erfolgt innerhalb eines einzigen Schritts, ohne dass Sie die Datentypen der
betreffenden Spalten kennen oder angeben müssen. Das macht es ganz einfach,
eine Tabelle mit all den Daten anzulegen, die Sie brauchen und die dann sofort in
weiteren Anfragen weiterverwendet werden können.
Sie können eine Tabelle kopieren, indem Sie den gesamten Inhalt markieren
(keine WHERE-Klausel), oder Sie legen eine leere Kopie an, indem Sie eine WHEREKlausel hinzufügen, die immer fehlschlägt:
CREATE TABLE new_tbl_name SELECT * FROM tbl_name
CREATE TABLE new_tbl_name SELECT * FROM tbl_name WHERE 1 = 0
Es ist dann sinnvoll, eine leere Kopie anzulegen, wenn Sie mit LOAD DATA eine
Datendatei in die ursprüngliche Datei laden wollen, aber nicht sicher sind, ob die
Optionen für das Dateiformat korrekt sind. Schließlich wollen Sie nicht die
Datensätze Ihrer Originaldatei zerstören, nur weil Sie die Optionen beim ersten
Mal nicht richtig angegeben haben! Mit Hilfe einer leeren Kopie der Original-
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
203
tabelle können Sie mit den Optionen für LOAD DATA experimentieren, um Spalten
und Zeilen auszuwählen, bis Sie davon überzeugt sind, dass Ihre Eingabedaten
korrekt interpretiert werden. Anschließend können Sie die Daten in die Originaltabelle laden.
Sie können CREATE TEMPORARY mit SELECT kombinieren, um eine temporäre
Tabelle als Kopie ihrer selbst anzulegen:
CREATE TEMPORARY TABLE my_tbl SELECT * FROM my_tbl
Auf diese Weise können Sie den Inhalt von my_tbl ändern, ohne dass das Original
dadurch beeinflusst wird. Das ist praktisch, wenn Sie Anfragen ausprobieren wollen, die den Inhalt der Tabelle ändern, ohne dabei jedoch sofort die Originaltabelle zu ändern. Vorgefertigte Scripts, die den Namen der Originaltabelle angeben, brauchen nicht geändert zu werden, um auf eine andere Tabelle zu
verweisen; fügen Sie einfach nur die Anweisung CREATE TEMPORARY TABLE am
Anfang des Scripts ein. Das Script legt eine temporäre Kopie an und arbeitet mit
dieser. Nachdem das Script abgearbeitet ist, löscht der Server die Kopie.
Um eine Tabelle als leere Kopie von sich selbst anzulegen, verwenden Sie die
Klausel WHERE 0 in Kombination mit CREATE TEMPORARY...SELECT:
CREATE TEMPORARY TABLE my_tbl SELECT FROM my_tbl WHERE 1 = 0
Beim dynamischen Anlegen von Tabellen müssen jedoch einige problematische
Aspekte berücksichtigt werden. Wenn Sie eine Tabelle anlegen, indem Sie Daten
aus einer Anfrage hineinkopieren, werden die Spaltennamen von den betreffenden Spalten übernommen. Wird eine Spalte als Ergebnis eines Ausdrucks berechnet, ist der »Name« der Spalte der Text des Ausdrucks. Ein Ausdruck ist kein
zulässiger Spaltenname. Das erkennen Sie bei der Ausführung der folgenden
Anfrage in mysql:
mysql> CREATE TABLE my_tbl SELECT 1;
ERROR 1166 at line 1: Incorrect column name '1'
Damit das funktioniert, geben Sie einen Spaltenalias an, so dass die Spalte einen
zulässigen Namen erhält:
mysql> CREATE TABLE my_tbl SELECT 1 AS my_col;
Query OK, 1 row affected (0.01 sec)
Ein ähnliches Problem tritt auf, wenn Sie Spalten aus unterschiedlichen Tabellen,
aber mit gleichem Namen auswählen. Angenommen, in den Tabellen t1 und t2
gibt es jeweils eine Spalte c, und Sie wollen eine Tabelle aus allen Zeilenkombinationen aus beiden Tabellen anlegen. Um eindeutige Namen in der neuen Tabelle
anzulegen, stellen Sie Aliase bereit:
CREATE TABLE t3 SELECT t1.c AS c1, t2.c AS c2 FROM t1, t2;
204
Tabellen anlegen, löschen, indizieren und bearbeiten
Beim Anlegen einer Tabelle über das Kopieren von Daten werden die Indizes der
Originaltabelle nicht automatisch kopiert.
3.4.2
Die Anweisung DROP TABLE
Es ist viel einfacher, eine Tabelle zu verwerfen, als sie anzulegen, weil Sie dabei
nichts zu ihrem Inhalt angeben müssen; Sie geben sie einfach nur an:
DROP TABLE tbl_name
MySQL hat die Anweisung DROP TABLE erweitert, wodurch einige praktische
Anwendungen möglich sind. Erstens können Sie mehrere Tabellen gleichzeitig
innerhalb einer einzigen Anweisung verwerfen:
DROP TABLE tbl_name1, tbl_name2, ...
Zweitens: Wenn Sie nicht sicher sind, ob eine Tabelle existiert, diese jedoch gegebenenfalls löschen wollen, fügen Sie der Anweisung die Klausel IF EXISTS hinzu.
Auf diese Weise beschwert sich MySQL nicht und gibt keinen Fehler aus, falls
der in der Anweisung verwendete Tabellenname nicht existiert:
DROP TABLE IF EXISTS tbl_name
IF EXISTS ist nützlich in Scripts, die Sie im Client-Programm mysql ausführen,
weil dieses beim Auftreten eines Fehlers automatisch beendet wird und den Versuch, eine nicht existente Tabelle zu entfernen, als Fehler wertet. Beispielsweise
könnten Sie ein Setup-Script einsetzen, das die Tabellen als Grundlage für die
weitere Verarbeitung in anderen Scripts anlegt. In dieser Situation werden Sie
sicherstellen wollen, dass das Setup-Script beim Aufruf eine saubere Ausgangsposition vorfindet. Wenn Sie am Anfang des Scripts regelmäßig die Anweisung
DROP TABLE ausführen, schlägt diese beim ersten Mal fehl, weil die Tabellen noch
nicht angelegt wurden. Durch IF EXISTS wird dieses Problem umgangen. Falls die
Tabellen existieren, werden sie entfernt, andernfalls wird das Script einfach fortgesetzt.
3.4.3
Indizes anlegen und verwerfen
Indizes beschleunigen den Zugriff auf Ihren Tabelleninhalt, insbesondere für
Anfragen mit Joins über mehrere Tabellen. Dies ist ein wichtiges Thema, das in
Kapitel 4 genauer besprochen wird, in dem es um Indizes und ihre Funktionsweise geht und wie Sie sie für die Optimierung Ihrer Anfragen am besten nutzen.
In diesem Abschnitt geht es um die Eigenschaften von Indexen und um die Syntax, mit der sie angelegt und gelöscht werden.
Indexeigenschaften
MySQL bietet eine relativ hohe Flexibilität beim Anlegen von Indexen. Der Index
kann für eine oder mehrere Spalten angelegt werden. Außerdem können Sie für
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
205
eine Spalte mehrere Indizes einführen, wenn Sie Werte aus unterschiedlichen
Spalten einer Tabelle schnell laden wollen. Hat eine Spalte einen Zeichenkettenoder ENUM- oder SET-Typ, können Sie nur die linken n Zeichen der Spalte mit
einem Index belegen. Ist die Spalte über die ersten n Zeichen eher eindeutig,
opfern Sie keine Leistung, sondern verbessern sie sogar: Durch die Indizierung
eines Spaltenpräfix anstelle der gesamten Spalte machen Sie den Index und damit
den Zugriff schneller.
Es gibt zwar auch einige Einschränkungen hinsichtlich des Index, die jedoch mit
der Weiterentwicklung von MySQL immer geringer geworden sind. Die folgende
Tabelle zeigt einige der Unterschiede zwischen ISAM- und MyISAM-Tabellen in
Hinblick auf die Indizierungsmöglichkeiten:
Indexeigenschaften
ISAM-Tabellen
MyISAM-Tabellen
NULL-Werte
Nicht erlaubt
Erlaubt
BLOB- und TEXT-Spalten
Können nicht indiziert
werden
Können indiziert
werden
Indizes pro Tabelle
16
32
Spalten pro Index
16
16
Maximale Zeilengröße für den Index
256 Byte
500 Byte
Aus dieser Aufstellung erkennen Sie, dass für ISAM-Tabellen indizierte Spalten
als NOT NULL deklariert werden müssen und keine BLOB- oder TEXT-Spalten sein dürfen. Für den Tabellentyp MyISAM wurden diese Einschränkungen aufgehoben
und andere gelockert. Eine Auswirkung dieser Unterschiede bezüglich der Indexeigenschaften der beiden Tabellentypen ist, dass Sie abhängig von Ihrer MySQLVersion unter Umständen einfach nicht in der Lage sind, einen Index für
bestimmte Spalten anzulegen. Beispielsweise können Sie in MySQL-Versionen
vor 3.23 nur ISAM-Tabellen anlegen, d.h. Spalten nur dann indizieren, wenn
keine NULL-Werte darin vorkommen dürfen.
Falls Sie MySQL 3.23 oder neuer einsetzen, haben Sie vielleicht ältere Tabellen,
die ursprünglich als ISAM-Tabellen angelegt wurden. Mit ALTER TABLE wird ihr
Speicherformat ganz einfach in MyISAM geändert, so dass Sie einige der neueren
Indexfunktionen nutzen können:
ALTER TABLE tbl_name TYPE = MYISAM
Indizes anlegen
Indizes für eine neue Tabelle werden bei Ausführung von CREATE TABLE angelegt
oder mit CREATE INDEX oder ALTER TABLE einer bereits existierenden Tabelle hinzugefügt. CREATE INDEX wurde in MySQL 3.22 eingeführt, aber Sie können auch
ALTER TABLE benutzen, falls Sie eine ältere MySQL-Version einsetzen. (MySQL
bildet momentan CREATE INDEX intern auf ALTER TABLE ab.)
206
Tabellen anlegen, löschen, indizieren und bearbeiten
Sie können festlegen, ob der Index doppelte Werte beinhalten darf. Wenn das
nicht der Fall ist, sollte der Index als PRIMARY KEY oder UNIQUE erzeugt werden.
Für einen eindeutigen Index über eine einzige Spalte wird damit sichergestellt,
dass die Spalte keine doppelten Werte enthält. Für einen eindeutigen Index über
mehrere Spalten wird sichergestellt, dass keine Kombination der betreffenden
Werte doppelt vorkommt.
Die als PRIMARY KEY und UNIQUE angelegten Indizes sind sehr ähnlich. Ein PRIMARY
KEY ist einfach nur ein UNIQUE-Index mit dem Namen PRIMARY. Das bedeutet, eine
Tabelle kann nur einen PRIMARY KEY enthalten, weil es nicht zwei Indizes mit demselben Namen geben darf. Sie können einer Tabelle jedoch mehrere UNIQUE-Indizes zuordnen, auch wenn das eher unüblich ist.
Um einer existierenden Tabelle einen Index zuzuordnen, verwenden Sie ALTER
TABLE oder CREATE INDEX. ALTER TABLE ist die flexiblere Anweisung, weil Sie
damit einen normalen Index, einen UNIQUE-Index oder einen PRIMARY KEY anlegen
können:
ALTER TABLE tbl_name ADD INDEX index_name (spalten_liste)
ALTER TABLE tbl_name ADD UNIQUE index_name (spalten_liste)
ALTER TABLE tbl_name ADD PRIMARY KEY (spalten_liste)
tbl_name ist der Name der Tabelle, der der Index hinzugefügt werden soll, und
spalten_liste gibt an, welche Spalten dabei berücksichtigt werden sollen. Besteht
der Index aus mehreren Spalten, trennen Sie diese durch Kommata voneinander ab.
Der Indexname index_name ist optional, Sie können ihn also weglassen. MySQL
wählt dann einen Namen abhängig von der ersten indizierten Spalte aus. ALTER TABLE erlaubt die Angabe mehrerer Tabellenänderungen innerhalb einer einzigen Anweisung, so dass Sie mehrere Indizes gleichzeitig anlegen können.
CREATE INDEX fügt einer Tabelle einen normalen oder einen UNIQUE-Index hinzu:
CREATE UNIQUE INDEX index_name ON tbl_name (spalten_liste)
CREATE INDEX index_name ON tbl_name (spalten_liste)
tbl_name, index_name und spalten_liste haben dieselbe Bedeutung wie für
ALTER TABLE. Der Indexname ist hier jedoch nicht optional. Es ist nicht möglich,
mit CREATE INDEX einen PRIMARY KEY anzulegen.
Um bei der Ausführung der Anweisung CREATE TABLE einen Index für eine neue
Tabelle anzulegen, verwenden Sie eine ähnliche Syntax wie für ALTER TABLE,
geben jedoch als Teil der Anweisung, in der die Tabellenspalten deklariert werden, die Klausel für das Erzeugen des Index an:
CREATE TABLE tbl_name
(
...
INDEX index_name (column_list),
UNIQUE index_name (column_list),
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
207
PRIMARY KEY (column_list),
...
)
Wie bei ALTER TABLE ist auch für INDEX und UNIQUE die Angabe des Indexnamens
optional, und MySQL erlaubt Ihnen, später einen Namen zuzuweisen oder ihn
ganz wegzulassen.
Als Sonderfall können Sie auch einen einspaltigen Primärschlüssel anlegen,
indem Sie am Ende der Spaltendeklaration PRIMARY KEY einfügen:
CREATE TABLE my_tbl
(
i INT NOT NULL PRIMARY KEY
)
Diese Anweisung ist äquivalent zur folgenden:
CREATE TABLE my_tbl
(
i INT NOT NULL,
PRIMARY KEY (i)
)
Jedes dieser Beispiele, in denen Tabellen angelegt werden, hat für die indizierten
Spalten NOT NULL vorgegeben. Für ISAM-Tabellen ist das eine Bedingung, weil
hier keine Indizes für Spalten mit NULL-Werten angelegt werden können. Für
MyISAM-Tabellen dürfen indizierte Spalten NULL-Werte enthalten, so lange der
Index kein PRIMARY KEY ist, also kein Primärschlüssel.
Wenn Sie einen Index für ein Präfix oder eine Zeichenkettenspalte anlegen (die
linken n Zeichen der Spaltenwerte), gilt für die Benennung der Spalte in einem
spalten_liste-Bezeichner die Syntax spalten_name(n) und nicht bloß spalten_
name. Die erste der folgenden Anweisungen beispielsweise erzeugt eine Tabelle
mit zwei CHAR-Spalten und einem Index, der beide Spalten verwendet. Die zweite
Anweisung ist ähnlich, erzeugt aber einen Index für ein Präfix jeder Spalte:
CREATE TABLE my_tbl
(
name CHAR(30),
address CHAR(60),
INDEX (name,address)
)
CREATE TABLE my_tbl
(
name CHAR(30),
address CHAR(60),
INDEX (name(10),address(20))
)
208
Tabellen anlegen, löschen, indizieren und bearbeiten
In bestimmten Situationen kann es erforderlich sein, einen Index für ein Spaltenpräfix anzulegen. Beispielsweise ist die Länge von Indexzeilen nach oben hin
begrenzt, und man muss Präfixe verwenden, falls die Länge der indizierten Spalten diese Obergrenze überschreitet. Präfixe werden auch für BLOB- und TEXT-Spalten in MyISAM-TabellenIndexe benötigt.
Durch die Indizierung eines Spaltenpräfix werden spätere Änderungen an der
Spalte eingeschränkt; Sie können die Spalte nicht kürzer machen als die Präfixlänge, ohne den Index zu verlieren und ihn später mit einem kürzeren Präfix neu
anlegen zu müssen.
Indizes löschen
Indizes werden mit den Anweisungen DROP INDEX oder ALTER TABLE gelöscht. DROP
INDEX wurde mit MySQL 3.22 eingeführt und wird intern momentan behandelt
wie ALTER TABLE. Die Syntax für die Anweisungen zum Löschen eines Index sieht
wie folgt aus:
DROP INDEX index_name ON tbl_name
ALTER TABLE tbl_name DROP INDEX index_name
ALTER TABLE tbl_name DROP PRIMARY KEY
Die beiden ersten Anweisungen sind äquivalent. Die dritte wird nur verwendet,
wenn ein PRIMARY KEY gelöscht werden soll; in diesem Fall muss kein Indexname
angegeben werden, weil eine Tabelle nur einen einzigen Primärschlüssel haben
kann. Wurde kein Index explizit als PRIMARY KEY angelegt, verfügt die Tabelle
aber über einen oder mehrere UNIQUE-Indizes, löscht MySQL den ersten dieser
Indizes.
Wenn Sie Spalten einer Tabelle löschen, können davon auch die Indizes betroffen
sein. Wird eine Spalte gelöscht, die Teil eines Index ist, wird diese auch aus dem
Index gelöscht. Werden alle Spalten gelöscht, aus denen sich der Index zusammensetzt, wird der gesamte Index gelöscht.
3.4.4
Die Anweisung ALTER TABLE
ALTER TABLE ist eine sehr flexible Anweisung in MySQL, und Sie können damit
die unterschiedlichsten Dinge tun. Einige der Funktionen wurden bereits vorgestellt (beispielsweise das Anlegen und Löschen von Indexen oder die Umwandlung von Tabellen aus einem Speicherformat in ein anderes). In diesem Abschnitt
geht es um einige andere Fähigkeiten. Die vollständige Syntax von ALTER TABLE
ist in Anhang D beschrieben.
ALTER TABLE ist nützlich, wenn Sie feststellen, dass die Struktur einer Tabelle
nicht mehr ihrer Funktion entspricht. Möglicherweise wollen Sie zusätzliche
Informationen in der Tabelle ablegen, oder sie enthält überflüssige Informationen.
Vielleicht sind bereits existierende Spalten zu klein, oder Sie haben sie größer
deklariert, als es eigentlich notwendig ist, und Sie wollen sie verkleinern, um
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
209
Speicherplatz zu sparen und die Anfrageleistung zu verbessern. Oder vielleicht
haben Sie bei der Ausführung von CREATE TABLE einfach nur den Tabellennamen
falsch eingegeben. Hier einige Beispiele:
•
Sie führen eine web-basierte Umfrage aus und speichern die Ergebnisse als Datensätze in einer Tabelle. Anschließend wollen Sie die Umfrage um einige zusätzliche Fragen erweitern. Sie müssen der Tabelle neue Spalten hinzufügen,
um die Antworten auf die neuen Fragen ablegen zu können.
•
Sie führen ein Forschungsprojekt. Mit Hilfe einer AUTO_INCREMENT-Spalte weisen Sie den einzelnen Datensätzen Nummern zu. Anfangs gingen Sie davon
aus, dass Ihre Erhebungen über maximal 50000 Datensätze gingen, und haben
die Spalte mit dem Typ UNSIGNED SMALLINT angelegt, so dass sie maximal 65535
eindeutige Werte aufnehmen konnte. Das Projekt wurde neu dimensioniert,
und Sie benötigen weitere 50000 Datensätze. Sie müssen einen größeren Typ
verwenden, um mehr Nummern zuweisen zu können.
•
Der Faktor Größe kann sich auch in die andere Richtung ändern. Angenommen, Sie haben eine CHAR(255)-Spalte angelegt, erkennen aber jetzt, dass kein
Wert in der Tabelle länger als 100 Zeichen ist. Sie können die Spalte verkürzen, um Speicher zu sparen.
Die Syntax für ALTER TABLE sieht wie folgt aus:
ALTER TABLE tbl_name action,...
Jede action gibt eine Änderung an, die an der Tabelle vorgenommen werden soll.
MySQL hat die ALTER TABLE-Anweisung erweitert und erlaubt, mehrere Aktionen
anzugeben, die durch Kommata getrennt werden. Auf diese Weise ersparen Sie
sich Schreibarbeit, aber ein viel wichtigerer Grund für diese Erweiterung ist, dass
es unmöglich ist, Tabellen mit Zeilen variabler Länge in Tabellen fixer Länge
umzuwandeln, wenn nicht alle VARCHAR-Spalten gleichzeitig in CHAR-Spalten
umgewandelt werden können.
Die folgenden Beispiele demonstrieren einige andere Funktionen von ALTER
TABLE:
•
Umbenennung einer Tabelle. Das ist einfach; Sie geben dafür nur den alten
und den neuen Namen an:
ALTER TABLE tbl_name RENAME AS neuer_tbl_name
In MySQL 3.23, wo es temporäre Tabellen gibt, führt die Umbenennung einer
temporären Tabelle in einen in der Datenbank bereits existierenden Namen
dazu, dass die Originaltabelle verborgen bleibt, bis die temporäre Tabelle wieder gelöscht wird. Dasselbe passiert, wenn eine Tabelle verborgen wird, weil
eine temporäre Tabelle desselben Namens angelegt wird.
210
Tabellen anlegen, löschen, indizieren und bearbeiten
•
Ändern eines Spaltentyps. Der Spaltentyp wird in den Klauseln CHANGE oder
MODIFY geändert. Angenommen, die Spalte liegt in der Tabelle my_tbl als
SMALLINT UNSIGNED vor und Sie wollen einen MEIDUMINT UNSIGNED daraus
machen. Dazu führen Sie eine der folgenden Anweisungen aus:
ALTER TABLE my_tbl MODIFY i MEDIUMINT UNSIGNED
ALTER TABLE my_tbl CHANGE i i MEDIUMINT UNSIGNED
Warum wird die Spalte in der CHANGE-Anweisung zweimal angegeben? Weil
CHANGE im Gegensatz zu MODIFY in der Lage ist, die Spalte sowohl umzubenennen als auch den Typ zu ändern. Wollten Sie bei der Änderung des Typs die
Spalte gleichzeitig umbenennen, würden Sie folgendes schreiben:
ALTER TABLE my_tbl CHANGE i j MEDIUMINT UNSIGNED
Wichtig dabei ist, die Spalte anzugeben, die geändert werden soll, gefolgt von
einer vollständigen Spaltendeklaration inklusive Spaltenname. Außerdem
müssen Sie in der Deklaration den Namen angeben, auch wenn der alte Name
weiterverwendet wird.
Ein wichtiger Grund für die Änderung von Spaltentypen ist eine verbesserte
Anfrageeffizienz für Joins, die Spalten aus zwei Tabellen vergleichen. Ein
Vergleich ist schneller, wenn die verglichenen Spalten denselben Typ haben.
Angenommen, Sie führen die folgende Anfrage aus:
SELECT ... FROM t1, t2 WHERE t1.name = t2.name
Ist t1.name ein CHAR(10) und t2.name ein CHAR(15), erfolgt die Anfrage langsamer, als hätten beide den Typ CHAR(15). Sie gleichen sie an, indem Sie
t1.name mit einer der folgenden Anweisungen ändern:
ALTER TABLE t1 MODIFY name CHAR(15)
ALTER TABLE t1 CHANGE name name CHAR(15)
Für MySQL-Versionen vor 3.23 müssen über einen Join verbundene Spalten
denselben Typ haben, sonst können für den Vergleich keine Indizes verwendet
werden. Ab Version 3.23 können Indizes auch für ungleiche Typen verwendet
werden, aber die Anfrage ist trotzdem schneller, wenn die Typen identisch
sind.
•
Umwandlung einer Tabelle mit Zeilen variabler Länge in eine Tabelle mit
Zeilen fester Länge. Angenommen, Sie haben die Tabelle chartbl mit VARCHAR-Spalten, die in CHAR-Spalten umgewandelt werden sollen, um zu prüfen, ob
auf diese Weise Leistungsverbesserungen möglich sind. (Tabellen mit Zeilen
fester Länge können im Allgemeinen schneller verarbeitet werden als Tabellen
mit Zeilen variabler Länge.) Die Tabelle wurde wie folgt angelegt:
CREATE TABLE chartbl (name VARCHAR(40), address VARCHAR(80))
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
211
Das Problem dabei ist, dass Sie alle Spalten gleichzeitig innerhalb einer einzigen ALTER TABLE-Anweisung ändern müssen. Sie können sie nicht nacheinander ändern, weil das schlicht nicht möglich ist. Die Ausführung von DESCRIBE
chartbl zeigt, dass die Spalten dann weiterhin als VARCHAR definiert sind.
Wenn Sie nämlich immer nur eine einzige Spalte entsprechend abändern,
bemerkt MySQL, dass in der Tabelle weiterhin Spalten variabler Länge enthalten sind, und wandelt die geänderten Spalten wieder in VARCHAR um, um
Speicherplatz zu sparen. Deshalb müssen alle VARCHAR-Spalten gleichzeitig
geändert werden:
ALTER TABLE chartbl MODIFY name CHAR(40), MODIFY address CHAR(80)
Jetzt zeigt DESCRIBE, dass die Tabelle CHAR-Spalten enthält. Und genau für
diese Operation ist es so wichtig, dass ALTER TABLE mehrere Aktionen innerhalb einer einzigen Anweisung unterstützt.
Es gibt jedoch eine Tücke, die Sie bei dieser Art der Tabellenumwandlung
beachten sollten: Wenn in einer Tabelle BLOB- oder TEXT-Spalten vorhanden
sind, ist es nicht möglich, sie in eine Tabelle mit Zeilen fester Länge umzuwandeln. Selbst wenn nur eine einzige Spalte eine variable Länge hat, hat die
Tabelle Zeilen variabler Länge, und für diese Spaltentypen gibt es kein Äquivalent fester Länge.
•
Umwandlung einer Tabelle mit Zeilen fester Länge in eine Tabelle mit
Zeilen variabler Länge. chartbl ist also schneller mit Zeilen fester Länge,
aber es belegt mehr Speicher, als Ihnen vielleicht recht ist, deshalb wollen Sie
sie möglicherweise wieder in das ursprüngliche Format umwandeln, um Speicher zu sparen. Die Umwandlung einer Tabelle in diese Richtung ist viel einfacher. Sie brauchen nur eine einzige CHAR-Spalte in VARCHAR umzuwandeln,
dann wandelt MySQL die anderen Spalten automatisch um. Um die chartblTabelle umzuwandeln, führen Sie eine der folgenden Anweisungen aus:
ALTER TABLE chartbl MODIFY name VARCHAR(40)
ALTER TABLE chartbl MODIFY address VARCHAR(80)
•
Umwandlung eines Tabellentyps. Wenn Sie von einer MySQL-Version vor
3.23 auf 3.23 oder höher umgestiegen sind, haben Sie möglicherweise ältere
Tabellen, die als ISAM-Tabellen angelegt wurden. Wenn Sie sie ins MyISAMFormat konvertieren möchten, gehen Sie wie folgt vor:
ALTER TABLE tbl_name TYPE = MYISAM
Warum sollten Sie das tun? Ein Grund dafür, der bereits im Abschnitt »Indizes
anlegen und verwerfen« beschrieben wurde, ist, dass das MyISAM-Speicherformat Indexfunktionen erlaubt, die im ISAM-Format nicht zur Verfügung stehen,
beispielsweise Indizes für Spalten mit NULL-Werten oder BLOB- und TEXT-Spalten.
Darüber hinaus sind MyISAM-Tabellen maschinenunabhängig, so dass Sie sie
auf andere Maschinen verschieben können, indem Sie die Tabellendateien direkt
212
Informationen über Datenbanken und Tabellen ermitteln
kopieren, auch wenn die Maschinen unterschiedliche Hardwarearchitekturen verwenden. Dies wird in Kapitel 11 im Abschnitt »Sichern und Kopieren von Datenbanken« genauer beschrieben.
3.5
Informationen über Datenbanken und Tabellen
ermitteln
MySQL unterstützt mehrere Anweisungen, mit deren Hilfe Sie Informationen
über Datenbanken und die darin enthaltenen Tabellen sammeln. Diese Anweisungen sind praktisch, um den Inhalt Ihrer Datenbanken zu überwachen und sich die
Struktur Ihrer Tabellen anzeigen zu lassen. Außerdem können Sie sie auch als
Hilfe bei der Verwendung von ALTER TABLE einsetzen; es ist einfacher, herauszufinden, wie eine Spalte geändert werden kann, wenn man weiß, wie diese momentan definiert ist.
Mit der SHOW-Anweisung ermitteln Sie unterschiedliche Informationen über Ihre
Datenbanken und Tabellen:
SHOW DATABASES
Listet die auf dem Server geführten Datenbanken auf
SHOW TABLES
Listet die Tabellen der aktuellen Datenbank
auf
SHOW TABLES FROM db_name
Listet die Tabellen in der angegebenen
Datenbank auf
SHOW COLUMNS FROM tbl_name
Zeigt Informationen über Spalten in der
angegebenen Tabelle an
SHOW INDEX FROM tbl_name
Zeigt Informationen über Indizes in der
angegebenen Tabelle an
SHOW TABLE STATUS
Zeigt beschreibende Informationen über
Tabellen in der Standarddatenbank an
SHOW TABLE STATUS FROM
db_name
Zeigt beschreibende Informationen über
Tabellen in der angegebenen Datenbank an
Die Anweisungen DESCRIBE tbl_name und EXPLAIN tbl_name sind synonym zu
SHOW COLUMNS FROM tbl_name.
Der Befehl mysqlshow zeigt einige derselben Informationen wie die SHOW-Anweisung, was Ihnen erlaubt, Informationen über Datenbanken und Tabellen von der
Shell abzurufen:
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
213
% mysqlshow
Listet die auf dem Server geführten Datenbanken auf
% mysqlshow db_name
Listet die Tabellen in der angegebenen
Datenbank auf
% mysqlshow db_name tbl_name
Zeigt Informationen über Spalten in der
angegebenen Tabelle an
% mysqlshow --keys db_name
Zeigt Informationen über Indizes in der
angegebenen Tabelle an
tbl_name
% mysqlshow --status db_name
Zeigt beschreibende Informationen über
Tabellen in der angegebenen Datenbank an
Mit dem Dienstprogramm mysqldum zeigen Sie die Struktur Ihrer Tabellen in
Form einer CREATE TABLE-Anweisung an. (Im Vergleich zur Anweisung SHOW
COLUMNS finde ich die Ausgabe von mysqldum einfacher zu lesen und es werden
außerdem die Indizes der Tabelle angezeigt.) Achten Sie bei der Ausführung von
mysqldump jedoch darauf, es mit der Option --no-data aufzurufen, so dass Sie
nicht von den Daten in Ihren Tabellen erschlagen werden!
% mysqldump --no-data db_name tbl_name
Sowohl für mysqlshow als auch für mysqldump können die üblichen Optionen angegeben werden, beispielsweise --host, um eine Verbindung zu einem Server auf
einem anderen Host einzurichten.
3.6
Datensätze suchen
Es wäre Unsinn, Datensätze in einer Datenbank abzulegen, würde man sie nicht
irgendwann wieder laden und irgendetwas damit tun. Das ist die Aufgabe der
SELECT-Anweisung: Sie hilft Ihnen, an Ihre Daten zu kommen. SELECT ist die vermutlich am häufigsten genutzte Anweisung in der Sprache SQL, kann aber auch
sehr kompliziert sein; die Beschränkungen für die Auswahl von Zeilen kann
beliebig komplex werden und Vergleiche zwischen Spalten verschiedener Tabellen bedingen.
Die grundlegende Syntax der SELECT-Anweisung sieht wie folgt aus:
SELECT auswahl_liste
Welche Spalten ausgewählt werden sollen
FROM tabellen_liste
Wo Zeilen ausgewählt werden sollen
WHERE primäre_bedingung
Welche Bedingungen die Zeilen erfüllen
müssen
GROUP BY spalten_gruppierung
Wie das Ergebnis gruppiert werden soll
214
Datensätze suchen
ORDER BY spalten_sortierung
Wie das Ergebnis sortiert werden soll
HAVING sekundäre_bedingung
Sekundäre Bedingungen, die die Zeilen
erfüllen müssen
LIMIT zähler
Begrenzung des Ergebnisses
Alles in dieser Syntax ist optional, außer dem Wort SELECT und der Komponente
spalten_liste, wo angegeben wird, was Sie suchen wollen. In einigen Datenbanken muss auch die FROM-Klausel angegeben werden. MySQL fordert das nicht, so
dass Sie Ausdrücke auswerten können, ohne auf irgendwelche Tabellen zu verweisen:
SELECT SQRT(POW(3,2)+POW(4,2))
Bereits in Kapitel 1 sind wir auf SELECT eingegangen, insbesondere in Hinblick
auf die Spaltenliste und die Klauseln WHERE, GROUP BY, ORDER BY, HAVING und LIMIT.
In diesem Kapitel geht es vor allem um den vielleicht schwierigsten Aspekt von
SELECT – den Join. Hier wird beschrieben, welche Join-Typen MySQL unterstützt,
was sie bedeuten und wie man sie ausführt. Damit sollte es Ihnen möglich sein,
MySQL effektiver zu nutzen, weil das eigentliche Problem bei der Entwicklung
einer Anfrage meistens ist, wie man die Tabellen korrekt miteinander verbindet.
Lesen Sie dazu auch den Abschnitt »Lösungsvorschläge« später in diesem Kapitel. Dort finden Sie Lösungen zu verschiedenen SQL-Problemen, die größtenteils
mit SELECT zu tun haben.
Ein Problem bei der Verwendung von SELECT ist, dass Sie bei einer neuen Aufgabe nicht immer sofort erkennen, mit welcher SELECT-Anfrage Sie ihr gerecht
werden können. Nachdem Sie es herausgefunden haben, können Sie jedoch diese
Erfahrung auf alle zukünftigen ähnlichen Probleme anwenden. SELECT ist vielleicht die Anweisung, für die Ihre Erfahrung die größte Rolle spielt, um sie effektiv nutzen zu können, weil es einfach so viele Möglichkeiten gibt, sie auszuführen.
Nachdem Sie eine gewisse Erfahrung gesammelt haben, wird Ihnen die Umsetzung neuer Aufgabenstellungen immer leichter fallen, und Sie werden irgendwann nur noch denken »Ach ja, das ist jetzt wieder diese Geschichte mit LEFT
JOIN«, oder »Aha, das ist ein Dreiwege-Join mit einer Einschränkung auf die
gemeinsamen Schlüsselspaltenpaare.« (Ich bin nicht ganz sicher, ob ich das so
sagen sollte. Vielleicht macht es Ihnen Mut, wenn Sie hören, dass Erfahrung Sie
weiter bringt. Andererseits finden Sie es vielleicht komisch, sich vorzustellen,
jemals Gedankengänge wie die oben beschriebenen haben zu können.)
In den nächsten Abschnitten, wo Sie die verschiedenen von MySQL unterstützten
Join-Operationen kennenlernen, werden für die meisten Beispiele die beiden folgenden Tabellen verwendet: Sie sind relativ klein, so dass Sie die Auswirkungen
der verschiedenen Joins sofort erkennen:
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
table t1:
+------+------+
| i1
| c1
|
+------+------+
|
1 | a
|
|
2 | b
|
|
3 | c
|
+------+------+
3.6.1
215
table t2:
+------+------+
| i2
| c2
|
+------+------+
|
2 | c
|
|
3 | b
|
|
4 | a
|
+------+------+
Der triviale Join
Der einfachste Join ist der triviale Join, wofür nur eine einzige Tabelle angegeben
wird. In diesem Fall werden Zeilen aus der angegebenen Tabelle ausgewählt:
SELECT ... FROM t1
+------+------+
| i1
| c1
|
+------+------+
|
1 | a
|
|
2 | b
|
|
3 | c
|
+------+------+
Einige Autoren berücksichtigen diese Form von SELECT mit Join nicht und gehen
nur auf SELECT-Anweisungen ein, die Datensätze aus zwei oder mehr Tabellen
ermitteln. Das ist alles eine Frage der Perspektive.
3.6.2
Der vollständige Join
Werden mehrere Tabellen durch Komma voneinander abgetrennt angegeben, findet ein vollständiger Join statt. Wenn Sie beispielsweise zwei Tabellen mit einem
Join verbinden, wird jede Zeile aus der ersten Tabelle mit jeder Zeile der zweiten
Tabelle verbunden:
SELECT t1.*, t2.* FROM t1, t2
+------+------+------+------+
| i1
| c1
| i2
| c2
|
+------+------+------+------+
|
1 | a
|
2 | c
|
|
2 | b
|
2 | c
|
|
3 | c
|
2 | c
|
|
1 | a
|
3 | b
|
|
2 | b
|
3 | b
|
|
3 | c
|
3 | b
|
|
1 | a
|
4 | a
|
|
2 | b
|
4 | a
|
|
3 | c
|
4 | a
|
+------+------+------+------+
216
Datensätze suchen
Ein vollständiger Join wird auch als Kreuz-Join bezeichnet, weil jede Zeile jeder
Tabelle mit jeder Zeile jeder anderen Tabelle verbunden wird, um alle möglichen
Kombinationen zu erhalten. Man spricht auch vom kartesischen Produkt. Durch
diese Form der Tabellenverknüpfung entstehen möglicherweise sehr viele Zeilen.
Die Anzahl der Zeilen ist das Produkt der Zeilen aller Tabellen. Ein vollständiger
Join für drei Tabellen mit 100, 200 bzw. 300 Datensätzen erzeugt also 100 * 200 *
300 = 6 Millionen Zeilen. Das ist eine ganze Menge, obwohl die einzelnen Tabellen noch relativ klein sind. Normalerweise verwendet man eine WHERE-Klausel,
um ein überschaubareres Ergebnis zu erhalten.
Wenn Sie der WHERE-Klausel eine Bedingung hinzufügen, die mit einem der Werte
bestimmter Spalten übereinstimmen muss, entsteht ein sogenannter Equi-Join,
weil Sie nur Zeilen mit gleichen Werten in den angegebenen Spalten auswählen:
SELECT t1.*, t2.* FROM t1, t2 WHERE t1.i1 = t2.i2
+------+------+------+------+
| i1
| c1
| i2
| c2
|
+------+------+------+------+
|
2 | b
|
2 | c
|
|
3 | c
|
3 | b
|
+------+------+------+------+
Die Join-Typen JOIN, CROSS JOIN und INNER JOIN sind äquivalent zum JoinOperator.
Ein STRAIGHT_JOIN ist ein vollständiger Join, aber die Tabellen werden in der in
der FROM-Klausel angegebenen Reihenfolge verknüpft. Normalerweise ordnet der
Optimierer von MySQL die Reihenfolge der Tabellen in einem vollständigen Join
so an, dass die Zeilen schneller gefunden werden. Manchmal trifft er dabei jedoch
eine nicht-optionale Auswahl, die Sie mit dem Schlüsselwort STRAIGHT_JOIN
überschreiben.
STRAIGHT_JOIN kann innerhalb einer SELECT-Anweisung an zwei Stellen angegeben werden. Sie können es zwischen dem Schlüsselwort SELECT und der Auswahlliste angeben, so dass es sich global auf alle vollständigen Joins der Anweisung
auswirkt, oder in der FROM-Klausel. Die beiden folgenden Anweisungen sind äquivalent:
SELECT STRAIGHT_JOIN ... FROM table1, table2, table3 ...
SELECT ... FROM table1 STRAIGHT_JOIN table2 STRAIGHT_JOIN table3 ...
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
217
Qualifizieren von Spaltenverweisen
Verweise auf Tabellenspalten in einer SELECT-Anweisung müssen sich den in
der FROM-Klausel angegebenen Tabellen eindeutig zuordnen lassen. Ist hier
nur eine einzige Tabelle angegeben, entstehen keine Mehrdeutigkeiten, weil
die Spalten dieser Tabelle zugehören müssen. Werden mehrere Tabellen angegeben, sind alle Spaltennnamen, die nur in einer dieser Tabelle vorkommen, ebenfalls eindeutig. Wird jedoch ein Spaltenname in mehreren Tabellen verwendet, müssen die Verweise auf die Spalte qualifiziert werden. Mit
Hilfe der Syntax tbl_name.spalten_name geben Sie an, welche Tabelle Sie
meinen. Enthält die Tabelle my_tbl1 die Spalten a und b und die Tabelle my_
tbl2 die Spalten b und c, sind die Verweise auf die Spalten a und c eindeutig, aber die Verweise auf b müssen entweder als my_tbl1.b oder als my_
tbl2.b qualifiziert werden:
SELECT a, my_tbl1.b, my_tbl2.b, c FROM my_tbl1, my_tbl2 …
Manchmal ist ein qualifizierter Tabellenname nicht ausreichend, um einen
Spaltenverweis aufzulösen. Verwenden Sie beispielsweise eine Tabelle in
einer Anfrage mehrmals, ist es nicht sinnvoll, eine Spalte mit dem Tabellennamen zu qualifizieren. In diesem Fall ist die Verwendung von Tabellenaliasen sinnvoll, um Ihre Absichten verständlich formulieren zu können. Sie
weisen jeder Instanz der Tabelle einen Alias zu und verweisen auf die Spalten dieser Instanz als alias_name.spalten_name. Die folgende Anfrage verknüpft eine Tabelle mit sich selbst, weist aber einer Instanz der Tabelle einen
Alias zu, so dass die Spaltenverweise eindeutig sind:
SELECT my_tbl.col1, m.col2
FROM my_tbl, my_tbl AS m
WHERE my_tbl.col1 > m.col1
3.6.3
Der Left-Join
Ein Equi-Join zeigt nur Zeilen an, für die in beiden Tabellen eine Übereinstimmung gefunden wurde. Ein Left-Join zeigt ebenfalls Übereinstimmungen an, aber
auch Zeilen aus der linken Tabelle, für die es in der rechten Tabelle keine Übereinstimmung gibt. Alle Spalten, die für solche Zeilen in der rechten Tabelle
gesucht aber nicht gefunden wurden, werden als NULL angezeigt. Dabei werden
aus der linken Tabelle alle Zeilen ausgewählt. Gibt es eine übereinstimmende
Zeile in der rechten Tabelle, wird diese ausgewählt. Gibt es keine Übereinstimmung, wird die Zeile trotzdem ausgewählt, ist dabei aber gar keine gültige Zeile,
weil alle ihre Spalten auf NULL gesetzt werden. Mit anderen Worten, ein LEFT JOIN
erzwingt, dass das Ergebnis für jede Zeile in der linken Tabelle eine Zeile enthält,
egal ob es in der rechten Tabelle eine Übereinstimmung dafür gibt. Der Vergleich
218
Datensätze suchen
erfolgt für die Spalten in einer ON- oder USING()-Klausel. ON kann immer verwendet werden, unabhängig davon, ob die beiden verknüpften Spalten denselben
Namen haben:
SELECT t1.*, t2.* FROM t1 LEFT JOIN t2 ON t1.i1 = t2.i2
+------+------+------+------+
| i1
| c1
| i2
| c2
|
+------+------+------+------+
|
1 | a
| NULL | NULL |
|
2 | b
|
2 | c
|
|
3 | c
|
3 | b
|
+------+------+------+------+
Die USING()-Klausel ist ON ganz ähnlich, aber hier müssen die Namen der verknüpften Spalten in den Tabellen gleich sein. Die folgende Anfrage verknüpft my_
tbl1.b mit my_tbl2.b:
SELECT my_tbl1.*, my_tbl2.* FROM my_tbl1 LEFT JOIN my_tbl2 USING (b)
LEFT JOIN ist insbesondere in Situationen nützlich, in denen Sie nur die Zeilen der
linken Tabelle herausfinden wollen, die in der rechten Tabelle nicht vorkommen.
Dazu fügen Sie eine WHERE-Klausel ein, die nach Zeilen sucht, für die die rechte
Tabelle NULL-Werte einträgt:
SELECT t1.*, t2.* FROM t1 LEFT JOIN t2 ON t1.i1 = t2.i2 WHERE t2.i2 IS
NULL
+------+------+------+------+
| i1
| c1
| i2
| c2
|
+------+------+------+------+
|
1 | a
| NULL | NULL |
+------+------+------+------+
Normalerweise sind uns die Spalten egal, die NULL ergeben, weil sie nicht von
Bedeutung sind. Aber hier suchen Sie nach Spalten der linken Tabelle, für die es
keine Übereinstimmung gibt:
SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.i1 = t2.i2 WHERE t2.i2 IS NULL
+------+------+
| i1
| c1
|
+------+------+
|
1 | a
|
+------+------+
Beachten Sie beim LEFT JOIN, dass, wenn die Spalten, die Sie verknüpfen, als NOT
NULL deklariert sind, Sie möglicherweise Zeilen im Ergebnis erhalten, die keine
Bedeutung haben.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
219
LEFT JOIN hat einige Synonyme und Varianten. LEFT OUTER JOIN ist ein Synonym
für LEFT JOIN. Außerdem gibt es eine ODBC-Notation für LEFT JOIN, die auch von
MySQL akzeptiert wird (das oj steht für outer join):
{ oj tbl_name LEFT OUTER JOIN tbl_name ON join_ausdruck }
NATURAL LEFT JOIN ist ähnlich dem LEFT JOIN; es führt einen LEFT JOIN aus,
wobei alle Spalten verglichen werden, die in der linken und in der rechten Tabelle
denselben Namen haben.
In einigen Datenbanken gibt es einen entsprechenden RIGHT JOIN; in MySQL gibt
es das noch nicht.
3.7
Kommentare
MySQL erlaubt Ihnen, Ihren SQL-Code mit Kommentaren zu versehen. Das kann
sehr praktisch sein, um die in Dateien gespeicherten Anfragen zu dokumentieren.
Kommentare können auf zweierlei Weise dargestellt werden. Alles von einem #Zeichen bis zum Zeilenende wird als Kommentar betrachtet. Außerdem können
Kommentare im C-Stil verwendet werden. Das bedeutet, alles zwischen /* und */
als Kennzeichnen für Anfang und Ende wird als Kommentar betrachtet. Die Kommentare im C-Stil können sich über mehrere Zeilen erstrecken:
# Dies ist ein einzeiliger Kommentar
/* Auch dies ist ein einzeiliger Kommentar */
/* Dies hingegen
ist ein mehrzeiliger
Kommentar
*/
Seit MySQL 3.23 können Sie MySQL-spezifische Schlüsselwörter in Kommentaren im C-Stil verbergen, indem Sie diesen mit /*! statt mit /* beginnen. MySQL
sucht innerhalb dieses Kommentars nach Schlüsselwörtern, während andere
Datenbank-Server sie als Teil des Kommentars ignorieren. Auf diese Weise können Sie Code schreiben, der MySQL-spezifische Funktionen nutzt, wenn er von
MySQL ausgeführt wird, aber auch ohne weitere Änderungen von anderen
Datenbank-Servern ausgeführt werden kann. Die beiden folgenden Anweisungen
sind äquivalent für andere Datenbank-Server als MySQL, aber MySQL führt für
die zweite eine INSERT DELAYED-Operation aus:
INSERT INTO absence (student_id,date) VALUES(13,"1999-09-28")
INSERT /*! DELAYED */ INTO absence (student_id,date) VALUES(13,"1999-0928")
Seit MySQL 3.23.3 können Sie neben den oben beschriebenen Stilen einen Kommentar auch mit zwei Trennstrichen und einem Leerzeichen beginnen (--); alles
von den Trennstrichen bis zum Zeilenende wird als Kommentar betrachtet. Einige
220
Lösungsvorschläge
andere Datenbanken kennzeichnen den Kommentar mit einem doppelten Trennstrich. MySQL erlaubt diese Schreibweise, fordert jedoch die Angabe eines Leerzeichens, um Mehrdeutigkeiten zu vermeiden.
Bei Anweisungen mit Ausdrücken wie 5--7 könnte man sonst meinen, dass sie
einen Kommentar enthalten. Es wäre aber eher unwahrscheinlich, dass Sie einen
solchen Ausdruck als 5-- 7 schreiben, es ist also eine sinnvolle Methode. Dennoch ist es besser, einen der anderen Kommentarstile zu verwenden und die doppelten Trennstriche nur in dem Code beizubehalten, den Sie aus anderen Datenbanken übernehmen.
3.8
Lösungsvorschläge
Dieser Abschnitt zeigt, wie Sie Anfragen für unterschiedliche Aufgaben schreiben. Größtenteils handelt es sich dabei um Lösungen für Probleme, die immer
wieder auf der Mailing-Liste auftauchen. (Danke den Leuten der Liste, die die
Antworten bereitgestellt haben.)
3.8.1
Subselects als Joins umformulieren
Ab Version 3.24 gibt es in MySQL Subselects. Diese Funktion wurde in MySQL
am meisten vermisst, aber viele Benutzer erkannten einfach nicht, dass Subselects
größtenteils auch als Join ausgedrückt werden können. Und selbst wenn MySQL
Subselects unterstützt, sollte man auf die Formulierung der Anfragen achten, weil
Joins in der Mehrzahl der Fälle effizienter sind als Subselects.
Subselects umformulieren, die übereinstimmende Werte auswählen
Hier folgt eine Beispielanfrage mit einem Subselect; sie wählt Noten aus der
Tabelle score für alle Tests aus (d.h. die Noten für die Stegreifaufgaben werden
ignoriert):
SELECT * FROM score
WHERE event_id IN (SELECT event_id FROM event WHERE type = "T")
Dieselbe Anfrage kann ohne Subselect ausgeführt werden, indem man sie in
einen einfachen Join umwandelt.
SELECT score.* FROM score, event
WHERE score.event_id = event.event_id AND event.type = "T"
Das nächste Beispiel wählt die Noten der weiblichen Schüler aus:
SELECT * from score
WHERE student_id IN (SELECT student_id FROM student WHERE sex = "F")
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
221
Auch das kann in einen Join umgewandelt werden:
SELECT score.* FROM score, student
WHERE score.student_id = student.student_id AND student.sex = "F"
Hier gibt es ein Muster. Die Subselect-Anfragen weisen die folgende Form auf:
SELECT * FROM table1
WHERE spalte1 IN (SELECT spalte2 FROM table2a WHERE spalte2b = wert)
Solche Anfragen können wie folgt in einen Join umgewandelt werden:
SELECT table1.* FROM table1, table2
WHERE table1.spalte1 = table2.spalte2a AND table2.spalte2b = wert
Umformulierung von Subselects, die nicht übereinstimmende Werte
auswählen
Ein weiterer gebräuchlicher Typ einer Subselect-Anfrage sucht nach Werten in
einer Tabelle, die nicht in einer anderen Tabelle vorhanden sind. Wie wir zuvor
gesehen haben, ist die Aufgabenstellung »deren Werte nicht vorhanden sind« ein
Hinweis darauf, dass ein LEFT JOIN verwendet werden könnte. Hier eine Anfrage,
die das Fehlen von Werten in einer Tabelle überprüft (sie findet alle Schüler, die
nie abwesend waren):
SELECT * FROM student
WHERE student_id NOT IN (SELECT student_id FROM absence)
Diese Anfrage kann unter Verwendung eines LEFT JOIN wie folgt umformuliert
werden:
SELECT student.*
FROM student LEFT JOIN absence ON student.student_id = absence.student_
id
WHERE absence.student_id IS NULL
Allgemein ausgedrückt hat die Subselect-Anfrage die folgende Form:
SELECT * FROM table1
WHERE spalten1 NOT EXISTS (SELECT spalte2 FROM table2)
Eine Anfrage dieser Form könnte wie folgt umformuliert werden:
SELECT table1.*
FROM table1 LEFT JOIN table2 ON table1.spalte1 = table2.spalte2
WHERE table2.spalte2 IS NULL
Dabei wird vorausgesetzt, dass table2.spalte2 als NOT NULL deklariert ist.
222
3.8.2
Lösungsvorschläge
Werte suchen, die in einer Tabelle nicht vorhanden sind
Im Abschnitt »Datensätze suchen« haben wir bereits gezeigt, wie man feststellt,
welche Werte in einer Tabelle, nicht aber in einer anderen Tabelle vorhanden sind,
indem man einen LEFT JOIN für die beiden Tabellen ausführt und nach Zeilen
sucht, die für die zweite Tabelle NULL ergeben. Eine einfache Situation wurde
anhand der beiden folgenden Tabellen demonstriert:
table t1:
+------+------+
| i1
| c1
|
+------+------+
|
1 | a
|
|
2 | b
|
|
3 | c
|
+------+------+
table t2:
+------+------+
| i2
| c2
|
+------+------+
|
2 | c
|
|
3 | b
|
|
4 | a
|
+------+------+
Der LEFT JOIN, mit dem alle Werte t1.i1 ermittelt werden, die in der Spalte t2.i2
nicht vorkommen, sieht wie folgt aus:
SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.i1 = t2.i2 WHERE t2.i2 IS NULL
+------+------+
| i1
| c1
|
+------+------+
|
1 | a
|
+------+------+
Jetzt betrachten wir eine kompliziertere Version der Frage »Welche Werte fehlen?«. Für das Notenverwaltungsprojekt aus Kapitel 1 gibt es eine Tabelle student, die alle Schüler enthält, und eine Tabelle event, die die Notenereignisse
festhält, sowie eine Tabelle score, die die Punktewerte auflistet, die die Schüler
bei diesen Prüfungen erreicht haben. War ein Schüler jedoch am Tag einer Prüfung krank, gibt es in der score-Tabelle keinen Eintrag für den Schüler und diese
Prüfung, so dass er eine Nachholprüfung absolvieren muss. Wie können wir die
fehlenden Datensätze ermitteln, um sicherzustellen, dass alle diese Schüler eine
Nachholprüfung erhalten?
Das Problem dabei ist, festzustellen, welche Schüler keinen Punktwert für eine
Prüfung haben. Man könnte auch fragen, welche Kombination aus Schüler und
Ereignis nicht in der Punktetabelle enthalten ist. Die Fragestellung »welche Werte
sind nicht vorhanden« ist ein Hinweis auf die Verwendung eines LEFT JOIN. Dieser Join ist jedoch nicht so einfach wie im vorigen Beispiel, weil wir nicht nur
nach Werten suchen, die in einer einzigen Spalte nicht vorhanden sind; vielmehr
suchen wir nach einer zweispaltigen Kombination.
Wir brauchen alle Schüler/Ereignis-Kombinationen, die durch Kreuzen der
Tabelle student mit der Tabelle event entstehen:
FROM student, event
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
223
Für das Ergebnis dieses Joins führen Sie ein LEFT JOIN mit der score-Tabelle aus,
um die Übereinstimmungen zu finden:
FROM student, event
LEFT JOIN score ON student.student_id = score.student.id
AND event.event_id = score.event_id
Beachten Sie die ON-Klausel. Sie erlaubt, die Zeilen der score-Tabelle mit den
Übereinstimmungen in anderen Tabellen zu verknüpfen. Das ist der Schlüssel zu
unserem Problem. Der LEFT JOIN erzwingt, dass für jede der im Kreuz-Join der
Tabellen student und event erzeugten Zeilen eine Zeile erzeugt wird, auch wenn
es keinen entsprechenden Eintrag in der score-Tabelle gibt. Die Zeilen für die
fehlenden Punktewertdatensätze in der Ergebnismenge werden daran erkannt,
dass die Spalte aus der score-Tabelle gleich NULL ist. Wir wählen diese Datensätze
in der WHERE-Klausel aus. Alle Spalten aus der score-Tabelle könnten dazu herangezogen werden, aber weil wir nach fehlenden Punktewerten suchen, sollten wir
der Klarheit halber die score-Spalte überprüfen:
WHERE score.score IS NULL
Mit Hilfe einer ORDER BY-Klausel sortieren wir die Ereignisse. Logisch wäre ein
Sortieren nach dem Schüler oder nach dem Ereignis. Hier wollen wir die erste
Methode zeigen:
ORDER BY student.student_id, event.event_id
Jetzt brauchen wir nur noch die Spalten anzugeben, die wir in der Ausgabe sehen
wollen, und fertig. Hier die endgültige Anfrage:
SELECT
student.name, student.student_id,
event.date, event.event_id, event.type
FROM
student, event
LEFT JOIN score ON student.student_id = score.student_id
AND event.event_id = score.event_id
WHERE
score.score IS NULL
ORDER BY
student.student_id, event.event_id
Die Ausführung dieser Anfrage führt zum folgenden Ergebnis:
224
Ausführung einer UNION-Operation
+-----------+------------+------------+----------+------+
| name
| student_id | date
| event_id | type |
+-----------+------------+------------+----------+------+
| Megan
|
1 | 1999-09-16 |
4 | Q
|
| Joseph
|
2 | 1999-09-03 |
1 | Q
|
| Katie
|
4 | 1999-09-23 |
5 | Q
|
| Devri
|
13 | 1999-09-03 |
1 | Q
|
| Devri
|
13 | 1999-10-01 |
6 | T
|
| Will
|
17 | 1999-09-16 |
4 | Q
|
| Avery
|
20 | 1999-09-06 |
2 | Q
|
| Gregory |
23 | 1999-10-01 |
6 | T
|
| Sarah
|
24 | 1999-09-23 |
5 | Q
|
| Carter
|
27 | 1999-09-16 |
4 | Q
|
| Carter
|
27 | 1999-09-23 |
5 | Q
|
| Gabrielle |
29 | 1999-09-16 |
4 | Q
|
| Grace
|
30 | 1999-09-23 |
5 | Q
|
+-----------+------------+------------+----------+------+
Hier gibt es einen interessanten Aspekt. Die Ausgabe zeigt die Schüler-ID und die
Ereignis-ID an. Die Spalte student_id wird sowohl in der Tabelle student als
auch in der Tabelle score geführt. Auf den ersten Blick glauben Sie deshalb vielleicht, dass die Auswahlliste entweder student.student_id oder score.student_
id angeben könnte. Das ist jedoch nicht der Fall, weil der eigentliche Grund, dank
dessen wir in der Lage sind, die gewünschten Datensätze zu finden, ist, dass alle
Felder der score-Tabelle als NULL zurückgegeben werden. Die Auswahl von
score.student_id würde nur eine Spalte aus NULL-Werten in der Ausgabe erzeugen. Eine ähnliche Argumentation gilt für die Spalte event_ID, die in beiden
Tabellen geführt wird.
3.9
Ausführung einer UNION-Operation
Wenn Sie eine Ergebnismenge erzeugen wollen, indem Sie Datensätze aus mehreren Tabellen auswählen, die dieselbe Struktur aufweisen, können Sie dazu in einigen Datenbanksystemen eine Art UNION-Anweisung benutzen. MySQL beinhaltet
keine UNION-Anweisung (wenigstens nicht bis MySQL 3.24), aber es gibt verschiedene Möglichkeiten, dasselbe auszudrücken. Hier zwei mögliche Lösungen:
•
Ausführung mehrerer SELECT-Anfragen, eine für jede Tabelle. Das funktioniert, wenn die Reihenfolge der ausgewählten Zeilen keine Rolle spielt.
•
Auswahl von Zeilen aus jeder Tabelle in eine temporäre Tabelle und Auswahl
des Inhalts dieser Tabelle. Auf diese Weise können Sie die Zeilen nach Bedarf
sortieren. In MySQL 3.23 und höher können Sie dieses Problem einfach in den
Griff kriegen, indem Sie dem Server erlauben, diese Behältertabelle für Sie anzulegen. Außerdem können Sie die Tabelle zu einer temporären Tabelle machen, so dass sie automatisch gelöscht wird, nachdem Sie Ihre Sitzung mit dem
Server beendet haben.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
225
Im folgenden Code löschen wir die Tabelle explizit, so dass der Server die
zugehörigen Ressourcen freigeben kann. Das ist sinnvoll, wenn die Client-Sitzung mit weiteren Anfragen fortgesetzt wird. Außerdem verwenden wir eine
HEAP-Tabelle (im Speicher), um eine bessere Leistung zu erzielen.
CREATE TEMPORARY TABLE hold_tbl TYPE=HEAP SELECT ... FROM table1 WHERE ...
INSERT INTO hold_tbl SELECT ... FROM table2 WHERE ...
INSERT INTO hold_tbl SELECT ... FROM table3 WHERE ...
...
SELECT * FROM hold_tbl ORDER BY ...
DROP TABLE hold_tbl
Für ältere MySQL-Versionen als 3.23 gilt dasselbe Konzept, außer dass Sie
hier die Spalten in der Tabelle hold_tbl explizit selbst deklarieren müssen und
die Anweisung DROP TABLE am Ende zwingend erforderlich ist, damit die
Tabelle nach dem Beenden der Client-Sitzung entfernt wird:
CREATE TABLE hold_tbl (column1 ..., column2 ..., ...)
TYPE=HEAP SELECT ... FROM table1 WHERE ...
INSERT INTO hold_tbl SELECT ... FROM table1 WHERE ...
INSERT INTO hold_tbl SELECT ... FROM table2 WHERE ...
INSERT INTO hold_tbl SELECT ... FROM table3 WHERE ...
SELECT * FROM hold_tbl ORDER BY ...
DROP TABLE hold_tbl
3.9.1
Einfügen einer Spalte für Folgenummern
Wenn Sie mit ALTER TABLE eine AUTO_INCREMENT-Spalte einfügen, wird die Spalte
automatisch mit Folgenummern gefüllt. Die folgenden Anweisungen einer mysqlSitzung zeigen, wie das funktioniert, indem eine Tabelle angelegt, eine Menge
Daten darin abgelegt und dann eine AUTO_INCREMENT-Spalte eingefügt wird:
mysql> CREATE TABLE t (c CHAR(10));
mysql> INSERT INTO t VALUES("a"),("b"),("c");
mysql> SELECT * FROM t;
+------+
| c
|
+------+
| a
|
| b
|
| c
|
+------+
mysql> ALTER TABLE t ADD i INT AUTO_INCREMENT NOT NULL PRIMARY KEY;
mysql> SELECT * FROM t;
+------+---+
| c
| i |
+------+---+
226
Ausführung einer UNION-Operation
| a
| 1 |
| b
| 2 |
| c
| 3 |
+------+---+
3.9.2
Eine existierende Spalte für die Nummerierung benutzen
Wenn Sie eine numerische Spalte haben, können Sie damit Ihre Datensätze nummerieren (oder neu nummerieren, falls sie bereits nummeriert war, Sie aber Zeilen
gelöscht haben und die Werte neu nummerieren wollen, damit sie fortlaufend
sind), etwa so:
ALTER TABLE t MODIFY i INT NULL
UPDATE t SET i = NULL
ALTER TABLE t MODIFY i INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY
Eine einfachere Methode wäre, die Spalte zu löschen und eine AUTO_INCREMENTSpalte dafür einzufügen. ALTER TABLE erlaubt die Ausführung mehrerer Aktionen,
deshalb kann das alles innerhalb einer einzigen Anweisung passieren:
ALTER TABLE t
DROP i,
ADD i INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY
3.9.3
Sortieren in außergewöhnlichen Reihenfolgen
Angenommen, Sie haben eine Tabelle, die das Personal eines Sportvereins repräsentieren soll, beispielsweise eine Fußballmannschaft: Sie wollen die Ausgaben
nach der Position der jeweiligen Angestellten sortieren, so dass diese in einer
bestimmten Reihenfolge erscheinen, etwa: Trainer, Co-Trainer, Tormänner, Feldspieler, Ersatzspieler usw. Definieren Sie die Spalte als ENUM und führen Sie die
Auflistungselemente in der Reihenfolge an, in der Sie sie später anzeigen wollen.
Beim Sortieren dieser Spalte wird automatisch die hier vorgegebene Reihenfolge
berücksichtigt.
3.9.4
Einrichten einer Zählertabelle
In Kapitel 2 haben wir im Abschnitt »Folgen« bereits gezeigt, wie man mit LAST_
INSERT_ID(ausdruck) eine Folge erzeugt. Das dortige Beispiel hat gezeigt, wie
man einen Zähler mit Hilfe einer Tabelle mit einer einzigen Zeile einrichtet. Das
ist für einen einzigen Zähler kein Problem, aber wenn Sie mehrere Zähler brauchen, führt diese Methode zu einer unnötig großen Anzahl von Tabellen. Angenommen, Sie haben eine Website und wollen auf mehreren Seiten den Zähler
»Diese Seite hatte bereits nnn Besucher« anzeigen. Bestimmt wollen Sie nicht für
jede Seite mit einem Zähler eine separate Zählertabelle einrichten.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
227
Eine Lösungsmöglichkeit wäre, eine einzige Tabelle mit zwei Spalten einzurichten. Eine Spalte nimmt einen Zählerwert auf; die andere einen Zählernamen. Wir
können die Funktion LAST_INSERT_ID() verwenden, aber anhand des Zählernamens stellen wir fest, auf welche Zeile sie sich bezieht. Die Tabelle sieht wie folgt
aus:
CREATE TABLE counter
(
count INT UNSIGNED,
name varchar(255) NOT NULL PRIMARY KEY
)
Der Name ist eine Zeichenkette, wir können einem Zähler also einen beliebigen
Namen zuweisen und ihn zu einem PRIMARY KEY machen, um doppelte Namen zu
vermeiden. Damit wird vorausgesetzt, dass die Anwendungen, die die Tabelle
verwenden, sich auf die verwendeten Namen einigen. Für unsere Web-Zähler
können wir die Eindeutigkeit der Zählernamen sicherstellen, indem wir einfach
die Pfadnamen der betreffenden Seite im Dokumentbaum als Zählernamen verwenden. Um beispielsweise einen neuen Zähler für die Homepage der Site einzurichten, gehen Sie wie folgt vor:
INSERT INTO counter (name) VALUES("index.html")
Damit wird der Zähler »index.html« mit einem Wert von Null initialisiert. Um
den nächsten Folgewert zu erzeugen, inkrementieren Sie den Zähler in der entsprechenden Zeile der Tabelle und laden ihn mit LAST_INSERT_ID():
UPDATE counter
SET count = LAST_INSERT_ID(count+1)
WHERE name = "index.html"
SELECT LAST_INSERT_ID()
Eine Alternative wäre, den Zähler ohne LAST_INSERT_ID() zu inkrementieren,
etwa wie folgt:
UPDATE counter SET count = count+1 WHERE name = "index.html"
SELECT count FROM counter WHERE name = "index.html"
Das funktioniert jedoch nicht, wenn ein anderer Client den Zähler inkrementiert,
nachdem Sie das UPDATE und bevor Sie das SELECT ausgeführt haben. Dieses Problem könnten Sie mit der Anwendung von LOCK TABLES und UNLOCK TABLES um
die beiden Anweisungen löschen, um andere Clients auszuschließen, während Sie
den Zähler verwenden. Mit der LAST_INSERT_ID()-Methode ist das Ganze jedoch
einfacher zu bewerkstelligen. Weil ihr Wert client-spezifisch ist, erhalten Sie
immer den Wert, den Sie eingetragen haben, nicht einen Wert irgendeines anderen
Clients. Und Sie müssen den Code nicht verkomplizieren, indem Sie Sperren setzen, die andere Clients blockieren.
228
3.9.5
Funktionen, die MySQL nicht unterstützt
Prüfen, ob eine Tabelle existiert
Manchmal ist es wichtig, innerhalb einer Anwendung zu erkennen, ob eine bestimmte Tabelle existiert. Dazu verwenden Sie eine der folgenden Anweisungen:
SELECT COUNT(*) FROM tbl_name
SELECT * FROM tbl_name WHERE 1=0
Diese Anweisungen sind erfolgreich, falls die Tabelle existiert, andernfalls schlagen sie fehl. Die Anfragen sind für diesen Test gut geeignet. Sie werden sehr
schnell ausgeführt, so dass Sie damit nicht viel Zeit verlieren. Diese Vorgehensweise ist insbesondere für Anwendungsprogramme praktisch, die Sie selbst
schreiben, weil Sie prüfen können, ob die Anfrage erfolgreich war und die entsprechenden Maßnahmen ergreifen können. Weniger sinnvoll dagegen ist sie bei
Verwendung eines Batch-Scripts, das Sie von mysql aus ausführen, weil Sie nichts
tun können, falls ein Fehler auftritt, außer das Programm zu beenden (oder den
Fehler zu ignorieren, was aber nicht Sinn der Anfrage gewesen sein kann).
3.10
Funktionen, die MySQL nicht unterstützt
Dieser Abschnitt beschreibt einige Funktionen, die es in anderen Datenbanksystemen gibt, die in MySQL aber nicht unterstützt werden. Hier wird gezeigt, was
fehlt, und welche Lösungsmöglichkeiten es dafür gibt. Im Allgemeinen fehlen
Funktionen in MySQL, wenn sie eine negative Auswirkung auf die Leistung
haben. Mehrere Punkte auf der Liste sind jedoch von den Entwicklern bereits
geplant und werden vermutlich realisiert, sobald es die Zeit erlaubt und man
damit keine wesentlichen Leistungseinbußen verursacht.
•
Subselects. Ein Subselect ist ein in ein anderes SELECT eingebettetes SELECT,
wie beispielsweise in der folgenden Anfrage gezeigt:
SELECT * FROM score
WHERE event_id IN (SELECT event_id FROM event WHERE type = "T")
Subselects sind für MySQL 3.24 angekündigt. Bis dahin können viele Anfragen, die ein Subselect benötigen, auch mit Hilfe von Joins realisiert werden.
Weitere Informationen finden Sie weiter oben im Abschnitt »Subselects als
Joins formulieren«.
•
Transaktionen und Commit/Rollback. Eine Transaktion ist eine Folge von
SQL-Anweisungen, die als Einheit ausgeführt werden, ohne dass andere Clients sie unterbrechen können. Mit Hilfe der Commit/Rollback-Fähigkeit stellen Sie sicher, dass die Anweisungen komplett als Einheit oder überhaupt nicht
ausgeführt werden. Schlägt also eine Anweisung in der Transaktion fehl, werden alle bis zu diesem Zeitpunkt ausgeführten Anweisungen der Einheit rückgängig gemacht.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
229
MySQL setzt automatisch Sperren für einzelne SQL-Anweisungen, um zu
verhindern, dass die Clients sich gegenseitig stören. (Beispielsweise können
zwei Clients nicht gleichzeitig in dieselbe Tabelle schreiben.) Darüber hinaus
können Sie mit LOCK TABLES und UNLOCK TABLES Anweisungen zu einer einzigen Einheit zusammenfassen, so dass Sie Operationen ausführen können, für
die die Nebenläufigkeitssteuerung einzelner Anweisungen nicht ausreichend
ist. Problematisch dabei ist, dass MySQL die Anweisungen nicht automatisch
für Sie gruppiert und Sie die Anweisungen nicht rückgängig machen können,
falls eine davon fehlschlägt.
Um die Bedeutung von Transaktionen zu verstehen, stellen Sie sich vor, Sie
hätten ein Bekleidungsgeschäft und aktualisierten die Bestände, wenn einer
Ihrer Verkäufer etwas verkauft. Das folgende Beispiel demonstriert die Problematik, die auftreten kann, wenn mehrere Verkäufer die Datenbank gleichzeitig aktualisieren (wobei vorausgesetzt wird, dass es einen Anfangsbestand
von 47 Hemden gibt):
t1
Verkäufer 1 verkauft 3 Hemden
t2
Verkäufer 1 ermittelt den aktuellen Hemdenstand (47)
SELECT menge FROM bestand WHERE artikel = "hemd"
t3
Verkäufer 2 verkauft 2 Hemden
t4
Verkäufer 2 ermittelt den aktuellen Hemdenstand (47)
t5
Verkäufer 1 berechnet den neuen Lagerbestand als 47-3=44 und setzt den
Hemdenzähler auf 44:
UPDATE bestand SET menge = 44 WHERE artikel = "hemd"
t6
Verkäufer 2 berechnet den neuen Lagerbestand als 47-2=45 und setzt den
Hemdenzähler auf 45:
UPDATE bestand SET menge = 45 WHERE artikel = "hemd"
Nach Ablauf dieser Ereignisse haben Sie fünf Hemden verkauft (was nicht
schlecht ist), aber der Lagerbestand steht auf 45 statt auf 42 (was schlecht ist).
Das Problem dabei ist, dass Sie für das Nachlesen des Lagerbestands in einer
Anweisung und die Aktualisierung des Werts in einer zweiten Anweisung
eine Transaktion für mehrere Anweisungen brauchen. Die Aktion in der zweiten Anweisung ist von dem in der ersten Anweisung ermittelten Wert abhängig. Wenn aber während überlappender Zeitrahmen separate Transaktionen
auftreten, können sich die Anweisungen der verschiedenen Transaktionen vermischen und sich gegenseitig beeinflussen. In einer transaktionsgesteuerten
Datenbank können die Anweisungen der verschiedenen Verkäufer als Transaktionen ausgeführt werden, und die Anweisungen von Verkäufer 2 werden
erst ausgeführt, nachdem die Anweisungen von Verkäufer 1 vollständig abgeschlossen sind. In MySQL gibt es zwei Möglichkeiten, diesen Sachverhalt zu
realisieren:
230
Funktionen, die MySQL nicht unterstützt
•
Lösungsansatz 1: Ausführung einer Gruppe von Anweisungen als Einheit.
Sie können Anweisungen zusammenfassen und sie als atomare Einheit ausführen, indem Sie sie zwischen LOCK TABLES und UNLOCK TABLES schreiben: Sperren
Sie alle betroffenen Tabellen, führen Sie Ihre Anfragen aus und heben Sie die
Sperren auf. Damit wird verhindert, dass jemand die Tabellen benutzt, während
Sie die Sperre dafür halten. Mit dieser Tabellensperre sieht die Situation wie folgt
aus:
t1
Verkäufer 1 verkauft 3 Hemden
t2
Verkäufer 1 fordert eine Sperre an und ermittelt den aktuellen Hemdenstand
(47)
LOCK TABLES bestand WRITE
SELECT menge FROM bestand WHERE artikel = "hemd"
t3
Verkäufer 2 verkauft 2 Hemden
t4
Verkäufer 2 versucht, eine Sperre anzufordern; das wird zurückgewiesen,
weil Verkäufer 1 bereits eine Sperre hält:
LOCK TABLES bestand WRITE
t5
Verkäufer 1 berechnet den neuen Lagerbestand als 47-3=44 und setzt den
Hemdenzähler auf 44:
UPDATE bestand SET menge = 44 WHERE artikel = "hemd"
UNLOCK TABLES
t6
Jetzt kann Verkäufer 2 eine Sperre anfordern. Er ermittelt den neuen Lagerbestand (44):
SELECT menge FROM bestand WHERE artikel = "hemd"
Verkäufer 2 berechnet den neuen Lagerbestand als 47-2=45 und setzt den
Hemdenzähler auf 45:
UPDATE bestand SET menge = 45 WHERE artikel = "hemd"
t7
Verkäufer 2 berechnet den neuen Lagerbestand als 44-2=42, setzt den Hemdenzähler auf 42 und gibt die Sperre frei:
UPDATE bestand SET menge = 42 WHERE artikel = "hemd"
UNLOCK TABLES
Auf diese Weise ist sichergestellt, dass die beiden Transaktionen nicht vermischt werden und der Lagerbestand korrekt gesetzt wird. Wir verwenden hier
eine Schreibsperre (WRITE), weil wir die Tabelle bestand ändern wollen. Wenn
Sie die Tabellen nur lesen, können Sie statt dessen auch eine Lesesperre (READ)
setzen. Bei einer Lesesperre können die anderen Clients die Tabellen lesen,
während Sie diese benutzen, aber sie können nichts hineinschreiben.
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
231
In dem oben gezeigten Beispiel würde Verkäufer 2 vermutlich keinen Unterschied hinsichtlich der Geschwindigkeit bemerken, weil Transaktionen kurz
sind und schnell ausgeführt werden. Sie sollten immer vermeiden, Tabellen
längere Zeit zu sperren.
Wenn Sie mehrere Tabellen verwenden, müssen Sie alle sperren, damit Sie
gruppierte Anfragen ausführen können. Wenn Sie nur aus einer bestimmten
Tabelle lesen, müssen Sie lediglich eine einzelne Lesesperre setzen. Angenommen, Sie haben mehrere Anfragen, die Änderungen an der Tabelle
bestand vornehmen sollen, und Sie wollen außerdem Daten aus der Tabelle
kunden lesen. In diesem Fall setzen Sie für die Tabelle bestand eine Schreibsperre und eine Lesesperre für die Tabelle kunden:
LOCK TABLES bestand WRITE, kunden READ
...
UNLOCK TABLES
Dafür ist es erforderlich, die Tabellen selbst zu sperren und die Sperre auch
wieder aufzuheben. Ein Datenbanksystem mit Transaktionsunterstützung
würde das automatisch für Sie erledigen. Die Gruppierung von Anweisungen
zur Ausführung als Einheit ist jedoch in transaktionsorientierten Datenbanken
auf dieselbe Weise zu planen.
•
Lösungsansatz 2: Verwenden relativer statt absoluter Aktualisierungen.
Die zweite Möglichkeit, wie Sie verhindern, dass sich Anweisungen aus mehreren Transaktionen vermischen, besteht darin, die Abhängigkeit zwischen den
Anweisungen aufzuheben. Das ist zwar nicht immer möglich, aber in unserem
hier gezeigten Beispiel funktioniert es. Für die in Lösungsansatz 1 verwendete
Aktualisierung hat die Transaktion die aktuelle Bestandsebene gesperrt, den neuen Wert abhängig von den verkauften Hemden berechnet und dann den Bestand
mit dem neuen Wert aktualisiert. Das kann innerhalb eines Schritts erfolgen, indem der Hemdenzähler einfach relativ zum aktuellen Wert aktualisiert wird:
t1
Verkäufer 1 verkauft drei Hemden
t2
Verkäufer 1 verringert den Hemdenzähler um drei:
t3
Verkäufer 2 verkauft zwei Hemden
t4
Verkäufer 2 verringert den Hemdenzähler um zwei:
UPDATE bestand SET menge = menge – 3 WHERE artikel = "hemd"
UPDATE bestand SET menge = menge – 2 WHERE artikel = "hemd"
Wie Sie sehen, braucht man überhaupt keine Transaktionen über mehrere
Anweisungen und muss damit auch keine Tabellen sperren, um Transaktionsfähigkeit zu simulieren. Falls Sie hauptsächlich Transaktionen dieser Form
ausführen, können Sie möglicherweise auch ganz ohne Transaktionen auskommen.
232
Funktionen, die MySQL nicht unterstützt
Das obige Beispiel zeigt, wie Sie in bestimmten Situationen ohne Transaktionen auskommen. Das heißt natürlich nicht, dass es keine Situationen gibt, in
denen Transaktionen erforderlich werden. Das typische Beispiel dafür ist eine
Geldüberweisung, im Zuge derer Geld von einem Konto auf ein anderes
Konto gebucht wird. Angenommen, Bill schreibt einen Scheck für Bob über
100 DM und Bob reicht den Scheck ein. Das Konto von Bill wird mit 100 DM
belastet, und dem Konto von Bob werden 100 DM gutgeschrieben.
UPDATE account SET balance = balance – 100 WHERE name = "Bill"
UPDATE account SET balance = balance + 100 WHERE name = "Bob"
Tritt zwischen der Ausführung der beiden Anweisungen ein Absturz auf, ist
die Transaktion unvollständig. Ein Datenbanksystem mit echten Transaktionen und Commit/Rollback-Fähigkeit könnte diese Situation kompensieren.
(Zumindest theoretisch. Auch hier müssten Sie herausfinden, welche Transaktionen eingegeben wurden, und sie wiederholen, aber zumindest müssten Sie
sich keine Gedanken über unvollständig ausgeführte Transaktionen machen.)
In MySQL ermitteln Sie den Status von Transaktionen bei Systemausfällen
anhand des Ereignisprotokolls.
•
Fremdschlüssel und referentielle Integrität. Mit Hilfe eines Fremdschlüssels legen Sie fest, dass ein Schlüssel in einer Tabelle in einer bestimmten Beziehung zu einem Schlüssel in einer anderen Tabelle steht. Die referentielle
Integrität erlaubt Ihnen, Einschränkungen festzulegen, was mit der Tabelle mit
dem Fremdschlüssel gemacht werden darf. Beispielsweise enthält die scoreTabelle in unserer Beispieldatenbank samp_db eine Spalte student_id, die wir
nutzen, um die Punktwertdatensätze mit den Schülern in der student-Tabelle
in eine Beziehung zu setzen. student_id würde in Datenbanken, die dieses
Konzept unterstützen, als Fremdschlüssel deklariert, und wir würden eine Beschränkung dafür festlegen, in der Form, dass kein Punktewertdatensatz für einen Schüler eingetragen werden kann, der in der student-Tabelle nicht
existiert. Darüber hinaus könnten wir ein kaskadenförmiges Löschen erlauben,
d.h., wenn ein Schüler aus der student-Tabelle gelöscht wird, werden auch alle
Punktwertdatensätze für diesen Schüler automatisch aus der score-Tabelle
gelöscht.
Mit Hilfe von Fremdschlüsseln gewährleisten Sie die Konsistenz Ihrer Daten.
Außerdem bieten Fremdschlüssel eine gewisse Bequemlichkeit. Der Grund
dafür, warum sie in MySQL nicht unterstützt werden, sind die negativen Auswirkungen auf die Leistung und die Wartungsfreundlichkeit der Datenbank.
(Im MySQL Reference Manual finden Sie genauere Informationen zu dieser
Begründung.) Beachten Sie, dass sich diese Betrachtung der Fremdschlüssel
von der in anderer Datenbankliteratur unterscheidet, wo sie häufig als »unabdingbar« bezeichnet werden. Die Entwickler von MySQL teilen diese Ansicht
nicht. Wenn Sie anders denken, sollten Sie sich besser für eine andere Datenbank entscheiden, in der Fremdschlüssel unterstützt werden. Haben Ihre
Kapitel 3 • Syntax und Verwendung von SQL in MySQL
233
Daten beispielsweise sehr komplexe Beziehungen, dann wollen Sie diese
Abhängigkeiten sicher nicht unbedingt selbst in Ihren Anwendungen implementieren (auch wenn dazu kaum mehr erforderlich ist als einige zusätzliche
DELETE-Anweisungen).
MySQL unterstützt keine Fremdschlüssel, parst aber FOREIGN KEY-Klauseln in
CREATE TABLE-Anweisungen. (Damit wird es einfacher, SQL-Code von anderen Datenbanken in MySQL zu portieren.) MySQL erzwingt keine Fremdschlüssel und unterstützt auch kein kaskadenförmiges Löschen.
Die Einschränkungen, die durch die Fremdschlüssel erzwungen werden sollen, sind größtenteils auch ganz einfach durch die Anwendungslogik nachzuvollziehen. Manchmal ist es einfach eine Frage der Dateneingabe. Wenn Sie
beispielsweise neue Datensätze in unsere score-Tabelle eingeben, werden Sie
sehr wahrscheinlich keine Werte für nicht existente Schüler eintragen. Um
Punkte einzutragen, würden Sie also von einer Liste der Schüler aus der student-Tabelle ausgehen, für jeden den Punktewert ermitteln und unter Verwendung der Schüler-IDs einen neuen Datensatz in die score-Tabelle eintragen.
Auf diese Weise besteht keine Gefahr, dass ein Datensatz für einen nicht existierenden Schüler angelegt wird. Sie würden ja nicht einfach einen Punktedatensatz erfinden, um ihn in die score-Tabelle einzutragen.
Um kaskadenförmiges Löschen zu ermöglichen, brauchen Sie auch eine
eigene Anwendungslogik. Angenommen, sie wollen den Schüler mit der
Nummer 13 löschen. Damit sollen auch alle Punktedatensätze für diesen
Schüler entfernt werden. In einer Datenbank, die kaskadenförmiges Löschen
unterstützt, würden Sie den Datensatz in der student-Tabelle sowie alle zugehörigen Datensätze in der scores-Tabelle mit der folgenden Anweisung
löschen:
DELETE FROM student WHERE student_id = 13
Die Datensätze für Schüler 13 in der score-Tabelle werden automatisch
gelöscht. In MySQL nehmen Sie dieses sekundäre Löschen mit einer expliziten DELETE-Anweisung vor:
DELETE FROM student WHERE student_id = 13
DELETE FROM score WHERE student_id = 13
•
Gespeicherte Prozeduren und Trigger. Eine gespeicherte Prozedur ist SQLCode, der kompiliert und im Server gespeichert wird. Später kann man darauf
zugreifen, ohne dass er erneut vom Client übertragen und geparst werden
müsste. Außerdem ist es möglich, Änderungen an einer Prozedur vorzunehmen, um damit die Client-Anwendungen entsprechend zu beeinflussen. Trigger erlauben, eine gespeicherte Prozedur zu aktivieren, wenn ein bestimmtes
Ereignis auftritt (beispielsweise wenn ein Datensatz aus einer Tabelle gelöscht
wird). Das könnte sinnvoll sein, wenn Sie nach einem Löschvorgang einen
234
Funktionen, die MySQL nicht unterstützt
komplexen Überblick neu anlegen wollen, in dem der Datensatz zuvor enthalten war. Die Einführung gespeicherter Prozeduren ist für MySQL geplant.
•
Sichten. Eine Sicht ist eine logische Einheit, die sich wie eine Tabelle verhält,
aber keine Tabelle ist. Sie bietet eine Möglichkeit, Spalten aus anderen Tabellen so anzuzeigen, als wären sie alle in derselben Tabelle enthalten. Sichten
werden manchmal auch als virtuelle Tabellen bezeichnet. Sie sind für MySQL
vorgesehen.
•
Berechtigungen und Sperren auf Datensatzebene. MySQL unterstützt unterschiedliche Berechtigungsebenen, von globalen bis hin zu Berechtigungen
für einzelne Datenbanken, Tabellen und Spalten. Berechtigungen auf Datensatzebene sind nicht möglich. Sie können jedoch in Ihren Anwendungen mit
Hilfe der Funktionen GET_LOCK()und RELEASE_LOCK() kooperative Datensatzsperren implementieren. Weitere Informationen finden Sie im Abschnitt über
GET_LOCK() in Anhang C.
•
-- als Kommentar. Dieser Kommentarstil wird nicht unterstützt, weil es sich
dabei um ein mehrdeutiges Konstrukt handelt. In MySQL 3.23.2 wird ein
Kommentar, der mit zwei Trennstrichen beginnt, jedoch akzeptiert. Weitere
Informationen dazu finden Sie weiter oben im Abschnitt »Kommentare«.
Herunterladen