C-Programmierung lernen. 9783827314055, 3827314054, 3827313422, 3827317762, 382731951X, 3827317940, 3827320259


191 31 5MB

German Pages 344 Year 1998

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
MySQL lernen - Anfangen, anwenden, verstehen......Page 3
3.9 Fragen......Page 5
C.3 Weitere fehlende Elemente......Page 6
Stichwortverzeichnis......Page 7
Vorwort......Page 9
1.2 Was ist eine Datenbank?......Page 13
1.3 Vom Karteikasten zum RDBMS......Page 14
1.4.1 Tabellen......Page 17
1.4.3 Indizes......Page 18
1.4.4 Weitere Elemente......Page 19
1.4.5 Standalone vs. Client/Server......Page 20
1.5 Einsatzbeispiele......Page 21
1.6.1 Was ist MySQL?......Page 22
1.6.3 Elemente von MySQL......Page 23
1.6.4 Lizenzen und Kosten bei MySQL......Page 24
2.2 Von der Realität zum Modell – Das Beispielszenario......Page 25
2.2.2 Das Modell......Page 26
2.3.1 Voraussetzungen......Page 28
2.3.2 Das interaktive Programm – mysql......Page 29
2.4 SQL......Page 31
2.4.2 Elemente von SQL......Page 32
2.5 Anlegen einer Datenbank......Page 33
2.6.1 Anlegen einer Tabelle......Page 38
2.6.2 Datentypen......Page 43
2.6.3 Tabellen des Modells......Page 49
2.6.4 CREATE TABLE – komplett......Page 60
2.6.5 Exkurs: Normalisierung......Page 66
2.7 Fragen......Page 70
3.1 Was Sie in diesem Kapitel lernen......Page 71
3.2 Eintragen, Abfragen, Ändern, Löschen – die Basis......Page 72
3.3 Daten eintragen......Page 78
3.4 Daten abfragen......Page 81
3.4.1 Select-Ausdruck......Page 82
3.4.3 WHERE......Page 85
3.4.4 ORDER BY......Page 89
3.4.5 Aggregatsfunktionen und GROUP BY......Page 90
3.4.6 HAVING......Page 93
3.4.7 LIMIT......Page 94
3.4.8 Weitere Elemente und Optionen......Page 95
3.5.1 UPDATE......Page 97
3.6.1 DELETE......Page 99
3.7.1 Stringfunktionen......Page 100
3.7.2 Stringvergleiche......Page 102
3.7.3 Numerische Funktionen......Page 103
3.7.4 Datums- und Uhrzeitfunktionen......Page 104
3.7.6 Andere Funktionen......Page 106
3.8.1 Intermezzo – Kommentare und SQL-Dateien......Page 108
3.8.2 Joins......Page 110
3.9 Fragen......Page 116
4.1 Was Sie in diesem Kapitel lernen......Page 117
4.2 Strukturänderungen......Page 118
4.2.1 ALTER TABLE......Page 120
4.2.2 RENAME TABLE......Page 123
4.2.4 DROP DATABASE......Page 124
4.3.1 Sinn und Funktionsweise von Indizes......Page 125
4.3.2 Anlegen von Indizes......Page 128
4.3.3 Anzeigen von Indizes......Page 131
4.3.4 Volltext-Recherche......Page 132
4.3.5 Löschen von Indizes......Page 135
4.4.1 Anzeigen von Strukturdaten......Page 136
4.4.2 Anzeigen weiterer Informationen......Page 144
4.5.1 OPTIMIZE TABLE......Page 153
4.6 Fragen......Page 154
5.1 Was Sie in diesem Kapitel lernen......Page 155
5.2.1 mysql......Page 156
5.2.2 mysqladmin......Page 166
5.2.3 mysqlshow......Page 170
5.3 phpMyAdmin......Page 173
5.4.1 SELECT ... INTO......Page 179
5.4.2 LOAD DATA INFILE......Page 186
5.4.3 mysqldump......Page 190
5.4.4 mysqlimport......Page 195
5.5 Schnittstellen......Page 197
5.5.1 Perl......Page 198
5.5.2 C++......Page 208
5.5.3 MyODBC......Page 212
5.6 Fragen......Page 220
6.1 Was Sie in diesem Kapitel lernen......Page 221
6.2.1 BACKUP/RESTORE......Page 222
6.2.2 mysqlhotcopy......Page 224
6.2.4 Ausblick......Page 225
6.3.1 CHECK TABLE......Page 226
6.3.2 REPAIR TABLE......Page 227
6.3.3 myisamchk......Page 228
6.3.4 mysqlcheck......Page 234
6.4.1 Berechtigungen......Page 236
6.4.2 GRANT/REVOKE......Page 240
6.4.3 Systemtabellen......Page 244
6.4.4 Benutzer, Hosts und Kennwörter......Page 251
6.4.5 Sicherung auf Betriebssystem-Ebene......Page 255
6.5 Fragen......Page 256
7.1 Was Sie in diesem Kapitel lernen......Page 257
7.2.1 Linux......Page 258
7.2.2 Apache......Page 259
7.2.3 MySQL......Page 264
7.2.4 PHP......Page 265
7.2.5 IIS-Nutzung......Page 270
7.3 MySQL und PHP......Page 274
7.3.1 PHP und HTML......Page 275
7.3.2 PHP-Überblick......Page 277
7.3.3 Zugriff auf die Datenbank......Page 282
7.3.4 Die PHP-API für MySQL......Page 295
A.1 Download der Dateien......Page 297
A.2 Installation unter Red Hat Linux......Page 299
A.3 Installation unter Windows......Page 300
Anhang B: Reservierte Wörter in MySQL......Page 303
C.1 Unterabfragen......Page 307
C.2 Transaktionen......Page 311
C.3.2 Stored Procedures......Page 312
C.3.4 Sichten......Page 313
D.1 Neu in MySQL 4.0......Page 315
D.2 Geplant in MySQL 4.1......Page 317
E.2 Bücher zu MySQL......Page 319
E.3 Newsgroups......Page 320
F.1 Kapitel 2......Page 321
F.2 Kapitel 3......Page 322
F.4 Kapitel 5......Page 324
F.5 Kapitel 6......Page 325
Aggregatsfunktion - ASCII......Page 327
AUTO_INCREMENT - Constraint......Page 328
Cross Join - DCL......Page 329
DDL - Gruppierung......Page 330
Host - Normalform......Page 331
Normalisierung - Promt......Page 332
RAID - Sicht......Page 333
skalare Funktion - Trigger......Page 334
Vergleichsoperator - Zeile......Page 335
A - C......Page 337
C - D......Page 338
D - J......Page 339
K - N......Page 340
O - S......Page 341
S - U......Page 342
V - numerisch......Page 343
Ins Internet: Weitere Infos zum Buch, Downloads, etc.......Page 0
© Copyright......Page 344
Recommend Papers

C-Programmierung lernen.
 9783827314055, 3827314054, 3827313422, 3827317762, 382731951X, 3827317940, 3827320259

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

Sandini Bib

MySQL lernen

Sandini Bib

Die Lernen-Reihe In der Lernen-Reihe des Addison-Wesley Verlages sind die folgenden Titel bereits erschienen bzw. in Vorbereitung: André Willms C-Programmierung lernen 432 Seiten, ISBN 3-8273-1405-4 André Willms C++-Programmierung lernen 408 Seiten, ISBN 3-8273-1342-2 Guido Lang, Andreas Bohne Delphi 6 lernen 432 Seiten, ISBN 3-8273-1776-2 Jürgen Bayer Programmieren lernen 528 Seiten, ISBN 3-8273-1951-X Judy Bishop Java lernen 768 Seiten, ISBN 3-8273-1794-0 Michael Ebner SQL lernen 264 Seiten, ISBN 3-8273-2025-9 René Martin XML und VBA lernen 336 Seiten, ISBN 3-8273-1952-8 René Martin VBA mit Word 2002 lernen 393 Seiten, ISBN 3-8273-1897-1 Olivia Adler, Hartmut Holzgraefe PHP lernen 288 Seiten, ISBN 3-8273-2000-3 Patrizia Sabrina Prudenzi VBA mit Excel 2000 lernen 512 Seiten, ISBN 3-8273-1572-7 Frank Eller C# lernen 384 Seiten, ISBN 3-8273-2045-3 Jörg Krause ASP.NET lernen 416 Seiten, ISBN 3-8273-2018-6

Sandini Bib

Thomas Demmig

MySQL lernen Anfangen, anwenden, verstehen

An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam

Sandini Bib

Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Produkt wurde auf chlorfrei gebleichtem Papier gedruckt.

10 9 8 7 6 5 4 3 2 1 06 05 04 03 ISBN 3-8273-2070-4 © 2003 by Addison Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Korrektorat: Lektorat: Herstellung: Satz: Druck und Verarbeitung: Printed in Germany

4

Barbara Thoben, Köln Christine Depta, Unterschleißheim Martin Asbach, [email protected] Ulrike Hempel, [email protected] mediaService, Siegen Media Print, Paderborn

Sandini Bib

I

Inhaltsverzeichnis

le

n e rn

V

Vorwort

9

1 1.1 1.2 1.3 1.4 1.5 1.6

Datenbanken Was Sie in diesem Kapitel lernen Was ist eine Datenbank? Vom Karteikasten zum RDBMS Aufbau einer Datenbank Einsatzbeispiele MySQL

13 13 13 14 17 21 22

2 2.1 2.2 2.3 2.4 2.5 2.6 2.7

Einsteigen in MySQL Was Sie in diesem Kapitel lernen Von der Realität zum Modell – Das Beispielszenario Verbinden mit MySQL SQL Anlegen einer Datenbank Erstellen der Tabellen Fragen

25 25 25 28 31 33 38 70

3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9

Daten Was Sie in diesem Kapitel lernen Eintragen, Abfragen, Ändern, Löschen – die Basis Daten eintragen Daten abfragen Daten ändern Daten löschen Funktionen in SQL Komplexe Abfragen Fragen

71 71 72 78 81 97 99 100 108 116

5

Sandini Bib

6

4 4.1 4.2 4.3 4.4 4.5 4.6

Strukturelles Was Sie in diesem Kapitel lernen Strukturänderungen Indizes Systeminformationen Optimieren von Tabellen Fragen

117 117 118 125 136 153 154

5 5.1 5.2 5.3 5.4 5.5 5.6

Zugriffsmöglichkeiten Was Sie in diesem Kapitel lernen Admin-Tools zu MySQL phpMyAdmin Import und Export Schnittstellen Fragen

155 155 156 173 179 197 220

6 6.1 6.2 6.3 6.4 6.5

Sicherheit Was Sie in diesem Kapitel lernen Datensicherung Datenprüfung und Datenreparatur Datenschutz Fragen

221 221 222 226 236 256

7 7.1 7.2 7.3

MySQL und das Web Was Sie in diesem Kapitel lernen LAMP/WAMP MySQL und PHP

257 257 258 274

A A.1 A.2 A.3

Installation von MySQL Download der Dateien Installation unter Red Hat Linux Installation unter Windows

297 297 299 300

B

Reservierte Wörter in MySQL

303

C C.1 C.2 C.3

Was fehlt in MySQL? Unterabfragen Transaktionen Weitere fehlende Elemente

307 307 311 312

, Q K D OW V Y H U ] HL F K Q L V

Sandini Bib

D Neu in MySQL 4.x D.1 Neu in MySQL 4.0 D.2 Geplant in MySQL 4.1

315 315 317

E E.1 E.2 E.3

Hilfe zu MySQL Dokumentation zu MySQL Bücher zu MySQL Newsgroups

319 319 319 320

F F.1 F.2 F.3 F.4 F.5

Lösungen zu den Fragen Kapitel 2 Kapitel 3 Kapitel 4 Kapitel 5 Kapitel 6

321 321 322 324 324 325

G

Glossar

327

Stichwortverzeichnis

337

,QKDOWVYHU]HLFKQLV

7

Sandini Bib

Sandini Bib

V

Vorwort

le

n e rn

MySQL ist wohl die am häufigsten verwendete freie relationale Datenbank. Sie ist schnell, stabil, zuverlässig und (meist) kostenlos. Viele Webserver greifen im Hintergrund auf eine MySQL-Datenbank zu. MySQL hat nicht alles, was andere Datenbanksysteme bieten. Es beschränkt sich auf die wichtigsten Dinge, die vor allem als Backend-Datenbank nötig sind. Dafür ist das oberste Ziel von MySQL Geschwindigkeit und Stabilität. Die Verwendung von MySQL ist nicht schwierig, sofern man etwas Grundwissen über Datenbanken besitzt. Dieses Buch will Ihnen dieses Grundwissen und natürlich vor allem die Nutzung von MySQL näher bringen. Dabei benötigen Sie wenig Vorkenntnisse über Datenbanken, es reicht, wenn Sie mit Ihrem Betriebssystem umgehen können. Im Laufe des Buches werde ich immer wieder auch ein wenig in die Tiefe gehen, weil ich der Meinung bin, dass man dadurch häufig ein besseres Verständnis dafür erhält, warum etwas so ist, wie es ist. MySQL läuft auf sehr vielen verschiedenen Betriebssystemen. In diesem Buch werden wir uns hauptsächlich mit Linux (speziell Red Hat) und Windows befassen, es sollte aber auch kein Problem sein, das Erlernte für andere Betriebssysteme umzusetzen. Der Aufbau des Buches sieht wie folgt aus: 1. Im ersten Kapitel geht es ganz allgemein um Datenbanken. Es erklärt

Ihnen, was sie sind und woraus sie bestehen. Weiterhin wird MySQL vorgestellt, die Idee und Geschichte dahinter erzählt und gezeigt, was MySQL kann und was es nicht kann. 2. Im zweiten Kapitel lernen wir SQL kennen, die Datenbankabfra-

gesprache schlechthin. Eine Musikschulverwaltung als Beispielszenario wird Schritt für Schritt in eine Datenbankstruktur umgesetzt. Dabei lernen Sie den Zugang zu MySQL kennen und erfahren, wie die Strukturen erstellt werden können.



Sandini Bib

3. Das dritte Kapitel befasst sich mit den Daten, den eigentlichen

Hauptakteuren. Eintragen, Ändern, Löschen und – ganz wichtig – Abfragen von Daten sind Thema dieses Abschnitts. Schwerpunkt hierbei ist der SELECT-Befehl, mit dem Sie sowohl einfache wie auch komplexe Abfragen schreiben können, um die gewünschten Informationen zu erhalten. 4. Das vierte Kapitel erläutert Ihnen Strukturen und deren Bedeutung.

Es beschreibt Änderungen an den Datenstrukturen gefolgt von Indizes, die für die Geschwindigkeit einer Datenbank von äußerster Wichtigkeit sind. Schließlich zeigt es auch noch, wie Sie sich die Systeminformationen von MySQL beschaffen können. 5. Kapitel fünf befasst sich mit den Verbindungsmöglichkeiten zu My-

SQL. Hier erläutere ich die verschiedenen Programme mit ihren unterschiedlichen Aufgabenbereichen. Wie Sie Daten „am Stück“ aus MySQL heraus oder in es hineinbekommen ist ebenfalls Gegenstand dieses Kapitels. Danach werden ich Ihnen die wichtigsten APIs für Perl und C++ vorstellen. Diese sind nötig, um sich aus eigenen Programmen mit dem MySQL-Server verbinden zu können. 6. Das sechste Kapitel befasst sich mit Datensicherung und -sicherheit.

Um im Falle eines Fehlers keinen Datenverlust zu haben, ist eine regelmäßige Sicherung absolut notwendig. Das Kapitel beschreibt, was für Möglichkeiten Sie haben und wie Sie vorgehen können. Der zweite Teil des Kapitels beschäftigt sich damit, wie Sie die Daten vor den Zugriffen Unbefugter schützen können. 7. Im siebten Kapitel schließlich geht es um das Zusammenspiel von

MySQL mit einem Web-Server. Der Aufbau eines LAMP- oder WAMPSystems wird hier ebenso beschrieben wie der Zugriff auf MySQL über die Programmiersprache PHP, welche gerne für solche Aufgaben verwendet wird. 8. Die Anhänge enthalten Informationen über die Installation von

MySQL, reservierte Wörter, fehlende Elemente des Servers und die Möglichkeiten für Workarounds, Neuerungen und Planungen für die Version 4.x sowie weitere Hilfsquellen. Am Ende der meisten Kapitel finden Sie einige Fragen, mit denen Sie überprüfen können, ob Sie den Inhalt des Kapitels verstanden haben. Die Antworten sind am Ende des Buches zusammengefasst, so dass Sie selber eine Kontrolle Ihres Wissens vornehmen können. Nachdem Sie alle Kapitel durchgearbeitet haben, sollten Sie problemlos mit MySQL umgehen und auch aufwändigere Aufgaben erledigen können. Die Modellierung von Daten, Administration des Servers, der Imund Export sowie die Verbindung zu Web-Servern sollten nun für Sie



9RUZRUW

Sandini Bib

nicht mehr völlig unverständlich sein. Und wenn es doch einmal Probleme gibt, wissen Sie dann auch, wo Ihnen geholfen werden kann. Damit Sie nicht all die beschriebenen Programme und Tools aus dem Internet laden müssen, haben ich für Sie fast alle erwähnten Dateien auf der beiliegenden CD-ROM zusammengestellt. Sie finden dort natürlich MySQL, aber auch phpMyAdmin, MySQL++, MyODBC, Perl, PHP und den Apache-Webserver. Unter dem jeweiligen Verzeichnisnamen liegen die für die Installation notwendigen Dateien für Linux und Windows. Die Vorgehensweise zum Einrichten ist in den entsprechenden Kapiteln beschrieben. Weiterhin sind sämtliche im Buch aufgeführten Skripte ebenfalls auf der CD-ROM zu finden. Um Ihnen die Orientierung im Buch zu erleichtern, finden Sie am Rand der Seiten immer wieder bestimmte Symbole (oder Icons), die einen besonderen Hinweischarakter haben. Die folgenden Icons werden in diesem Buch verwendet: Das Referenz-Icon verweist auf Dateien, die Sie auf der CD-ROM finden. Dieses Symbol steht für einen Hinweis, der Sie auf besonders nützliche Informationen aufmerksam machen soll. Dieses Icon schließlich stellt eine Warnung dar, um Sie auf mögliche Fehlerquellen hinzuweisen.

9RUZRUW



Sandini Bib

Sandini Bib

1

Datenbanken

le

n e rn

Was ist das – eine Datenbank? Und was ist das Besondere an MySQL? Dies ist der Inhalt dieses Kapitels. Datenbanken sind ein wichtiger Teil der Computerlandschaft und ohne sie wäre so gut wie jedes größere Programm – vor allem Geschäftsanwendungen – hilflos.

1.1

Was Sie in diesem Kapitel lernen

In diesem Kapitel geht es erst einmal um einen allgemeinen Überblick über Daten und deren Organisation. Es wird erläutert, was eine Datenbank ist und wie allgemein oder speziell dieser Begriff genutzt wird. Dann wird der Weg von der „guten alten Zeit“ hin zur modernen Datenbank skizziert und gezeigt, wie eine Datenbank aussieht und was sie vollbringt. Anschließend zähle ich ein paar grundlegende Elemente einer Datenbank auf, um dann einige Einsatzbeispiele zu beschreiben. Schließlich geht es um MySQL (darum sollte es in diesem Buch eigentlich die ganze Zeit gehen), wie es entstand, was dahinter steckt und warum es meist kostenlos ist.

1.2

Was ist eine Datenbank?

Im Prinzip ist jede halbwegs strukturierte Zusammenstellung von Informationen eine Datenbank. Dabei muss sie nicht einmal elektronisch sein. Das Telefonbuch ist eine Datenbank, denn man findet für eine Person oder eine Firma die Telefonnummer. Ein Fahrplan ist eine Datenbank: durch ihn lässt sich herausbekommen, wie und wann man von A nach B kommt. Ein Lexikon ist eine Datenbank: zu vielen Stichworten erhält man Erklärungen und Beispiele. Heutzutage wird der Begriff „Datenbank“ meist etwas eingeschränkter verwendet. Im Allgemeinen versteht man darunter eine oder mehrere

:DV 6LH LQ GLHVHP .DSLWHO OHUQHQ



Sandini Bib

Listen mit Daten, die mehr oder weniger sinnvoll sortiert sind und aus denen man die gewünschten Informationen möglichst leicht herausbekommt. Zudem sind die Daten über einen Computer erreichbar. So kann man zum Beispiel ein Adressprogramm auf seinem PC anweisen, die Adressen der wichtigsten Freunde auszugeben, damit man diese im nächsten Urlaub anschreiben kann. Oder man lässt sich heraussuchen, auf welcher der vielen CDs, die man besitzt, ein bestimmtes Lied zu finden ist. Und wenn man sich dabei auch nur noch an Bruchstücke des Titels erinnert, kann das Programm zur CD-Verwaltung trotzdem eine kleine Auswahl an möglichen CDs bieten, die man gemeint hat.

1.3

Vom Karteikasten zum RDBMS

Das, was heute in einer Datenbank auf dem Computer verwaltet wird, befand sich früher meist in einem Karteikasten, Aktenschrank oder Archiv. Die Karteikarten oder Akten waren in einem bestimmten System sortiert, zum Beispiel nach Nachname und Vorname, Kundennummer, Geburtsdatum oder Ort. Sinnvollerweise handelte es sich dabei um die Sortierreihenfolge, durch die man bei einem Großteil der Suchen schnell an den gewünschten Inhalt kam. Was aber, wenn man häufiger nach einem anderen Kriterium suchen wollte, welches nicht der Sortierreihenfolge entsprach? Angenommen, man hatte seine Kundendaten nach Nachname und Vorname sortiert (was ja meist am sinnvollsten war). Nun wollte man alle Kunden in einem bestimmten Ort anschreiben. Was konnte man tun? Im Prinzip konnte man Listen mit allen Kunden erstellen, die nach dem Ort sortiert waren. Mit diesen Listen – die natürlich möglichst aktuell gehalten werden mussten – ließen sich nun alle Kunden eines Ortes heraussuchen. Mit Computern lässt sich so etwas viel einfacher und vor allem deutlich schneller erledigen. Ein Computer kann sehr schnell sortieren und finden. Legt man also seine Liste mit Kunden im Rechner ab, kann man diese zum Beispiel beliebig sortiert ausgeben und – viel wichtiger – man kann nach beliebigen Kriterien suchen lassen. So ist es damit ein Leichtes, alle Kunden zu finden, die in einem bestimmten Ort wohnen. Nun kann man sich ein Programm zur Adressverwaltung von Kundendaten schreiben. Dies ermöglicht das Erfassen der Kundendaten, das Ändern, Suchen, Sortieren und was auch immer. Was muss dieses Programm können, abgesehen von der Ein- und Ausgabe? Es soll die Daten sinnvoll abspeichern, schnell in ihnen suchen, sie nach beliebigen Kriterien sortieren und dazu auch sicher und zuverlässig speichern. Vielleicht möchte man jetzt auch seine Produkte per Computer verwalten. Man erfasst dazu den Namen, die Nummer, die Kategorie, den Lagerort



'DWHQEDQNHQ

Sandini Bib

und auch die Preise. Auch hier möchte man eventuell nach verschiedenen Daten suchen können. Es zeigt sich, dass im Prinzip ganz ähnliche Anforderungen an das Programm gestellt werden, wie bei der Kundenverwaltung. Sicher, die Ein- und Ausgabe wird anders sein, aber grundsätzlich müssen die Daten auch sicher abgelegt werden, man muss sie durchsuchen und sortieren können. Eine ganze Reihe von Funktionen sind also identisch. Diese Funktionen stellen die Grundlage eines Datenbankmanagementsystems (DBMS) dar. Solch ein Programm ist speziell darauf ausgelegt, Daten unter anderem zu sichern, zu verwalten, in ihnen etwas zu finden und sie zu sortieren. Um so etwas schnell, sicher und auch für große Mengen an Daten handhabbar zu machen, gibt es elegante und trickreiche Algorithmen, die von einem DBMS genutzt werden. So braucht man sich bei einer eigenen Applikation nicht mehr um die Speicherung und den Zugriff auf die Daten zu kümmern, sondern kann sich ganz auf die eigentliche Programmlogik und die Einund Ausgabe konzentrieren. Die Datenbank wird dann über eine Programmierschnittstelle angesprochen. Es gibt verschiedene Konzepte, wie man die Daten in einer Datenbank verwaltet: • Hierarchische Datenbank: Die Daten werden in einer Baumstruktur abgelegt. So besteht zum Beispiel eine Firma aus mehreren Abteilungen, diese wiederum aus Gruppen und die Gruppen aus einzelnen Mitarbeitern. Diese Struktur kann man auf eine Datenbank übertragen. Hierarchische Datenbanken wurden früher häufig genutzt (als Datenbanken quasi nur auf Großrechnern liefen). Abbildung 1.1 zeigt ein Beispiel für eine hierarchische Struktur.

Firma Lager

Einkauf

Verkauf

Schmidt

Herms

Werner

Meier

Gebert

Kirch

Müller

Schmitt

Schulze Abbildung 1.1: Beispiel für eine hierarchische Struktur

• Netzwerkdatenbank: In einer Netzwerkdatenbank lassen sich die einzelnen Informationen beliebig miteinander verknüpfen. Über diese Verknüpfungen kann man auf die Daten auch wieder zugreifen. Auch

9RP .DUWHLNDVWHQ ]XP 5'%06



Sandini Bib

Netzwerkdatenbanken werden heutzutage nicht mehr sehr häufig genutzt. Abbildung 1.2 zeigt ein Beispiel für eine Netzwerkstruktur.

Firma

Einkauf

Projekt 1

Meier

Verkauf

Projekt 2

Müller

Projekt 3

Schulze

Schmidt

Abbildung 1.2: Beispiel für eine Netzwerkstruktur

• Relationale Datenbanken: Dies ist das heute am meisten verwendete Konzept zur Speicherung von Daten. Dabei werden diese in einzelnen Tabellen (Relationen) verwaltet, die aus Zeilen und Spalten bestehen. Die Tabellen lassen sich über Schlüsselfelder miteinander verknüpfen, um Beziehungen zu realisieren. In Abbildung 1.3 sieht man ein Beispiel für eine (kleine) relationale Datenbank. Es gibt die drei Tabellen Kunden, Produkte und Auftrag, die miteinander über Schlüssel verbunden sind.

Kunden KuNr 1001 1002 1003 1004 ....

Produkte

Name Meier Müller Herms Gebert ...

Auftrag

Ort

... ... Berlin Bremen ... Dresden ... ... Köln ... ...

AufNr 901 911 923 945 ...

KuNr 1001 1001 1003 1004 ...

PrNr Name 101 Isozet 212 Nanolen 331 Optax 148 Relan .... ...

PrNr Anzahl ... 101 10 ... 148 23 ... 212 4 ... 331 55 ... ... ... ...

Abbildung 1.3: Beispiel für eine relationale Struktur



'DWHQEDQNHQ

Typ 01 02 01 03 ...

... ... ... ... ... ...

Sandini Bib

• Objektorientierte Datenbanken: Diese Datenbanken verwalten die Daten als Einheiten, die nicht nur die Werte enthalten, sondern auch noch die Methoden, um etwas mit diesen Werten machen zu können. Sie sind eigentlich ideal für die Nutzung mit den aktuellen objektorientierten Programmiersprachen geeignet, trotzdem sind sie nicht sehr verbreitet. Meist wird heutzutage mit objektorientierten Programmiersprachen entwickelt, die Daten aber relational gespeichert. Einige Datenbanksysteme können neben den Daten auch Programme direkt speichern und ausführen (zum Beispiel in Java), dies ist dann ein objektrelationaler Ansatz. Der Großteil der heutzutage verwendeten Datenbanken baut auf dem relationalen Modell auf. Daher soll hier – neben der Tatsache, dass MySQL dieses Modell nutzt – im Weiteren auch nur darauf eingegangen werden.

1.4

Aufbau einer Datenbank

Eine (relationale) Datenbank besteht aus verschiedenen Elementen, die im Folgenden zumindest kurz beschrieben werden sollen.

1.4.1 Tabellen Weiter oben steht, dass die Daten in Tabellen abgespeichert sind. Eine Tabelle ist immer dafür gedacht, eine bestimmte Art von Daten aufzunehmen, zum Beispiel Adressen, Produkte oder Aufträge. In einem relationalen Datenbanksystem gibt es viele solcher Tabellen für verschiedene Arten von Werten. Sie sind aufgeteilt in Zeilen, in denen einzelne Datensätze stehen, und Spalten, welche die Felder der Datensätze definieren. Jeder Spalte ist (neben anderen Parametern) ein Name und ein Datentyp zugeordnet, der festlegt, was in dieser Spalte gespeichert werden kann. So kann es eine Spalte mit dem Namen NAME geben, die (sinnigerweise) den Namen eines Kunden enthält und so definiert ist, dass sie beliebige (alphanumerische) Zeichen bis zu einer Länge von 40 Zeichen aufnimmt. Eine andere Spalte heißt PLZ, soll die Postleitzahl enthalten und ist so definiert, dass auch nur Ziffern eingetragen werden können. In Abbildung 1.4 sieht man eine Beispieltabelle mit den einzelnen Elementen Spalte, Zeile bzw. Datensatz und Feld. Die Kombination aus einem Datensatz und einer Spalte ergibt ein Feld. In diesem Feld ist immer eine einzelne Information abgelegt (man kann natürlich auch mehr ablegen, das ist aber meist ein Zeichen für einen schlechten Datenbank-Entwurf und wird später behandelt).

     



Sandini Bib

Spalte

Kundennr Name

Zeile/ Datensatz

1001 1002 1003 1004 ...

Meier Müller Schmidt Herms ...

PLZ 28357 69190 47877 68219 ...

Ort Bremen Walldorf Willich Mannheim ...

Feld Abbildung 1.4: Elemente einer Tabelle

1.4.2 Beziehungen Eine Tabelle wird man meist nicht einzeln nutzen. Wie schon in Abbildung 1.3 zu sehen, benötigt man fast immer mehrere Tabellen. Jede Tabelle sollte dabei einen Primärschlüssel besitzen. Dieser besteht aus einer oder mehrerer Spalten, die für jeden Datensatz zusammen einen eigenen Wert haben und die einzelnen Zeilen somit eindeutig identifizieren. Im Beispiel hat die Tabelle Kunden den Primärschlüssel KuNr (für Kundennummer), die Tabelle Produkte den Schlüssel PrNr (für Produktnummer) und die Tabelle Auftrag den Schlüssel AufNr (für Auftragsnummer). In dieser letzten Tabelle wird nun auf die beiden anderen Tabellen verwiesen, um festzuhalten, welcher Kunde welches Produkt haben möchte. Dafür bedient man sich derer Primärschlüssel, die an dieser Stelle als Fremdschlüssel zum Einsatz kommen. Auf diese Weise kann man Beziehungen zwischen Tabellen in einer relationalen Datenbank einrichten. Je nach Produkt müssen diese Beziehungen durch die Anwendungslogik abgebildet werden oder die Datenbank berücksichtigt diese Verbindungen selber. MySQL unterstützt zwar Primärschlüssel (d.h. eindeutige Tabellenfelder), aus verschiedenen Gründen aber keine Fremdschlüssel.

1.4.3 Indizes Indizes sind streng genommen für eine Datenbank nicht unbedingt nötig, werden aber andererseits für eine halbwegs zügige Verarbeitung unbedingt gebraucht. Sie helfen, häufige Abfragen schneller zu machen. In einer Tabelle werden die Datensätze normalerweise unsortiert abgelegt. Die Reihenfolge mag zwar meist der des Eintragen entsprechen, aber es liegt im Ermessen des Datenbanksystems, die Sätze möglichst effektiv



'DWHQEDQNHQ

Sandini Bib

zu speichern. Wenn man nun nach bestimmten Werten sucht, kann es sinnvoll sein, auf die Datensätze in einer vorsortierten Reihenfolge zugreifen zu können. Dazu legt man einen Index an, der für die entsprechenden Suchfelder in alphabetischer oder numerischer Abfolge Verweise auf die Datensätze speichert. Durch einen Index ist das Datenbanksystem nun nicht mehr gezwungen, die gesamte Tabelle auf der Suche nach einem Wert abzugrasen, sondern es kann mit wenigen Schritten den passenden Satz finden. Indizes haben aber auch einen Nachteil: sie müssen bei jeder Aktualisierung der Tabelle nachgezogen werden (das macht das Datenbanksystem selber), was natürlich Zeit kostet. Je mehr Indizes man also für eine Tabelle definiert hat, desto schneller kann eine Suche sein, aber desto langsamer wird auch das Aktualisieren von Daten. Man sollte daher Indizes nur für die häufigsten Suchvorgänge anlegen, um einen guten Kompromiss zu finden.

1.4.4 Weitere Elemente Es gibt noch weitere Elemente in einer Datenbank, die hilfreich sein können, aber nicht in allen Datenbanksystemen vorhanden sind: • Regeln/Constraints: Eine Regel oder auch Constraint ist eine Einschränkung von möglichen Inhalten für ein Feld, welche bei der Definition einer Tabelle zusätzlich zum Datentyp angegeben wird. Somit kann man den Wertebereich noch weiter einschränken oder auch angeben, ob der Wert des Feldes in einer anderen Tabelle vorhanden sein muss. MySQL kann gewisse Regeln verarbeiten, allerdings nur in eingeschränktem Maße. • Datenbank-interne Programme (Stored Procedures): Auch in einer Datenbank können Programme und Funktionen gespeichert werden, um häufig wiederkehrende Abläufe sichern zu können. Der Vorteil solcher Programme ist, dass sie unabhängig von der ausführenden Applikation sind und häufig auch schneller ablaufen können. Jedes Datenbanksystem hat dabei seine eigene Programmiersprache, manche können auch mit Java arbeiten. MySQL bietet diese Möglichkeit nicht an, mit der Ausnahme von selbstdefinierten Funktionen, die allerdings zum einen auch außerhalb der Datenbank definiert sind und zum anderen nicht unter Windows funktionieren. • Sichten/Views: Eine Sicht bzw. View ist eine vordefinierte Abfrage auf Tabellen. Sie dient dazu, Anfragen einfacher zu gestalten oder auch, bestimmte Felder vor Benutzern zu verbergen. MySQL bietet keine Sichten an.

     



Sandini Bib

• Trigger: Dies sind interne Programme, die ausgeführt werden, sobald ein bestimmtes Ereignis eintritt. Dabei kann es sich zum Beispiel um das Hinzufügen, Ändern oder Löschen von Datensätzen in einer Tabelle handeln. Trigger sind nützlich, um erweiterte Regeln zu definieren, die nicht von der ausführenden Applikation abhängen. Auch Trigger sind nicht in MySQL vorhanden und müssen mit der Applikationslogik nachgestellt werden. Es mag so aussehen, als ob MySQL keine „richtige“ Datenbank ist, da so vieles nicht vorhanden ist. Das hat aber seine guten Gründe. MySQL ist darauf ausgerichtet, möglichst schnell und effizient seine Arbeit zu erledigen. Die meisten der oben aufgeführten Punkte sind zwar hilfreich, lassen sich aber fast alle auf die eine oder andere Weise ersetzen und kosten die Datenbank zudem viel Verwaltungsaufwand. Durch den Verzicht darauf kann MySQL so schnell sein. Andere Punkte sind durchaus auf der Liste der abzuarbeitenden Wünsche, wie zum Beispiel Sichten. Kann oder will man wirklich nicht auf diese Punkte verzichten, muss man sich entweder mit den „Workarounds“ beschäftigen oder letztendlich auf ein anderes Datenbanksystem ausweichen.

1.4.5 Standalone vs. Client/Server Wollte man früher auf eine Datenbank zugreifen, musste man sich direkt an den Rechner setzen oder eine Terminalverbindung dorthin aufbauen. Dies ist natürlich nicht sehr komfortabel. Mittlerweile arbeiten nahezu alle größeren Datenbanken nach dem Client/Server-Prinzip. Das bedeutet, dass auf einem Rechner der Datenbank-Server läuft, d.h. Dienste für andere Programme anbietet. Auf anderen Rechnern (oder auch auf dem gleichen Computer) laufen Client-Applikationen (sei es die interaktive Applikation für den direkten Datenbankzugriff oder „richtige“ Programme, die die Daten in der Datenbank nutzen), die sich mit dem Datenbank-Server verbinden und seine Dienste nutzen. Auch MySQL nutzt dieses Prinzip. Dabei ist es durchaus häufig, dass Serverund Client-Applikation auf dem gleichen Rechner laufen. Durch Sperren gerade verwendeter Daten wird verhindert, dass mehrere Clients gleichzeitig die selben Daten ändern und es dadurch zu Problemen kommt. Auch sogenannte „Desktop-Datenbanken“ wie Microsoft Access können prinzipiell von mehreren Rechnern im Netz gleichzeitig genutzt werden. Sie sind aber nicht unbedingt darauf ausgelegt und es bereitet nicht unerhebliche Probleme, wenn man sie im wirklichen Leben auf diese Art und Weise verwenden will. Diese Systeme bezeichnet man als „Standalone-Systeme“.



'DWHQEDQNHQ

Sandini Bib

1.5

Einsatzbeispiele

Wofür kann man nun eigentlich eine Datenbank nutzen? Nun, es gibt natürlich unendlich viele Beispiele und ein paar davon will ich hier aufzählen: • Die klassische Adressverwaltung: Name, Straße, PLZ, Ort, Telefon usw. für verschiedene Kontaktgruppen, wie Bekannte, Verwandte, Kunden, Lieferanten usw. Dies kann man beliebig erweitern, indem man zum Beispiel beliebig viele Adressen pro Kontakt zulässt und dabei eine Standardadresse markiert. Oder man ermöglicht verschiedene Kontaktarten (Telefon, Fax, E-Mail, SMS, ...), die auch mehrfach vorkommen können. • Genauso klassisch: eine CD-Verwaltung: Titel, Interpret, Stücke, Länge, Genres usw. Auch hier kann man deutlich mehr ins Detail gehen, indem man mehrere Interpreten pro CD und auch pro Stück zulässt, um dann bei einer Suche auch das Lied „Don't Give Up“ zu finden, wenn man nach Kate Bush sucht (das hat sie nämlich mit Peter Gabriel gesungen und findet sich unter anderem auf „Shaking the Tree“ von Peter Gabriel). • Vereinsverwaltung: Natürlich als zentrale Information die Mitglieder mit Name, Adresse, Eintrittsdatum, Geburtsdatum usw. Dazu die finanziellen Aspekte: Wer hat welchen Mitgliedsstatus, wieviel muss er bezahlen, wieviel hat er schon bezahlt, geschieht es per Lastschrift oder Überweisung und noch vieles mehr. Man kann bei einem Kanuverein zum Beispiel auch verwalten, wer welches Boot wo liegen hat und welche Leihboote bereitstehen. • Nun mehr ins Geschäftliche: Die Lagerverwaltung: Ein- und Ausgänge werden verzeichnet und man kann nachschauen, wo man was findet. Sinkt der Bestand einer Ware unter einen bestimmten Level, kann automatisch nachbestellt werden. Eventuelle Verfallsdaten lassen sich ebenso berücksichtigen wie besondere Lagervorschriften. • Dazu passend: Die Auftragsverwaltung: Kunden (natürlich aus der Kundenverwaltung) bestellen Waren. Bei der Bestellung kann direkt geprüft werden, ob noch genug Waren vorhanden sind oder wann sie bei zu geringem Lagerbestand kommen werden. Der Lagerbestand kann sofort um die Bestellung reduziert werden und das Erstellen der Rechnung lässt sich auch automatisieren. Die letzten beiden Beispiele zeigen schon, dass man durch einen geschickten Entwurf verschiedene Bereiche eines Unternehmens miteinander verknüpfen kann. Dabei dient die Datenbank meist nur als Grundlage für die Datenspeicherung; die gesamte Geschäftslogik findet sich auf Applikationsebene.

(LQVDW]EHLVSLHOH



Sandini Bib

Diese Trennung ist nicht unbedingt streng, so lässt sich ein Teil der Geschäftslogik auch direkt in der Datenbank abbilden (über Trigger, Regeln und gespeicherte Prozeduren) oder die Anwendungsprogramme als Daten selbst in Tabellen speichern. Letzteres findet sich unter anderem bei dem Beispiel für Unternehmenssoftware: SAP R/3. Hier ist – bis auf die grundlegendsten Bibliotheken – alles in der Datenbank abgelegt: die Programme und Transaktionen und natürlich auch die Daten. Nun läuft R/3 nicht gerade auf MySQL, worauf ich aber hinaus will, ist, dass es mit einer Datenbank alleine nicht getan ist. Man benötigt eigentlich immer eine passende Applikation dazu, egal ob sie im Web oder lokal läuft. Natürlich gibt es auch bei MySQL Programme zum Verwalten der Daten (die in diesem Buch auch den Schwerpunkt bilden), aber der eigentliche Nutzer der Daten sollte nicht gerade über diese auf die Datenbank zugreifen. Im nächsten Kapitel werden wir ein Beispielszenario etwas detaillierter beleuchten, welches uns durch das restliche Buch begleiten wird.

1.6

MySQL

Wir haben uns bisher eher allgemein mit Datenbanken beschäftigt und sind nur ab und zu auf MySQL eingegangen. In diesem Abschnitt soll es nun nur um MySQL gehen: wie es entstanden ist, was das Besondere daran ist, was es kostet und wer es einsetzen darf.

1.6.1 Was ist MySQL? MySQL ist ein relationales Datenbankmanagementsystem, wie weiter oben beschrieben. Seine Quelltexte sind frei verfügbar und können selber angepasst werden. Das primäre Ziel ist vor allem Geschwindigkeit, nicht Vollständigkeit. So werden Sie längst nicht alle Funktionen finden, welche die „großen“ Datenbanken bieten, auch das wurde schon kurz erwähnt und wird im Anhang noch erläutert werden. Aber ein Großteil dessen, was man im Allgemeinen bei einem RDBMS benötigt, ist vorhanden, so dass sich MySQL für sehr viele Zwecke sehr wohl eignet. Und es ist wirklich schnell. Auf den Webseiten von MySQL (www.mysql.com) finden sich Benchmark-Ergebnisse, die sich sehen lassen können. Zudem kann auch MySQL mit sehr großen Datenmengen umgehen. Dies alles hat zur Folge, dass MySQL bei vielen, auch großen, Unternehmen auf der ganzen Welt eingesetzt wird.



'DWHQEDQNHQ

Sandini Bib

1.6.2 Die Geschichte und Idee hinter MySQL Die Entwickler von MySQL wollten ursprünglich den Code der (ebenso freien) Datenbank mSQL nutzen, um für die Firma TcX Webanwendungen zu implementieren. Dabei war geplant, die eigenen Tabellen über eigene Routinen anzusprechen. Allerdings stellte man schnell fest, dass mSQL nicht so flott war, wie man es eigentlich benötigte, und sich auch nicht so leicht anpassen ließ, wie man es sich wünschte. Also wurde eine neue SQL-Schnittstelle für die eigenen Tabellen geschrieben, die sich allerdings stark an die mSQL-Syntax anlehnte. Damit war sichergestellt, dass vorhandene Software und bestehende Tools leicht mit MySQL zusammenarbeiten konnte. Dies geschah 1994. Etwa ein Jahr später wurde MySQL im Internet veröffentlicht. Im Laufe der Zeit wurde das System immer umfangreicher und erhielt mehr und mehr Möglichkeiten. Aber immer wurde darauf geachtet, das System nicht langsamer zu machen. Wenn heutzutage Dinge fehlen, werden sie entweder demnächst entwickelt oder es wurde bewusst auf sie verzichtet, da sie im Vergleich zu ihrem Nutzen die Geschwindigkeit zu sehr reduzieren würden. MySQL AB, die Firma hinter MySQL, wurde 1995 in Schweden von Michael „Monty“ Widenius, David Axmark und Allan Larsson gegründet. Michael und David gehörten schon lange der Open Source-Szene an und auch MySQL sollte darin begründet sein. MySQL AB entwickelt MySQL, versucht, es bekannter zu machen und bietet vor allem auch Support dafür an (der durchaus kostenpflichtig ist). Ziel ist weiterhin, einen schnellen, kompakten und stabilen Datenbankserver bereitzustellen.

1.6.3 Elemente von MySQL MySQL besteht vor allem aus dem eigentlichen Datenbank-Server. Diesen gibt es in verschiedenen Varianten und auf vielen unterschiedlichen Betriebssystemen. Die Varianten ermöglichen es, entweder einen besonders schnellen Server auszuwählen, der manche Funktionen nicht beinhaltet, oder einen zu nutzen, der mehr bietet, aber nicht so schnell ist wie sein „eingeschränkter“ Bruder. Man kann aus diversen Betriebssystemen wählen, unter anderem Linux, FreeBSD und vielen anderen Unix-Betriebssystemen (kommerziellen wie freien), sowie Windows, Mac OS usw. Dabei werden allerdings nicht alle Betriebssysteme gleich weit unterstützt, manche spezielleren Teile sind nur unter Linux/Unix vorhanden.

0\64/



Sandini Bib

Neben dem eigentlichen Server gibt es natürlich noch weitere Programme, denn nur mit dem Server alleine kommt man nicht weit. Man braucht noch Client-Programme, um auf den Server zugreifen zu können. Zunächst ist da der interaktive Client, in dem man SQL-Befehle ausführen und sich Ausgaben anzeigen lassen kann. Nun wird man einem normalen Endbenutzer nicht zumuten wollen, direkt die Datenbank zu befragen, daher dient dieses Programm eher dem Entwickler und Administrator. Neben diesem interaktiven Client gibt es weitere Tools, die zur Administration der Datenbank dienen und auf diesem Client aufbauen. Weiterhin gibt es natürlich Programme, um die Datenbank zu sichern und wieder einzuspielen. Um anderen Applikationen den Zugriff auf die Datenbank zu ermöglichen, kann man sie über verschiedene Schnittstellen (APIs, Application Programming Interfaces) ansprechen. Zu den wichtigsten gehören das PHP-API, das Perl-DBI-API und MyODBC, die ODBC-Schnittstelle von MySQL. Zudem gibt es APIs für C, C++, Java (via JDBC) und andere Sprachen.

1.6.4 Lizenzen und Kosten bei MySQL Man sagt immer, MySQL ist eine „freie“ Datenbank. Nun stimmt das zwar im Prinzip, aber es gibt durchaus Szenarien, bei denen man eine Lizenz erwerben muss. Grundsätzlich ist MySQL tatsächlich kostenlos und Open Source. Man erhält den Quellcode und kann mit ihm arbeiten, ihn anpassen und erweitern. MySQL läuft unter der GPL, der GNU General Public License. Wenn man mit dieser Lizenz Probleme hat, weil man zum Beispiel seine eigenen Sourcen nicht veröffentlichen will, kann man bei MySQL AB Lizenzen erwerben. Wichtiger aber ist, dass man eine Lizenz erwerben muss, wenn man MySQL in seine eigene kommerzielle Applikation einbetten will oder wenn die eigene kommerzielle Applikation nur mit MySQL läuft. Aber auch dann ist der Preis einer Lizenz ausgesprochen günstig im Vergleich zu den sonstigen kommerziellen Datenbanksystemen. Nutzt man MySQL mit seiner eigenen Applikation kommerziell, muss aber keine Lizenzgebühr zahlen, wird es trotzdem gerne gesehen, wenn man einen Support-Vertrag abschließt, den es in verschiedenen Varianten gibt. Detailliertere Informationen zur Lizenzierung und vor allem zu den Support-Leveln finden sich auf den Webseiten von MySQL.



'DWHQEDQNHQ

Sandini Bib

2

Einsteigen in MySQL

le

n e rn

Bisher haben wir uns nur allgemein mit Datenbanken befasst und uns angeschaut, was MySQL eigentlich besonders ausmacht. Nun wollen wir tatsächlich in die Benutzung von MySQL einsteigen.

2.1

Was Sie in diesem Kapitel lernen

In diesem Kapitel lernen Sie unser Beispielszenario kennen. Wir setzen es in ein Modell aus Tabellen und Beziehungen um. Dann starten wir endlich MySQL – nachdem wir geklärt haben, was vorher schon vorhanden und eventuell gestartet sein muss. Wie kommuniziert man nun mit MySQL? Die Sprache SQL wird erläutert und dann eine Datenbank angelegt. Wir überlegen, wie die Tabellen mit welchen Spalten und welchen Wertetypen erstellt werden sollen. Dabei lernt man gleich die verschiedenen Feldtypen und Möglichkeiten kennen. Zudem werden wir Relationen zwischen Tabellen sehen und modellieren.

2.2

Von der Realität zum Modell – Das Beispielszenario

In einer Datenbank speichert man im Allgemeinen Daten ab, die aus dem wirklichen Leben kommen. Dabei muss man beachten, dass man die Informationen sinnvoll ablegt, um sie schnell (oder überhaupt) erreichen zu können. Zudem sind auch die Beziehungen so aufzubauen, dass sie zum einen die Realität widerspiegeln, zum anderen aber auch in der Datenbank abbildbar sind. Man muss also die Realität in einem Modell für die Datenbank abbilden. In diesem Abschnitt wollen wir ein Beispielszenario vorstellen, welches uns durch den Rest des Buches begleiten soll. In Büchern über Daten-

:DV 6LH LQ GLHVHP .DSLWHO OHUQHQ



Sandini Bib

banken werden als Beispiele häufig Schul- und Notenverwaltungen verwendet. Wir wollen ein etwas anderes Szenario nutzen, das deswegen nicht besser oder schlechter ist: Eine Musikschule.

2.2.1 Die Realität In einer Musikschule geht es naturgemäß um das Erlernen eines Instruments (wobei ich die Stimme hier auch als Instrument ansehen möchte, ebenso Sonderfälle wie die musikalische Früherziehung). Schüler kommen in die Schule, um in der Nutzung eines Instruments unterrichtet zu werden. Dies geschieht durch Lehrkräfte, die verschiedene Instrumente beherrschen. Manche Schüler haben Einzelunterricht, andere wiederum lernen in Gruppen. Dazu gibt es mehrere Orchester und Chöre, die unterschiedliche Bereiche abdecken (Jazz-Combo, Kammerorchester, Symphonieorchester, Rockband, Gospel-Chor, „klassischer“ Chor usw.). Um unterrichten und üben zu können, benötigt man Räume, die belegt werden müssen. Schließlich haben nicht alle Schüler eigene Instrumente – wenn es darum geht, auszuprobieren, ob einem ein Instrument wirklich Spaß macht, ist es nicht sinnvoll gleich mehrere Tausend Euro auszugeben. Also hat die Musikschule ein gewisses Repertoire an Leihinstrumenten, das sie den Schülern gegen Entgelt zur Verfügung stellt. Wenn die Leihinstrumente begehrt sind, macht auch eine Warteliste Sinn. Für die Teilnahme am Unterricht oder die Nutzung von Leihinstrumenten zahlen die Schüler Geld per Lastschrift – und die Lehrkräfte erhalten Einkommen. Man benötigt also die Kontodaten der Teilnehmer und muss wissen, wer woran teilnimmt.

2.2.2 Das Modell Im vorigen Abschnitt wurde einiges aufgezählt, was eine Musikschule ausmacht. Dabei wurde noch lange nicht alles berücksichtigt, so zum Beispiel die unterschiedliche Qualifikation der Lehrkräfte und der Wissensstand der Schüler. Aber es soll an dieser Stelle ausreichen und wir werden in diesem Buch auch nicht alles behandeln. Zunächst einmal wollen wir herausfinden, welche Elemente es gibt und was für Eigenschaften sie haben. Dies kann dann auf Tabellen und ihren Spalten abgebildet werden. Wir haben also: • Schüler: Ein Schüler hat natürlich einen Vor- und Nachnamen, ein Geburtsdatum, eine Straße, PLZ und Ort, an dem er wohnt, eine Tele-



   

Sandini Bib

fonnummer, unter der man ihn erreichen kann und eine Bankverbindung. • Lehrer: Auch der Lehrer hat Vor- und Nachnamen, eine Adresse bestehend aus Straße, PLZ und Ort, eine Telefonnummer und Bankdaten. • Leihinstrument: Ein Leihinstrument ist ein bestimmtes Instrument (Flöte, Gitarre, Geige usw.) von einem Hersteller und hat eine Typbezeichnung. Dazu kommt die Leihgebühr, die ein Schüler pro Monat für dieses Instrument zahlen muss. • Unterricht: Ein (Einzelunterricht) oder mehrere (Gruppenunterricht) Schüler werden von einem Lehrer in einem bestimmten Instrument jede Woche an einem bestimmten Wochentag, zu einer bestimmten Uhrzeit für eine gewisse Dauer in einem Raum unterrichtet. Die Kosten für den Schüler hängen vom Instrument ab und davon, ob es sich um Einzel- oder Gruppenunterricht handelt. • Orchester/Chor: Auch an einem Orchester oder Chor nehmen Schüler teil und werden von einem Leiter dirigiert. Dabei gibt es verschiedene Instrumente (oder Stimmen). Das Ganze findet an einem bestimmten Wochentag zu einer gewissen Uhrzeit für eine bestimmte Dauer statt. Dabei wird ein Raum belegt. Kosten fallen für die Schüler im Allgemeinen nicht an, da die Schule ein eigenes Interesse daran hat, Orchester und Chor im Angebot zu haben. Allerdings wird der Leiter eventuell eine Aufwandsentschädigung erhalten. Wie man sieht, sind die ersten drei Punkte wirkliche „Objekte“, die man anfassen kann. Die beiden letzten Punkte sind aber nicht wirklich materiell, sondern es handelt sich eher um Ereignisse (oder neudeutsch „Events“). Ein Element einer Datenbank, welches in einer Tabelle abgebildet werden soll, ist also nicht immer etwas Greifbares. Noch auffälliger wird dies bei den Leihinstrumenten. Sind schon Unterricht und Orchester/Chor nur Ereignisse, ist das Ausleihen eines Instruments oder erst recht die Position auf der Warteliste eher ein Zustand, der aber auch in Tabellen abgebildet werden kann und muss: • Ausleihe: Ein bestimmtes Leihinstrument wird von einem Datum an einen Schüler ausgeliehen. Ein Enddatum ist nicht unbedingt notwendig, da meist nicht klar ist, wie lange der Schüler das Instrument nutzen möchte. • Ausleihwarteliste: Das Leihinstrument ist in Benutzung und die interessierten Schüler stehen mit einer gewissen Position auf der Warteliste.

9RQ GHU 5HDOLWlW ]XP 0RGHOO ¥ 'DV %HLVSLHOV]HQDULR



Sandini Bib

Zudem gibt es Objekte, die immer wieder vorkommen, und für die es sich lohnt, Tabellen anzulegen, obwohl sie nicht direkt im Mittelpunkt stehen: • Instrument: Die Bezeichnung des Instruments und die Instrumentengruppe (Holzblasinstrument, Streichinstrument usw.) • Raum: Die Bezeichnung des Raums und die Etage, auf der er sich befindet (unter der Annahme, dass wir es mit nur einem Gebäude zu tun haben). Stecken wir diese Auswahl an Objekten in eigene Tabellen, vermeidet man das immer wiederkehrende Eingeben der Werte mit der Möglichkeit, sich zu verschreiben (Querfölte statt Querflöte) und mit der Gewissheit, immer die gleiche Bezeichnung zu nutzen (Geige, Violine oder was?). Solche Auswahllisten können ungemein hilfreich sein und sind deshalb hier schon aufgeführt, auch wenn sie nicht unbedingt notwendig wären. Aber allein im Rahmen eines guten Datenbank-Designs macht es Sinn, sie zu nutzen. Kann man dieses Modell jetzt direkt in Tabellen umsetzen? Im Prinzip schon, allerdings werden wir auf Unschönheiten stoßen, die wir dann beheben. Wir werden in Kapitel 2.6 die Tabellen zusammenstellen.

2.3

Verbinden mit MySQL

Jetzt wollen wir uns unsere Datenbank für die Musikschule erstellen. Aber wie kann man sich mit MySQL verbinden? Im vorigen Kapitel wurde geschildert, dass es sich bei MySQL um ein Client/Server-System handelt. Das bedeutet, dass sich der Datenbank-Server auf einem ganz anderen Rechner befinden kann. Daher sollen hier kurz die Voraussetzungen beschrieben werden, die für das weitere Gelingen notwendig sind.

2.3.1 Voraussetzungen Um einen MySQL-Client starten zu können, benötigt man zunächst einmal einen laufenden MySQL-Server. Dieser kann auf dem eigenen Rechner sein (was zu Beginn durchaus sinnvoll ist, da es manches vereinfacht), sich aber auch ganz woanders befinden. Sofern Sie einen eingerichteten MySQL-Server zur Verfügung haben, können Sie sich von dessen Administrator die Verbindungsdaten geben lassen. Ist dies nicht der Fall, finden Sie im Anhang ein Kapitel über die Installation von MySQL. Das ist nicht schwer und ist auch schnell erledigt.



   

Sandini Bib

Um sich die Angabe des Verzeichnisses mit den MySQL-Applikationen zu ersparen, bietet es sich an, den Pfad /bin unter Unix bzw. \bin unter Windows in die Pfadvariable des Betriebssystems mit aufzunehmen.

2.3.2 Das interaktive Programm – mysql mysql ist das Programm, mit dem so gut wie alle administrativen Tätigkeiten erledigt werden können. Man kann Datenbanken und Tabellen anlegen und anpassen, Daten eingeben, ändern und löschen, Rechte vergeben und entziehen und vieles mehr. Natürlich kann man auch hier die Daten abfragen, aber der Endanwender sollte mit diesem Tool eigentlich nichts zu tun bekommen.

mysql

Gestartet wird das Programm, indem man an der Kommandozeile den Befehl mysql aufruft: prompt> mysql

Auch wenn MySQL unter so gut wie jedem Betriebssystem auf diesem Planeten läuft, werde ich im Weiteren nur auf die Nutzung unter Linux und Windows eingehen. Wenn es darum geht, Befehle auf Betriebssystemebene einzugeben, ist die Anzeige des Prompt naturgemäß bei Linux anders als bei Windows. Aber selbst bei ein und dem selben Betriebssystem kann man sich den Prompt meist sehr flexibel einstellen. Daher werde ich im Folgenden immer dann, wenn das Betriebssystem im Spiel ist, folgende Darstellung wählen: prompt> mysql

Dabei kann es in Wirklichkeit bei Linux vielleicht so aussehen: truhe: ~/mysql> mysql

während es unter Windows eher so aussieht: C:\mysql> mysql

Dazu sollte möglichst – wie schon erwähnt – das Verzeichnis mit den Programmdateien von MySQL mit in den Standardpfad aufgenommen sein. Befindet sich der Server auf einem anderen Rechner, kann man das dem Programm mit dem Parameter -h mitteilen. Dahinter folgt dann der Name des entsprechenden Servers. Läuft der Datenbank-Server zum Beispiel auf dem Rechner truhe, müsste der Aufruf dementsprechend lauten:

  



Sandini Bib

prompt> mysql -h truhe

Wird auch die Angabe eines bestimmten Benutzers benötigt, geschieht dies über den Parameter -u, gefolgt vom Benutzernamen, sowie den Parameter -p, mit dem das Kennwort angegeben oder – falls nicht mit angegeben – abgefragt wird. Falls man sich also als Benutzer thomas am Server truhe anmelden möchte, geschieht dies per: prompt> mysql -h truhe -u demmig -p

Gibt man dem Parameter -p ein Kennwort direkt mit, darf zwischen -p und dem Kennwort kein Leerzeichen stehen. Allerdings ist es meist besser, das Kennwort nicht mit anzugeben, sondern es abfragen zu lassen, da man es dann nicht auf dem Bildschirm sehen kann. Man erhält dann die Aufforderung Enter password:

und kann das Kennwort verdeckt eingeben. Hat man dies erfolgreich getan (oder war gar keins notwendig), meldet sich MySQL mit einem informativen Text ähnlich dem folgenden: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 to server version: 3.23.51-nt Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql>

Die letzte Zeile zeigt an, dass MySQL nun auf die Eingabe von Befehlen wartet. Zwei werden wir gleich kennen lernen: das Beenden und die Hilfefunktion. Zum Beenden des Programms (nicht des Servers) gibt man an der Eingabeaufforderung QUIT oder EXIT ein. Unter Linux funktioniert auch die Tastenkombination (Strg)(D). mysql verabschiedet sich nun mit einem kurzen

QUIT/EXIT

mysql> QUIT Bye prompt>

Unter MySQL ist es – von wenigen Ausnahmen abgesehen – unwichtig, ob Sie groß (QUIT), klein (quit) oder gemischt (QuIt) schreiben. Ich werde in diesem Buch die Befehle groß, Tabellen-, Datenbank- und andere Namen aber klein schreiben, wenn ich nicht auf etwas gesondert hinweisen möchte.



   

Sandini Bib

Um die Hilfe aufzurufen, gibt man an der Eingabeaufforderung schlicht HELP oder \h ein (bei der zweiten Variante muss das h allerdings klein sein). Man erhält dann folgenden Text:

HELP

mysql> HELP MySQL commands: Note that all text commands must be first on line and end with ';' help (\h) Display this help. ? (\?) Synonym for `help'. clear (\c) Clear command. connect (\r) Reconnect to the server. Optional arguments are db and host. ego (\G) Send command to mysql server, display result vertically. exit (\q) Exit mysql. Same as quit. go (\g) Send command to mysql server. notee (\t) Don't write into outfile. print (\p) Print current command. quit (\q) Quit mysql. rehash (\#) Rebuild completion hash. source (\.) Execute a SQL script file. Takes a file name as an argument. status (\s) Get status information from the server. tee (\T) Set outfile [to_outfile]. Append everything into given outfile. use (\u) Use another database. Takes database name as argument. Connection id: 11 (Can be used with mysqladmin kill) mysql>

Die in der Hilfe aufgeführten Befehle dienen eigentlich alle dazu, dem Programm mysql etwas mitzuteilen, aber nicht, um mit dem Server zu kommunizieren. Dazu bedient man sich der Sprache SQL, die im nächsten Abschnitt vorgestellt wird. Innerhalb von mysql kann man sich recht komfortabel mit den Cursortasten bewegen: (½] und (¼] blättern zum vorigen oder nächsten Befehl, der schon eingegeben worden war. (Bild½] und (Bild¼] springen an den Anfang und das Ende der Befehlsliste.

2.4

SQL

SQL steht für Structured Query Language. Es handelt sich dabei um die Abfragesprache für Datenbanken. Sie ist zwar theoretisch standardisiert, aber praktisch hat jeder Datenbank-Hersteller seine eigenen Besonderheiten, Ergänzungen und Abweichungen. SQL ist recht „sprechend“, d.h., ein Befehl liest sich fast wie ein normaler (englischer) Satz. Ein Beispiel ist

64/



Sandini Bib

SELECT name, ort FROM kunden WHERE plz BETWEEN 68000 AND 68999 ORDER BY name;

Dieser Befehl sagt: „Wähle den Namen und Ort der Kunden aus, deren Postleitzahl zwischen 68000 und 68999 liegt und sortiere sie nach dem Namen.“ Eigentlich klar, oder?

2.4.1 Geschichte von SQL Die Geschichte von SQL verläuft parallel zur Entwicklung der Datenbanken selber. 1969 erstellte Edgar F. Codd erstmals einen Bericht, der einen Ansatz zur Verknüpfung mehrerer Tabellen beschreibt. Dieser Bericht ist allerdings recht unbekannt, da er nur IBM-intern an einen eingeschränkten Kreis ging. Eine überarbeitete Version des Konzepts floss dann in einen 1970 erschienenen Artikel „A Relational Model of Data for Large Shared Data Banks“ im Journal der Association of Computer Machinery ein. 1974 gab es dann einen Prototyp eines RDBMS namens „System R“. Zwei Jahre später tauchte zum ersten Mal die Abfragesprache auf, die damals den Namen „Structured English Query Language“ (SEQUEL) erhielt. Im Laufe der Jahre mutierte der Name schließlich zu „Structured Query Language“ (SQL), wird aber häufig noch als „Sequel“ ausgesprochen (MySQL allerdings möchte bevorzugt „my es qu ell“ gesprochen werden). Im Jahr 1979 brachte die Firma „Relational Software, Inc.“ (mittlerweile Oracle) eine erste kommerzielle Version von SQL heraus. 1986 wurde ein erster Standard vom American National Standards Institute (ANSI) veröffentlicht, der mittlerweile in den Jahren 1992 und 1999 zwei Nachfolger fand. Alle „richtigen“ Datenbanken unterstützen SQL, auch wenn sich so gut wie keine komplett an den Standard hält. Alle haben ihn erweitert und/oder eingeschränkt oder verwenden eine leicht unterschiedliche Syntax. Allerdings sind die grundlegenden Elemente immer gleich, so dass man sich meist schnell zurechtfindet, wenn man den Datenbank-Hersteller wechselt.

2.4.2 Elemente von SQL Man kann SQL in vier Bereiche unterteilen: Datendefinition, Datenänderung, Datenkontrolle und Datenabfrage. Ich will sie im Folgenden kurz vorstellen. Datendefinition Die Befehle dieses Bereichs dienen dazu, die Strukturen zu definieren, in denen später die Daten abgelegt werden sollen. Dazu gehören zum Bei-

DDL



   

Sandini Bib

spiel Datenbanken und Tabellen. Auch zum Ändern oder Löschen von Strukturen gibt es Kommandos. Dieser Bereich wird auch Data Definition Language (DDL) genannt. Beispiele sind CREATE, ALTER und DROP. Datenänderung Mit den Befehlen dieses Abschnitts lassen sich die eigentlichen Daten einfügen, ändern und löschen. Der übliche Begriff für diesen Bereich ist Data Manipulation Language (DML); Beispiele dazu sind INSERT, UPDATE und DELETE.

DML

Datenkontrolle Nicht jeder soll alle Daten einer Datenbank einsehen und auch nicht alle Tätigkeiten ausführen dürfen. So macht es Sinn, dass nur der Datenbank-Entwickler und der Administrator Änderungen an der Struktur vornehmen können. Ein normaler Mitarbeiter wird aber sicherlich nur seine Daten nutzen sollen, und zum Beispiel vor allem keine Gehaltsdaten anderer Kollegen einsehen dürfen. Die Befehle zum Erteilen und Entziehen von Rechten (GRANT und REVOKE) gehören zum Bereich der Data Control Language (DCL).

DCL

Datenabfrage Zum Schluss der Bereich mit dem Befehl, der wohl am häufigsten genutzt wird: SELECT. Mit dieser Anweisung kann man die Daten aus den Tabellen auslesen. Übrigens kann man in den meisten Datenbank-Systemen auch die Datendefinitionen (also Tabellenstrukturen, Datenbanken usw.) mittels SELECT aus Systemtabellen auslesen (bei MySQL gibt es dazu andere Befehle).

2.5

Anlegen einer Datenbank

Wenn Sie sich mit MySQL verbunden haben, ist normalerweise noch keine Datenbank ausgewählt. Man kann auf jedem MySQL-Server mehrere Datenbanken anlegen. (Zudem kann man auf einem Rechner auch noch mehrere MySQL-Server laufen lassen.) Es gibt in jedem vollständig eingerichteten MySQL-Server schon zwei Datenbanken. Eine Übersicht darüber erhält man mit dem Befehl SHOW DATABASES. Geben Sie ihn in ihrem MySQL-Client ein und schließen Sie das Ganze mit einem Semikolon ab.

   

SHOW DATABASES



Sandini Bib

mysql> SHOW DATABASES; +----------+ | Database | +----------+ | mysql | | test | +----------+ 2 rows in set (0.11 sec)

Befehle werden in MySQL grundsätzlich mit einem Semikolon abgeschlossen. Wenn Sie es einmal vergessen und die Eingabetaste drücken, werden Sie Folgendes sehen: mysql> SHOW DATABASES ->

Der Pfeil nach rechts ist ein Zeichen dafür, dass MySQL noch eine Eingabe erwartet. Falls Ihnen dies unabsichtlich passiert, geben Sie einfach hier das Semikolon ein und drücken Sie die Eingabetaste: mysql> SHOW DATABASES -> ; +----------+ | Database | +----------+ | mysql | | test | +----------+ 2 rows in set (0.00 sec)

An diesem Beispiel kann man aber noch etwas anderes, sehr Praktisches erkennen: Ist ein Befehl einmal zu lang für eine einzelne Zeile, kann man jederzeit eine neue Zeile beginnen, bis man den Befehl mit einem Semikolon beendet. Das kann der Übersichtlichkeit durchaus förderlich sein: SELECT FROM WHERE ORDER BY

name, ort kunden plz BETWEEN 68000 AND 68999 name;

Es gibt zudem noch weitere Ausgaben der Konsole, die zeigen, dass auf etwas gewartet wird: mysql> SELECT * FROM kunden WHERE name = 'Meier '>



   

Sandini Bib

Hier fehlt ein einfaches Anführungszeichen. Wenn man an dieser Stelle nur ein Semikolon eingeben würde, käme man nicht weiter, da dies als Teil einer Zeichenkette angesehen würde. Daher kann man hier nur entkommen, indem man erst das Anführungszeichen und dann das Semikolon eingibt: mysql> SELECT * FROM kunden WHERE name = 'Meier '> ';

Das Gleiche gilt genauso für das doppelte Anführungszeichen: mysql> SELECT * FROM kunden WHERE name = "Meier "> ";

Hier muss man natürlich mit einem doppelten Anführungszeichen „auflösen“. Manche Befehle benötigen übrigens kein Semikolon als Abschluss, ich werde in diesen Fällen gesondert darauf hinweisen. Zwei Beispiele (bzw. drei) haben Sie schon kennen gelernt: QUIT, EXIT und HELP. Die angezeigte Ausgabe ist übrigens typisch für MySQL. In übersichtlicher Tabellenform werden Spalten und Zeilen angezeigt. Als Abschluss folgt die Angabe der ausgegebenen Zeilen und die Dauer, die für die Ausführung benötigt wurde. Diese Dauer ist allerdings nicht direkt mit der CPU-Zeit auf dem Server-Rechner gleichzusetzen, da unter anderem auch der Transport über das Netzwerk mit eingerechnet ist. Im Weiteren werde ich die letzte Zeile mit der Angabe der Zeilen und der Dauer nicht immer mit ausgeben. Wofür werden diese zwei Datenbanken nun verwendet? Zunächst zu test: Dies ist eine „Spieldatenbank“, in der man ausprobieren und herumdoktern kann. Die andere Datenbank, mysql, ist dagegen ausgesprochen wichtig. In ihr finden sich alle Systemdaten wie Benutzer, Berechtigungen und andere Parameter. Um eine Datenbank auszuwählen, verwendet man den Befehl USE, gefolgt vom Namen der Datenbank:

USE

mysql> USE test Database changed

   



Sandini Bib

Die Namen von Datenbanken (und auch Tabellen) sind unter Unix abhängig von Groß- oder Kleinschreibung! „test“, „Test“ und „TEST“ sind also verschiedene Datenbanken. Unter Windows gilt diese Einschränkung nicht, allerdings muss man innerhalb einer Abfrage die Schreibweise beibehalten. Auf Grund der unterschiedlichen Behandlung zwischen Unix und Windows empfiehlt es sich, eine einheitliche Schreibweise zu nutzen und vor allem keine Datenbanken (und natürlich auch keine Tabellen) anzulegen, die sich nur in ihrer Groß- und Kleinschreibung von einer anderen unterscheiden. USE ist ein weiterer Befehl, der kein Semikolon am Ende benötigt. Dafür

hat er noch eine andere Besonderheit: er muss komplett in einer Zeile stehen und darf nicht umbrochen werden. Falls man test zum Spielen nutzen will, ist zu beachten, dass jeder andere (sofern sich denn mehrere Nutzer mit MySQL befassen) auch auf diese Datenbank zugreifen darf und darin befindliche Tabellen und anderes verändern und löschen kann. Möchte man dies (bei einem Mehrbenutzersystem, dass auch tatsächlich als Solches genutzt wird) verhindern, muss man sich vom Datenbank-Administrator eine eigene Datenbank einrichten lassen, auf die man nur selber Zugriff hat. Wir werden hier eine eigene Datenbank erstellen, aber noch nicht weiter auf Benutzerberechtigungen eingehen. CREATE DATABASE

Um nun eine eigene Datenbank anzulegen, nutzen Sie den DDL-Befehl CREATE DATABASE. Diesem wird als Parameter der Name der Datenbank mitgegeben: mysql> CREATE DATABASE musikschule;

Damit hat man nun eine Datenbank namens „musikschule“ angelegt. Die vollständige Syntax von CREATE DATABASE lautet: CREATE DATABASE [IF NOT EXISTS] db_name;

Der in eckigen Klammern angegebene Teil ist optional, d.h. er muss nicht mit angegeben werden (aber wenn, dann bitte ohne die Klammern). Wird er weggelassen und die Datenbank ist schon vorhanden, gibt es eine Fehlermeldung. Mit Angabe von IF NOT EXISTS vermeidet man Fehlermeldungen, wenn eine Datenbank zum Beispiel per Skript angelegt wird. Was für Namen sind für Datenbanken gültig? Sie dürfen höchsten 64 Zeichen lang sein und müssen aus Zeichen bestehen, die in einem Verzeichnisnamen erlaubt sind, mit Ausnahme des Slashes „/“ und dem Punkt „.“. Für Tabellen gilt Ähnliches, nur dass statt einem Verzeichnis-



   

Sandini Bib

namen ein Dateiname ausschlaggebend ist. Die Einschränkung auf Verzeichnis- und Dateinamen erklärt sich daraus, dass Datenbanken im Verzeichnis data von MySQL als Ordner erstellt werden und Tabellen darunter in (mehreren) Dateien angelegt sind. Spalten und Aliase können alle Zeichen enthalten, wobei Spaltennamen bis zu 64 Zeichen lang sein dürfen, Aliase dagegen 255. Anders als bei anderen Datenbanksystemen sind hier auch reine Ziffernfolgen als Bezeichner erlaubt, auch wenn dies nicht unbedingt erstrebenswert ist. Ist ein Bezeichner eventuell mit anderen Elementen der Datenbank verwechselbar (zum Beispiel, weil er genauso heißt wie ein reserviertes Wort, siehe Anhang), muss er in Anführungszeichen stehen. Solche Fälle sollten aber soweit wie möglich vermieden werden, weil dadurch häufig schwierig zu identifizierende Fehler entstehen. Auch sollte man auf Umlaute und Ähnliches verzichten. Diese sind zwar prinzipiell möglich, man muss solche Bezeichner dann allerdings komplett mit einem accent grave umgeben. Ein Datenbank „Schüler“ müsste dann wie folgt angelegt werden: CREATE TABLE `Schüler` ...

Diese Anführungszeichen müssten dann bei jeder Nutzung des Bezeichners verwendet werden. Zudem ist nicht immer sichergestellt, dass ein anderer Benutzer, der mit dieser Datenbank arbeiten will, diese Zeichen leicht erreichen kann. Die somit neu erstellte Datenbank ist allerdings noch nicht aktiviert. Dies geschieht nun erst durch USE: mysql> USE musikschule

Man kann auch schon beim Starten von mysql dafür sorgen, dass eine Datenbank ausgewählt ist, indem man ihren Namen in der Kommandozeile mit angibt: prompt> mysql musikschule

Falls man Host und Benutzer mit angibt und auch ein Kennwort eingeben will, sähe das Ganze so aus: prompt> mysql -h truhe -u demmig -p musikschule

Man beachte, dass „musikschule“ hier nicht das Kennwort ist, da zwischen dem Parameter -p und dem Kennwort kein Leerzeichen stehen darf.

   



Sandini Bib

Somit haben wir unsere eigene Datenbank erstellt. Wenn man nun einen Blick in das Datenverzeichnis von MySQL wirft (zum Beispiel /var/ lib/mysql bzw. C:\mysql\data), sieht man ein leeres Verzeichnis mit dem Namen musikschule. Im nächsten Abschnitt geht es nun um die Tabellen, die es der Datenbank erst ermöglichen, mit Leben gefüllt zu werden.

2.6

Erstellen der Tabellen

Eine Datenbank haben wir nun, aber wohin mit den Daten? Dafür benötigen wir die Tabellen. Welche Tabellen benötigt werden, muss aus dem Modell ermittelt werden. Eine Tabelle besteht, wie schon im vorigen Kapitel erläutert, aus Zeilen und Spalten. Jede Spalte erhält einen Namen und eine Datentyp, der beschreibt, was für Daten in der Spalte abgelegt werden können. Dazu kommt die Angabe, ob die Spalte leer bleiben darf, ob sie Grundlage eines Indexes ist und ob bei Nichtangabe eines Wertes ein Standardwert genutzt werden soll.

2.6.1 Anlegen einer Tabelle Um eine Tabelle anzulegen, verwendet man den DDL-Befehl CREATE TABLE. Ein einfaches Beispiel dafür ist:

CREATE TABLE

CREATE TABLE raum (raum_id INT, raum_name VARCHAR(10), etage VARCHAR(3), personen INT);

Damit wird eine Tabelle mit dem Namen raum angelegt, die vier Spalten enthält: eine Kennung raum_id, die ganze Zahlen speichern kann und gleichzeitig der primäre Schlüssel der Tabelle ist, ein Name raum_name, der bis zu zehn beliebige Zeichen verwaltet, eine Etage, die drei Zeichen lang sein kann, und die Anzahl der Personen, die (sinnvoll) in den Raum hineinpassen. Der Datentyp INT steht dabei für ganze Zahlen zwischen -231 und +231-1 (also -2.147.483.648 bis +2.14.7483.647), VARCHAR für Zeichenketten bis zu einer maximalen Länge (die in Klammern angegeben ist). Nun gibt es hierbei einiges zu verbessern. Die raum_id darf nie leer sein, da es sich um den primären Schlüssel handelt.



   

Sandini Bib

Was ist ein primärer Schlüssel? Es handelt sich dabei um ein oder mehrere Felder, die zusammen eindeutig einen Datensatz charakterisieren. Man kann im Prinzip auch Tabellen ohne Primärschlüssel anlegen, dies empfiehlt sich allerdings überhaupt nicht. Es kann sonst später zum Beispiel sehr schwierig sein, Duplikate zu löschen, wenn es keine Möglichkeit gibt, sie irgendwie zu unterscheiden. Man kann als Primärschlüssel natürlich eine Kombination aus Feldern nehmen, die eindeutig sein sollten. Der Nachname wird gerne für so etwas genommen, reicht aber natürlich bei weitem nicht. Vor- und Nachname mögen in einem kleineren Datenbestand ausreichen, aber sehr viel weiter kommt man damit auch nicht. Vielleicht Vorname, Nachname, Ort? Sicher auch nicht. Wie viele Personen mit dem Namen Thomas Schmidt mag es zum Beispiel in Bremen geben? In meiner Schulzeit gab es an meiner Oberstufe zwei davon im gleichen Jahrgang – sie hatten annähernd identische Kurse und lebten beide in Bremen. Man unterschied sie in den aushängenden Listen schließlich mit Thomas Schmidt (H) und Thomas Schmidt (W) – H bzw. W standen für den Anfangsbuchstaben der vorigen Schule... Zudem ist es ausgesprochen ungeschickt, wenn sich ein Primärschlüssel ändern kann. Dann müssen nämlich auch alle Felder in anderen Tabellen, die auf diesen Schlüssel verweisen, geändert werden. Und Namen und Orte ändern sich schließlich schnell einmal – einer der beiden Thomas Schmidts im obigen Beispiel war ich, der bei der Hochzeit den Namen seiner Frau angenommen hat... Was lässt sich daraus folgern? Man sollte immer jeder Tabelle eine eigene Spalte für den Primärschlüssel spendieren. Diese Spalte sollte einen Wert enthalten, der unabhängig von den eigentlichen „Nutzdaten“ ist. Dafür bieten sich Integer-Werte (also Ganzzahlen) an, für die man zudem einen eleganten Mechanismus zum automatischen Erstellen neuer Werte nutzen kann. Ein primärer Schlüssel darf schließlich auch nie leer sein, da er sonst nicht mehr als Schlüssel dienen kann und auch bei Vergleichen nicht mehr so einfach zu berücksichtigen ist. Um dafür zu sorgen, dass eine Spalte immer einen Wert enthalten muss, verpasst man ihr in der Definition ein zusätzliches Argument: NOT NULL. Der Wert NULL bedeutet, dass kein Wert angegeben wurde. Er unterscheidet sich von der 0 bei Zahlen und von der leeren Zeichenkette „“ bei Strings. Man kann einer Spalte auch mitteilen, dass sie NULL enthalten darf, indem man das Argument NULL mitgibt. Allerdings ist dies die Standardvorgabe und erschwert eher den Lesefluss.

  

NOT NULL



Sandini Bib

Neben der raum_id sollte auch der Raumname immer einen Wert enthalten, da er einen Raum in einer für Menschen sinnvoll lesbaren Form enthält (sie würden die Form A5.35 doch sicherlich auch der Zahl 36.432 vorziehen, wenn aus A5.35 neben der Etage gleich auch der Bereich hervorgeht, und man davon ausgehen kann, dass sich A5.35 in der Nähe von A5.34 und A5.36 befindet, deren IDs einen ganz anderen Wert haben können, oder?). Die Etage kann ruhig leer bleiben, da sie nicht immer notwendig ist. Ebenso mag man eventuell die Zahl der Personen weglassen, da dies nicht von Interesse sein muss. Unser Befehl sieht nun also so aus: CREATE TABLE raum (raum_id INT NOT NULL, raum_name VARCHAR(10) NOT NULL, etage VARCHAR(3), personen INT);

(So nebenbei: es zwingt Sie keiner, Befehle in einer so lesbar untergliederten Form einzugeben, wie es hier geschieht. Ich selber mache das auch nicht immer. Aber es hilft ungemein bei der Fehlersuche und wenn man Skripten verwendet, da man dann leichter sehen kann, was sich der Autor bei der ganzen Sache gedacht hat.) AUTO INCREMENT

Wir wollen die Definition noch etwas weiter verfeinern. Da die raum_id ein Primärschlüssel ist, soll sie, wie schon weiter oben vorgeschlagen, automatisch generiert werden. Dazu gibt man das Argument AUTO_INCREMENT mit. Dieses sorgt dafür, dass das entsprechende Feld automatisch mit der höchsten bestehenden Nummer, zusätzlich um eins erhöht, gefüllt wird, falls nicht schon ein Wert übergeben wurde. Damit erhält man eine Nummer, die in dieser Tabelle noch nicht verwendet wurde. Es kann eventuell sein, dass es Lücken gibt (wenn zwischendurch Sätze gelöscht wurden), aber das ist für einen Primärschlüssel nicht wichtig. Bei AUTO_INCREMENT muss man beachten, dass immer nur ein Feld dieses Argument haben kann (von bestimmten exotischeren Tabellentypen abgesehen). Es muss sich dabei um ein ganzzahliges Feld handeln und die Verwendung von negativen Werten ist ausgesprochen unerwünscht, die Dokumentation zu MySQL rät deutlichst davon ab. Da nur positive Zahlen genutzt werden sollten, kann man die Spalte auch gleich mit einem weiteren Attribut namens UNSIGNED versehen. Damit sind keine negativen Werte möglich, die höchste speicherbare Zahl (und damit auch die Anzahl der möglichen Schlüssel) verdoppelt sich aber. Zusätzlich hat man die Möglichkeit, einen Startwert festzulegen, worauf ich aber nur in der späteren kompletten Beschreibung von CREATE TABLE eingehe.



   

Sandini Bib

Schließlich muss die Spalte mit AUTO_INCREMENT eindeutig sein. Man sorgt dafür, indem man sie mit einem eindeutigen Index versieht (siehe bei den Indizes im nächsten Kapitel oder in der kompletten Syntax weiter unten) oder das Attribut PRIMARY KEY anhängt. Auch für die Spalte personen kann man noch etwas tun. Normalerweise sollte in einen Raum immer mindestens eine Person hineinpassen, sonst bräuchte man ihn nicht mit in diese Tabelle aufnehmen (es geht schließlich um Übungsräume). Was spräche also dagegen, gleich einen Standardwert von eins mitzugeben? Dies erreicht man, indem als Attribut auch noch DEFAULT, gefolgt von dem gewünschten Standardwert übergeben wird. Wird nun beim Eintragen von Daten kein Wert für diese Spalte angegeben, erhält sie den vorher definierten Standardwert. Zusätzlich kann man auch hier dafür sorgen, dass die Spalte keine negativen Werte aufnehmen kann (wäre auch nicht wirklich sinnvoll).

DEFAULT

Jetzt hat sich die Definition also weiter verändert: CREATE TABLE raum (raum_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, raum_name VARCHAR(10) NOT NULL, etage VARCHAR(3), personen INT UNSIGNED DEFAULT 1);

Einen letzten Optimierungsschritt wollen wir noch vornehmen: die Datentypen an sich. Das Feld raum_id ist als vorzeichenloses INT definiert. Das bedeutet, dass Zahlen von 0 bis 232-1 verwendet werden können, was einem Speicherbedarf von 4 Byte entspricht. Nun ist es sehr unwahrscheinlich, dass man tatsächlich vier Milliarden Räume zur Verfügung hat, daher macht es durchaus Sinn, einen „kleineren“ Datentyp zu nutzen. Auch wenn heutzutage meist genug Speicher- und Festplattenplatz zur Verfügung steht, sollte man sich zumindest Gedanken über den richtigen Datentyp machen. Dabei darf natürlich nicht zu knapp kalkuliert werden (wie schnell ist eine einmal festgelegte Grenze dann doch überschritten), aber mit einem ordentlichen Puffer versehen kann man durchaus Datentypen nutzen, die nicht immer den kompletten Umfang ermöglichen, der nur dann ausgenutzt wird, wenn die gesamte Weltbevölkerung teilnimmt. Daher wollen wir hier für die raum_id den Datentyp SMALLINT wählen. Dieser ermöglicht (sofern mit UNSIGNED versehen) Werte von 0 bis 65.535 (= 216-1; 2 Byte), was reichen sollte. Genauso wird die Anzahl der Personen meist geringer sein als bei INT möglich. Nun könnte da SMALLINT schon zu klein sein (man stelle sich nur eine Stadion als Ort für eine Musikveranstaltung vor), daher wählen wir hier MEDIUMINT – vorzeichenlos von 0 bis 16.777.215 (= 224-1; 3 Byte).

  



Sandini Bib

Nun wollen wir etwas ins Detail gehen und uns die Etage vornehmen. Diese ist als VARCHAR(3) definiert. Das VAR in VARCHAR bedeutet, dass nur so viel Platz benötigt wird, wie der gespeicherte Text tatsächlich lang ist. Dazu kommt nur noch ein Byte für die Längenangabe. Nutzt man stattdessen den Typ CHAR, wird mit fester Länge abgespeichert und der übrige Platz mit Leerzeichen aufgefüllt. MySQL macht nun etwas, was als „stille Spaltendefinitionsänderung“ bezeichnet wird: Spalten vom Typ VARCHAR(n), deren maximale Länge n kleiner als vier ist, werden automatisch in den Typ CHAR(n) umgewandelt. Dies hat den Grund, dass die Speicherersparnis bis zu drei Zeichen zu gering ist (man benötigt schließlich immer noch ein Byte für die Länge), der Typ CHAR dafür aber einen kleinen Tick schneller ist als VARCHAR, weil er mit festen Längen rechnen kann. Um nun MySQL zuvorzukommen, wandeln wir den Typ selber um. Es ergibt sich schließlich folgende Definition: CREATE TABLE raum (raum_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, raum_name VARCHAR(10) NOT NULL, etage CHAR(3), personen MEDIUMINT UNSIGNED DEFAULT 1);

Führt man diesen Befehl nun in MySQL aus (und hat möglichst vorher die Datenbank musikschule gewählt), wird die Tabelle raum angelegt. Man kann sich die Definition anzeigen lassen. Dazu verwendet man den Befehl SHOW COLUMNS FROM table_name (siehe Abbildung 2.1)

SHOW COLUMNS

mysql> SHOW COLUMNS FROM raum; +-----------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-----------------------+------+-----+---------+----------------+ | raum_id | smallint(5) unsigned | | PRI | NULL | auto_increment | | raum_name | varchar(10) | | | | | | etage | char(3) | YES | | NULL | | | personen | mediumint(8) unsigned | YES | | 1 | | +-----------+-----------------------+------+-----+---------+----------------+

Abbildung 2.1: Struktur der Tabelle raum

Man kann übrigens diese Ausgabe noch einschränken, wenn man nach dem Tabellennamen noch LIKE 'filter' anhängt. Dabei ist filter eine Zeichenkette, die die Spalten beschränkt, die ausgegeben werden sollen. Jokerzeichen sind erlaubt, dabei gilt (anders als im Betriebssystem, aber so wie immer bei SQL) das Prozentzeichen (%) als Ersatz für beliebig viele Zeichen und der Unterstrich (_) als Ersatz für genau ein Zeichen. Mit SHOW COLUMNS FROM raum LIKE 'raum%'; erhält man also die Ausgabe in Abbildung 2.2.



   

Sandini Bib

mysql> SHOW COLUMNS FROM raum LIKE 'raum%'; +-----------+----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+----------------------+------+-----+---------+----------------+ | raum_id | smallint(5) unsigned | | PRI | NULL | auto_increment | | raum_name | varchar(10) | | | | | +-----------+----------------------+------+-----+---------+----------------+

Abbildung 2.2: Eingeschränkte Ausgabe der Struktur der Tabelle raum

Die gleiche Ausgabe wie bei SHOW COLUMNS FROM erhält man, wenn man den Befehl DESCRIBE oder DESC nutzt. Er ist zwar eigentlich nur vorhanden, um es Umsteigern von Oracle etwas bequemer zu machen, aber durch seine Kürze auch für andere Nutzer sehr praktisch. Um dort die angezeigten Spalten einzuschränken, muss man den Filter direkt nach dem Tabellennamen angeben. Für das obige Ergebnis lautete der Befehl also DESCRIBE

DESCRIBE

raum 'raum%';

Tabellen werden auch gerne in sogenannten ER-Diagrammen (Entity Relationship-Diagramme) dargestellt. Die obige Tabelle ist in Abbildung 2.3 zu sehen. Eine einzelne Tabelle ist dabei natürlich nur die „Entity“, Relationships kommen erst zustande, wenn man mehrere Tabellen miteinander verbindet. Das wird in Kapitel 2.6.3 zu sehen sein.

Abbildung 2.3: Die Tabelle raum in einem ER-Diagramm

Um das Diagramm nicht allzu unübersichtlich zu gestalten (vor allem dann, wenn später mehr Tabellen dazukommen), wurden einige Parameter weggelassen. Entscheidend sind hier der Tabellenname sowie die Spaltennamen und Datentypen. Zusätzlich ist der Primärschlüssel raum_id unterstrichen.

2.6.2 Datentypen Bevor wir uns mit den restlichen Tabellen des Modells befassen, wollen wir zunächst aufzeigen, welche Datentypen es gibt und was für Besonderheiten sie haben. Numerische Werte Die numerischen Datentypen unterteilen sich noch einmal in exakte Typen mit begrenzter Stellenzahl und in Gleitkommatypen.

  

numerische Felder



Sandini Bib

Exakte Typen sind: TINYINT[(m)] [UNSIGNED] [ZEROFILL]

speichert ganze Zahlen im Bereich von -128 bis +127 (-27 bis +27-1). Wird das Attribut UNSIGNED mit angegeben, werden Zahlen von 0 bis +255 gespeichert (0 bis +28-1). Der Speicherbedarf beträgt 1 Byte. Der optionale Parameter m wird genutzt, um festzulegen, mit welcher Breite die Spalte auf dem Bildschirm ausgegeben wird. Er schränkt nicht den Wertebereich ein. Wird zusätzlich das Attribut ZEROFILL mit angegeben, werden bei der Ausgabe die fehlenden Stellen mit 0 aufgefüllt. Bei einer Definition TINYINT(2) ZEROFILL würde eine 3 als 03 ausgegeben werden, eine 123 aber weiterhin als 123. Wird ZEROFILL genutzt, fügt MySQL automatisch ein UNSIGNED mit hinzu. BIT

entspricht dem Typ TINYINT(1). BOOL

entspricht dem Typ TINYINT(1). SMALLINT[(m)] [UNSIGNED] [ZEROFILL]

speichert ganze Zahlen im Bereich von -32.768 bis +32.767 (-215 bis +215-1). Wird das Attribut UNSIGNED mit angegeben, werden Zahlen von 0 bis +65.535 gespeichert (0 bis +216-1). Der Speicherbedarf beträgt 2 Byte. Für die weiteren Attribute siehe TINYINT. MEDIUMINT[(m)] [UNSIGNED] [ZEROFILL]

speichert ganze Zahlen im Bereich von -8.388.608 bis +8.388.607 (-223 bis +223-1). Wird das Attribut UNSIGNED mit angegeben, werden Zahlen von 0 bis +16.777.215 gespeichert (0 bis +224-1). Der Speicherbedarf beträgt 3 Byte. Für die weiteren Attribute siehe TINYINT. INT[(m)] [UNSIGNED] [ZEROFILL]

speichert ganze Zahlen im Bereich von -2.147.483.648 bis +2.147.483.647 (-231 bis +231-1). Wird das Attribut UNSIGNED mit angegeben, werden Zahlen von 0 bis +4.294.967.295 gespeichert (0 bis +232-1). Der Speicherbedarf beträgt 4 Byte. Für die weiteren Attribute siehe TINYINT.



  

Sandini Bib

INTEGER[(m)] [UNSIGNED] [ZEROFILL]

entspricht exakt dem Typ INT. BIGINT[(m)] [UNSIGNED] [ZEROFILL]

speichert ganze Zahlen im Bereich von -9.223.372.036.854.775.808 bis +9.223.372.036.854.775.807 (-263 bis +263-1). Wird das Attribut UNSIGNED mit angegeben, werden Zahlen von 0 bis + 18.446.744.073.709.551.615 gespeichert (0 bis +264-1). Der Speicherbedarf beträgt 8 Byte. Für die weiteren Attribute siehe TINYINT. Beim Umgang mit Werten vom Typ BIGINT, speziell bei vorzeichenlosen Typen, muss man Vorsicht walten lassen und vorher speziell bei Berechnungen einen Blick in die Dokumentation von MySQL werfen. DECIMAL[(m[,d])] [UNSIGNED] [ZEROFILL]

speichert Zahlen als Zeichenketten ab, welche allerdings in Berechnungen normal verwendet werden können. m definiert die Anzahl der Stellen, d die Anzahl der Nachkommastellen. Die Werte müssen zwischen -3,402823466·1038 und -1,175494351·10-38 oder 1,175494351·10-38 und 3,402823466·1038 liegen oder 0 sein, allerdings ist die Anzahl der Stellen meistens die entscheidendere Begrenzung. Lässt man d weg, wird dies auf 0 gesetzt, lässt man auch m weg, wird dies auf 10 gesetzt. ZEROFILL hat die gleiche Bedeutung wie bei TINYINT. Der Speicherbedarf liegt bei m Byte plus 2 Byte für das Vorzeichen und den Dezimalpunkt. Sind keine Nachkommastellen angegeben, wird nur das Byte für das Vorzeichen benötigt. DEC[(m[,d])] [UNSIGNED] [ZEROFILL] DEC entspricht exakt dem Typ DECIMAL. NUMERIC[(m[,d])] [UNSIGNED] [ZEROFILL] NUMERIC ist eine weitere Darstellung für DECIMAL.

Gleitkommatypen sind: FLOAT[(m, d)] [ZEROFILL]

speichert Gleitkommazahlen einfacher Genauigkeit mit folgenden möglichen Werten: -3,402823466·1038 bis -1,175494351·10-38, 0 und 1,175494351·10-38 bis 3,402823466·1038. m und d bedeuten die Anzahl der Stellen bei der Ausgabe und die Zahl der Nachkommastellen. Anders als bei DECIMAL & Co ist hier die Ausgabebreite unerheblich für die

  



Sandini Bib

Größe der speicherbaren Zahl. Sind m und d nicht mit angegeben, sind diese tatsächlich undefiniert und es wird die nötige Stellenzahl bei der Ausgabe verwendet. ZEROFILL hat die übliche Funktion, wie bei TINYINT beschrieben, allerdings ist es für negative Werte (die dann trotzdem erlaubt sind) nicht empfehlenswert, da die Ausgabe solcher Werte unschön ist. Werte vom Typ FLOAT benötigen 4 Byte Speicherplatz. DOUBLE[(m, d)] [ZEROFILL]

Speichert Gleitkommazahlen doppelter Genauigkeit mit folgenden prinzipiell möglichen Werten: -1,7976931348623157·10308 bis -2,2250738585072014·10-308, 0 und 2,2250738585072014·10-308 bis 1,7976931348623157·10308. Die weiteren Parameter und Hinweise sind bei FLOAT nachzulesen. Werte vom Typ DOUBLE verbrauchen 8 Byte Speicherplatz. DOUBLE PRECISION[(m, d)] [ZEROFILL]

ist identisch zu DOUBLE. REAL[(m, d)] [ZEROFILL]

ist identisch zu DOUBLE. FLOAT (p) [ZEROFILL]

wurde aus Kompatibilitätsgründen zu ODBC aufgenommen und wird in Abhängigkeit von p als FLOAT (p £ 24) oder als DOUBLE (24 < p £ 53) abgespeichert. Stringwerte [NATIONAL] CHAR (m) [BINARY]

Zeichenketten

definiert eine Zeichenkette der Länge m. Werden weniger Zeichen genutzt, werden die restlichen mit Leerzeichen aufgefüllt. Das Attribut NATIONAL ist aus ANSI-Kompatibilitätsgründen aufgenommen und dient dazu, MySQL anzuweisen, den Standardzeichensatz zu verwenden. Allerdings ist dies sowieso das normale Verhalten von MySQL. Beim Sortieren und Vergleichen wird Rücksicht auf den Zeichensatz genommen sowie die Groß- und Kleinschreibung ignoriert. Will man dies vermeiden, muss man das Attribut BINARY angeben. m kann zwischen 0 und 255 liegen, als Speicherplatz werden m Byte verbraucht. Wird als Länge 0 angegeben, wird nur ein Bit benötigt, dafür können aber auch nur zwei verschiedene Werte abgelegt werden: NULL und eine leere Zeichenkette.



   

Sandini Bib

[NATIONAL] CHARACTER (m) [BINARY]

ist identisch zu obigem CHAR. NCHAR (m) [BINARY]

entspricht NATIONAL CHAR(m) [BINARY]. CHAR

entspricht dem Typ CHAR(1). [NATIONAL] VARCHAR (m) [BINARY]

speichert Zeichenketten variabler Länge, maximal allerdings m Zeichen. Die maximale Länge m kann zwischen 1 und 255 liegen. Anders als bei CHAR wird hier nur soviel Platz verbraucht, wie der String tatsächlich lang ist, plus einem Byte für die Länge. NATIONAL und BINARY entsprechen den Attributen bei CHAR. Leerzeichen am Ende des Strings werden automatisch abgeschnitten. [NATIONAL] CHARACTER VARYING (m) [BINARY]

entspricht dem Typ VARCHAR. TEXT

speichert maximal 65.535 Zeichen (= 216-1). TEXT ist wie ein langer VARCHAR, allerdings gibt es einige (wenige) Einschränkungen: Spalten dieses Typs können keinen Standardwert haben, Leerzeichen am Ende des Strings werden nicht abgeschnitten und es gibt gewisse Dinge, die man beim Gruppieren und Sortieren beachten muss (siehe dazu auch die Dokumentation zu MySQL). Felder dieses Typs werden separat gespeichert. BLOB

Felder dieses Typs verhalten sich wie Felder vom Typ TEXT, mit der Ausnahme, dass Groß- und Kleinschreibung beim Sortieren und Vergleichen berücksichtigt werden (also quasi ein langer VARCHAR BINARY). TINYTEXT

speichert maximal 255 Zeichen (= 28-1). Ansonsten ist dieser Typ identisch zu TEXT. TINYBLOB

speichert maximal 255 Zeichen (= 28-1). Ansonsten ist dieser Typ identisch zu BLOB.

  



Sandini Bib

MEDIUMTEXT

speichert maximal 16.777.215 Zeichen (= 224-1). Ansonsten ist dieser Typ identisch zu TEXT. MEDIUMBLOB

speichert maximal 16.777.215 Zeichen (= 224-1). Ansonsten ist dieser Typ identisch zu BLOB. LONGTEXT

speichert maximal 4.294.967.295 Zeichen (= 232-1). Ansonsten ist dieser Typ identisch zu TEXT. Allerdings sorgen Beschränkungen von MySQL in der Paketgröße des Client/Server-Protokolls und bei der Verwaltung von bestimmten Tabellentypen dafür, dass nicht die komplette Größe genutzt werden kann. LONGBLOB

speichert maximal 4.294.967.295 Zeichen (= 232-1). Ansonsten ist dieser Typ identisch zu BLOB. Es gilt die gleiche Einschränkung wie bei LONGTEXT. ENUM ('Wert1', 'Wert2', ...)

speichert einen Wert ab, der in der Liste der vorgegebenen Werte vorhanden sein muss. Es können maximal 65.535 verschiedene Werte angegeben werden. Dabei werden nur 1 Byte oder 2 Byte verbraucht, abhängig davon, ob es mehr als 255 verschiedene Werte gibt. SET ('Wert1', 'Wert2', ...)

speichert eine Menge von Werten ab, die aus den vorgegebenen Werten ausgewählt sein müssen. Maximal 64 verschiedene Werte sind möglich und abhängig von der Zahl unterschiedlicher Werte werden 1 Byte, 2 Byte, 3 Byte, 4 Byte oder 8 Byte verbraucht. Dieser Typ kann sehr nützlich sein, auch wenn er eigentlich einem sauberen Datenmodell widerspricht (bei dem solche Sets in einer eigenen Tabelle aufgelöst werden sollten, was aber nicht immer der performanteste Weg ist). Datumswerte und Uhrzeiten DATE

Datum/Uhrzeit

speichert ein Datum zwischen dem 1.1.1000 und dem 31.12.9999. Datumswerte werden im Format JJJJ-MM-TT angezeigt, lassen sich aber auf vielfältige Weise für Berechnungen und Ausgaben nutzen. Felder dieses Typs verbrauchen 3 Byte.



   

Sandini Bib

DATETIME

speichert ein Datum und eine Uhrzeit zwischen dem 1.1.1000 00:00:00 und dem 31.12.9999 23:59:59. Werte dieses Typs werden im Format JJJJ-MM-TT HH:MM:SS angezeigt, lassen sich aber wie Werte vom Type DATE unterschiedlichst nutzen. Felder vom Type DATETIME benötigen 4 Byte Speicher. TIMESTAMP [(m)]

speichert einen Zeitstempel in einem Bereich zwischen dem 1.1.1970 00:00:00 und einem Zeitpunkt im Jahr 2037. Ausgegeben werden die Werte in Abhängigkeit von m wie folgt: Ist m = 14 oder nicht angegeben, wird JJJJMMTTHHMMSS ausgegeben. Bei m = 12 wird JJMMTTHHMMSS, bei m = 8 JJJJMMTT und bei m = 6 JJMMTT ausgegeben. Ein solcher Zeitstempel eignet sich hervorragend, um Änderungen in einem Datensatz zu protokollieren. Setzt man nämlich ein Feld dieses Typs auf NULL oder gibt keinen eigenen Wert an, wird automatisch der momentane Zeitpunkt eingetragen. Zeitstempel werden immer mit 4 Byte abgespeichert. TIME

speichert eine Uhrzeit im Bereich -838:59:59 bis +838:59:59 ab. Uhrzeiten werden in MySQL in der Form HH:MM:SS ausgegeben, können aber mit verschiedenen Funktionen und in vielen Variationen verwendet werden. Felder dieses Typs benötigen 3 Byte Speicherplatz. YEAR [(2|4)]

speichert ein Jahr in einem Byte ab. Bei der 4-stelligen Version (die auch Standard ist), lassen sich die Jahre 1901 bis 2155 und 0 nutzen, bei der 2-stelligen Variante nur die Jahre 1970 bis 2069 (also 70–69).

2.6.3 Tabellen des Modells Für jedes Objekt oder Ereignis benötigen wir eine Tabelle, um es abzubilden. In Kapitel 2.2.2 haben wir die verschiedenen Elemente des Modells aufgeführt, die eigentlich direkt in eine Tabelle umgesetzt werden können. Die Eigenschaften des Objekts oder Ereignisses werden dann zu den Spalten der Tabelle. Eine Tabelle haben wir im Prinzip schon erstellt: die für die Räume. Aber das ist natürlich eine der einfachsten. Daher nehmen wir uns jetzt die restlichen Tabellen vor.

  



Sandini Bib

Beginnen wir mit den Schülern. Die zugehörige Tabelle schueler sollte folgende Spalten enthalten: • schueler_id: der Primärschlüssel als ganze Zahl, die automatisch hochgezählt wird. • nachname: ein Stringfeld der Länge 30. Die Länge eines Feldes ist immer ein Kompromiss zwischen Speicherverbrauch (der natürlich bei einem String variabler Länge nicht ins Gewicht fällt) und Einschränkung bei der Nutzung. Um auf einen Nachnamen mit 30 Zeichen zu kommen, muss man sich allerdings schon anstrengen. Der Nachname sollte immer gefüllt sein müssen. • vorname: ebenfalls ein String mit bis zu 30 Zeichen. • geburtsdatum: ein Datumsfeld, das auch leer bleiben darf (worauf ich bei den folgenden Feldern nicht mehr hinweise). • strasse: Stringfeld mit 30 Zeichen. • plz: Man ist geneigt, hier ein numerisches Feld mit bis zu fünf Stellen zu nehmen. Allerdings kann es dann zu zwei Problemen kommen: – Ausländische Postleitzahlen haben durchaus auch sechs Stellen. – Es gibt in Deutschland Postleitzahlen, die mit einer 0 beginnen. So würde dann die PLZ 01234 als 1234 ausgegeben werden, was dann eventuell als alte Postleitzahl interpretiert würde. Man könnte das Attribut ZEROFILL nutzen, allerdings ist dann immer noch nicht sichergestellt, dass ein externes Programm, welches auf die Daten zugreift, darauf Rücksicht nimmt (im Allgemeinen nämlich nicht). Daher sollte die Postleitzahl als Stringfeld mit sechs Zeichen angelegt werden. Generell sollte man auch Felder, die nur Ziffern enthalten, als Stringfelder deklarieren, sofern nicht mit ihnen gerechnet werden muss. Bei diesem Feld kann man auch CHAR statt VARCHAR nutzen, da es meist fast komplett gefüllt wird. • ort: ein Zeichenfeld mit 40 Zeichen. • telefon: Auch hier darf man aus nachvollziehbaren Gründen kein numerisches Feld nutzen; daher ein Stringfeld mit 20 Zeichen. • mobil: ebenso String mit 20 Zeichen. • email: ein Stringfeld mit 50 Zeichen. • einzug: Angabe, ob eine Einzugsermächtigung vorliegt. Dieses Feld hat den Typ BOOL. Zudem soll es als Standardwert eine 0 haben (für „keine Einzugsermächtigung“). • bankname: der Name der Bank, falls eine Einzugsermächtigung vorliegt. Die Länge des Stringfeldes beträgt 58 Stellen (das ist auch die Länge im offiziellen BLZ-Verzeichnis). • blz: Bankleitzahl: Zeichenfeld mit 8 Stellen.



   

Sandini Bib

• kontonr: Kontonummer, 10 Stellen als Zeichenkette. • kontoname: Name des Kontoinhabers, 30 Stellen Stringfeld. Damit sollten wir alle Elemente eines Schülers zunächst erschlagen haben. Der Befehl zum Anlegen der Tabelle lautet nun: CREATE TABLE schueler (schueler_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, nachname VARCHAR(30) NOT NULL, vorname VARCHAR(30), geburtsdatum DATE, strasse VARCHAR(30), plz CHAR(6), ort VARCHAR(40), telefon VARCHAR(20), mobil VARCHAR(20), email VARCHAR(50), einzug BOOL DEFAULT 0, bankname VARCHAR(58), blz CHAR(8), kontonr CHAR(10), kontoname VARCHAR(30));

Schaut man sich nun das Ergebnis mit DESCRIBE schueler an, sieht es wie in Abbildung 2.4 aus. mysql> DESCRIBE schueler; +--------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+------------------+------+-----+---------+----------------+ | schueler_id | int(10) unsigned | | PRI | NULL | auto_increment | | nachname | varchar(30) | | | | | | vorname | varchar(30) | YES | | NULL | | | geburtsdatum | date | YES | | NULL | | | strasse | varchar(30) | YES | | NULL | | | plz | varchar(6) | YES | | NULL | | | ort | varchar(40) | YES | | NULL | | | telefon | varchar(20) | YES | | NULL | | | mobil | varchar(20) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | | einzug | tinyint(1) | YES | | 0 | | | bankname | varchar(58) | YES | | NULL | | | blz | varchar(8) | YES | | NULL | | | kontonr | varchar(10) | YES | | NULL | | | kontoname | varchar(30) | YES | | NULL | | +--------------+------------------+------+-----+---------+----------------+

Abbildung 2.4: Struktur der Tabelle schueler

Dabei fällt auf, dass die Felder plz, blz und kontonr nun vom Typ VARCHAR sind. Das liegt daran, dass automatisch der Typ gewechselt wird, wenn in einer Tabelle mindestens ein Wert vom Typ VARCHAR vorkommt und das entsprechende Feld eine Länge von drei oder mehr Zeichen hat. Das soll uns hier aber nicht weiter stören, sondern einfach als Optimierung von MySQL angesehen werden.

  



Sandini Bib

Die weiteren Tabellen will ich hier nicht so detailliert aufführen, sondern lediglich die Tabellendefinitionen aufzählen und auf die einzelnen Besonderheiten und neuen Aspekte eingehen. Zunächst wäre da natürlich die Tabelle für die Lehrer. Sie ist der der Schüler sehr ähnlich, nur wird das Feld einzug nicht benötigt, da die Lehrer keine Erlaubnis geben müssen, um Geld zu erhalten. Hier die Definition für die Lehrer: CREATE TABLE lehrer (lehrer_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, nachname VARCHAR(30) NOT NULL, vorname VARCHAR(30), geburtsdatum DATE, strasse VARCHAR(30), plz CHAR(6), ort VARCHAR(40), telefon VARCHAR(20), mobil VARCHAR(20), email VARCHAR(50), bankname VARCHAR(58), blz CHAR(8), kontonr CHAR(10), kontoname VARCHAR(30));

Die Tabelle für die möglichen Räume haben wir schon weiter oben ausgearbeitet, deshalb hier nur kurz die Tabellendefinition als Wiederholung: CREATE TABLE raum (raum_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, raum_name VARCHAR(10) NOT NULL, etage CHAR(3), personen MEDIUMINT UNSIGNED DEFAULT 1);

Zudem haben wir eine Tabelle für die möglichen Instrumente. Auch diese ist recht einfach: CREATE TABLE instrument (instrument_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, instr_name VARCHAR(50) NOT NULL, instr_gruppe VARCHAR(50));

Jetzt kommen wir zum Unterricht. Auch dieser ist eigentlich auf den ersten Blick nicht so kompliziert. Wir benötigen (neben der obligaten ID) einen Lehrer, den oder die Schüler, das Instrument, um das es geht, den Raum, in dem alles stattfindet, den Wochentag und die Uhrzeit, die Kos-



   

Sandini Bib

ten pro Monat sowie den Vermerk, ob es sich um Einzel- oder Gruppenunterricht handelt. Man könnte also eine Tabelle wie folgt aufstellen: CREATE TABLE unterricht (unterricht_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, lehrer VARCHAR(30) NOT NULL, schueler VARCHAR(30), instrument VARCHAR(50) NOT NULL, raum VARCHAR(10) NOT NULL, wochentag TINYINT UNSIGNED NOT NULL, uhrzeit_von TIME NOT NULL, uhrzeit_bis TIME NOT NULL, kosten DECIMAL(6,2) DEFAULT 0.0, einzel BOOL);

Dabei wird der Wochentag als Zahl von 0 bis 6 (Montag bis Sonntag) aufgefasst. Der Grund dafür ist, dass es bei MySQL eine Funktion WEEKDAY(Datum) gibt, die diese Werte zurückgibt. Irgendwie gibt es mit dieser Definition jetzt aber ein Problem: Was, wenn mehrere Schüler teilnehmen (was bei Gruppenunterricht zum Beispiel durchaus Sinn der Sache ist)? Speichert man die Schüler dann, durch Komma getrennt, im Feld schueler ab? Das wäre schlecht, da dieses Feld nur eine begrenzte Länge hat. Zudem kann man dann später sehr schlecht herausfinden, wer wo mitmacht und vor allem ist es ein Heidenaufwand, Schüler einzufügen oder zu löschen. So kann man das also nicht machen. Die Lösung ist stattdessen also eine eigene Tabelle, die abspeichert, welcher Schüler an welchem Unterricht teilnimmt. An jedem Unterricht können (theoretisch) beliebig viele Schüler mitmachen. Solche Beziehungen nennt man 1:n-Beziehungen. D.h., an einem Unterrichtseintrag hängen n Schüler. Diese neue Tabelle wollen wir unterricht_tn (für Unterricht-Teilnehmer) nennen und sie sieht wie folgt aus: CREATE TABLE unterricht_tn (unterricht_tn_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, unterricht_id INT UNSIGNED NOT NULL, schueler VARCHAR(30));

Jetzt hat jeder Schüler seinen eigenen Tabelleneintrag und es kann beliebig viele Schüler pro Unterricht geben. In unterricht_id steht die ID des entsprechenden Eintrags der Unterrichtstabelle, so dass man (da das Feld nicht leer sein darf) immer weiß, zu welchem Unterricht dieser Eintrag gehört. Die Tabelle unterricht sähe nun wie folgt aus:

  



Sandini Bib

CREATE TABLE unterricht (unterricht_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, lehrer VARCHAR(30) NOT NULL, instrument VARCHAR(50) NOT NULL, raum VARCHAR(10) NOT NULL, wochentag TINYINT UNSIGNED NOT NULL, uhrzeit_von TIME NOT NULL, uhrzeit_bis TIME NOT NULL, kosten DECIMAL(6,2) DEFAULT 0.0, einzel BOOL);

Diese Tabellen und vor allem die Beziehung untereinander kann man nun wieder in einem ER-Diagramm darstellen, welches in Abbildung 2.5 zu sehen ist. Dieses Aufteilen der Daten in Tabellen ist Teil der Normalisierung, die zur Ergänzung in Kapitel 2.6.5 beschrieben ist.

Abbildung 2.5: ER-Diagramm für die Unterrichtstabellen

Die Verbindung zwischen den beiden Tabellen geschieht hier durch das Feld unterricht_id. Dieses Feld bezeichnet man als Fremdschlüssel. Solche Fremdschlüssel kann man aber noch viel mehr und sehr sinnvoll in den beiden Tabellen verwenden. Bisher haben wir nämlich nicht festgelegt, wie der Lehrer, das Instrument oder die Schüler gespeichert werden. Der Feldtyp VARCHAR(30) bzw. VARCHAR(50) legt nahe, dass der Name genutzt werden soll. Das aber ist – wie wir schon weiter oben fest-



   

Sandini Bib

gestellt haben – ein denkbar schlechter Identifizierer. Denn erstens können sich Namen ändern (womit diese Änderung in vielen Tabellen nachgezogen werden müsste) und zweitens ist ein Name sehr selten wirklich eindeutig. Also nutzen wir für möglichst viele Relationen die Schlüssel, die wir bisher in jeder Tabelle mit angelegt haben. Dieser Vorgang wird als „Normalisieren“ bezeichnet (wobei schon das Abtrennen der Schüler in eine eigene Tabelle ein Teil der Normalisierung war, die man recht weit treiben kann). Unsere endgültigen Tabellen für den Unterricht sehen also wie folgt aus: CREATE TABLE unterricht (unterricht_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, lehrer_id INT UNSIGNED NOT NULL, instrument_id INT UNSIGNED NOT NULL, raum_id SMALLINT UNSIGNED NOT NULL, wochentag TINYINT UNSIGNED NOT NULL, uhrzeit_von TIME NOT NULL, uhrzeit_bis TIME NOT NULL, kosten DECIMAL(6,2) DEFAULT 0.0, einzel BOOL); CREATE TABLE unterricht_tn (unterricht_tn_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, unterricht_id INT UNSIGNED NOT NULL, schueler_id INT UNSIGNED NOT NULL);

Das ER-Diagramm wird nun natürlich schnell größer, da nun auch die Lehrer-, Schüler-, Instrumenten- und Raumtabellen mit beteiligt sind. In Abbildung 2.6 sind daher für diese Tabellen nur die wichtigsten Felder mit aufgeführt. Solche Relationen über Fremdschlüssel lassen sich in anderen Datenbank-Systemen durch Regeln abbilden, so dass es zum Beispiel nicht möglich ist, in der Unterrichtstabelle eine raum_id einzutragen, die gar nicht vorhanden ist. Diese Prüfung muss in MySQL über die Applikation erfolgen. Die Syntax dazu ist zwar zum Teil implementiert, so dass das Übernehmen von CREATE TABLE-Anweisungen aus anderen Datenbanken wenig Probleme bereitet, allerdings haben diese Regeln keine Auswirkungen.

  



Sandini Bib

Abbildung 2.6: ER-Diagramm für die Unterrichtstabellen und deren Relationen

Beim Orchester/Chor haben wir einen ähnlichen Aufbau wie beim Unterricht, nur dass diesmal jeder Schüler/Teilnehmer ein anderes Instrument nutzen kann. Zudem haben wir einen Namen für das Orchester und diesmal keine Kosten (die Musikschule ist schließlich froh, wenn möglichst viele Schüler teilnehmen). Also sehen die Tabellen so aus: CREATE TABLE orchester (orchester_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, lehrer_id INT UNSIGNED NOT NULL, orch_name VARCHAR(50) NOT NULL, raum_id SMALLINT UNSIGNED NOT NULL, wochentag TINYINT UNSIGNED NOT NULL, uhrzeit_von TIME NOT NULL, uhrzeit_bis TIME NOT NULL); CREATE TABLE orchester_tn (orchester_tn_id INT UNSIGNED orchester_id INT UNSIGNED schueler_id INT UNSIGNED instrument_id INT UNSIGNED



   

NOT NOT NOT NOT

NULL AUTO_INCREMENT PRIMARY KEY, NULL, NULL, NULL);

Sandini Bib

Es sei hier noch angemerkt, dass bei einem Chor die Schüler als „Instrument“ ihre Stimme in den verschiedenen Tonlagen (Sopran, Alt, Tenor, Bass) „nutzen“. Das bedeutet, dass diese auch in der Tabelle instrument eingetragen sein müssen (was aber gar nicht so verkehrt ist, da Gesang schließlich auch unterrichtet wird). Das ER-Diagramm sieht hier ein wenig anders aus. Die Instrumente sind nun mit den Schüler verbunden (siehe Abbildung 2.7).

Abbildung 2.7: ER-Diagramm für die Orchestertabellen und deren Relationen

Für die Leihinstrumente wird es nun wieder etwas einfacher: Wir haben neben den üblichen Verdächtigen nur einen Verweis auf die Instrumententabelle: CREATE TABLE leihinstrument (leihinstrument_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, instrument_id INT UNSIGNED NOT NULL, hersteller VARCHAR(30), typ VARCHAR(30) NOT NULL, kosten DECIMAL(6,2) NOT NULL DEFAULT 0.0);

Jetzt fehlen nur noch die Tabellen für die Verwaltung der Leihinstrumente. Zunächst einmal die eigentliche Ausleihe. Wir brauchen Verweise auf das Leihinstrument und den Schüler. Dazu das Datum, an dem

  



Sandini Bib

das Instrument verliehen wurde und das Datum, an dem es voraussichtlich zurückkommt (das ist meist nicht so genau bekannt, deshalb haben wir es hier nicht mit NOT NULL versehen). CREATE TABLE ausleihe (ausleihe_id leihinstrument_id schueler_id datum_von datum_bis

INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, INT UNSIGNED NOT NULL, INT UNSIGNED NOT NULL, DATE NOT NULL, DATE);

Schließlich noch die Warteliste. Wir benötigen das Leihinstrument, den interessierten Schüler und das Datum, an dem er Interesse bekundet hat (um denjenigen Vorrang geben zu können, die am längsten warten): CREATE TABLE warteliste (warteliste_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, leihinstrument_id INT UNSIGNED NOT NULL, schueler_id INT UNSIGNED NOT NULL, datum DATE NOT NULL);

Jetzt sollten wir alle Tabellen zusammenhaben. Aber sind auch alle angelegt worden? Haben wir keine vergessen? Um das zu prüfen, gibt es den Befehl SHOW TABLES. Mit ihm kann man sich eine Übersicht der Tabellen in der aktuellen Datenbank anzeigen lassen:

SHOW TABLES

mysql> SHOW TABLES; +-----------------------+ | Tables_in_musikschule | +-----------------------+ | ausleihe | | instrument | | lehrer | | leihinstrument | | orchester | | orchester_tn | | raum | | schueler | | unterricht | | unterricht_tn | | warteliste | +-----------------------+

Auch hier kann man – wie bei SHOW COLUMNS (und auch bei SHOW DATABASES) – einen Filter anhängen, um nur eine Auswahl an Tabellen zu erhalten, zum Beispiel:



   

Sandini Bib

mysql> SHOW TABLES LIKE '%instrument'; +-------------------------------------+ | Tables_in_musikschule (%instrument) | +-------------------------------------+ | instrument | | leihinstrument | +-------------------------------------+

Zum Abschluss – bevor wir den Befehl CREATE TABLE nochmals komplett vorstellen – eine Darstellung aller Tabellen und ihrer Verknüpfungen in einem ER-Diagramm in Abbildung 2.8. Wie man sieht, wird so etwas schnell unübersichtlich. Andererseits kann man durch geschicktes Anordnen erkennen, welches die zentralen Tabellen sind. Wenn man nämlich versucht, die Verknüpfungen möglichst kreuzungsfrei zu gestalten (ganz wird das meist nie gelingen), wandern die wichtigen und am häufigsten verknüpften Tabellen meist in die Mitte. Auch hier ist dies der Fall. Die Tabellen instrument und schueler sind mit vielen anderen Tabellen verbunden.

Abbildung 2.8: ER-Diagramm mit allen Tabellen

  



Sandini Bib

2.6.4 CREATE TABLE – komplett Zum Abschluss dieses Kapitels wollen wir den Befehl CREATE TABLE nochmals in seiner ganzen Fülle vorstellen. Die Syntaxbeschreibung ist recht umfangreich und daher in mehrere Teile unterteilt. Zunächst der grobe Rahmen:

CREATE TABLE

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [select_statement] tbl_name ist natürlich der Name der Tabelle, der einigen Einschränkungen unterliegt (siehe Abschnitt 2.5 über das Erstellen einer Datenbank). Die create_definition ist die Aufzählung der Spalten dieser Datenbank mit ihren Typen und Besonderheiten. Wir werden sie als Nächstes behandeln. table_options sind Optionen, die sich auf die gesamte Tabelle beziehen (wie zum Beispiel der Typ) und select_statement ist eine Abfrage (auf die wir im nächsten Kapitel zu sprechen kommen), deren Spalten und Daten mit in die neu zu erstellende Tabelle aufgenommen werden.

Nun zur create_definition. Sie ist recht komplex zu lesen, andererseits aber dann doch einfach: { col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [PRIMARY KEY] [reference_definition] | PRIMARY KEY (index_col_name,...) | KEY [index_name] (index_col_name,...) | INDEX [index_name] (index_col_name,...) | UNIQUE [INDEX] [index_name] (index_col_name,...) | FULLTEXT [INDEX] [index_name] (index_col_name,...) | [CONSTRAINT symbol] FOREIGN KEY index_name (index_col_name,...) [reference_definition] | CHECK (expr) }

Wie gesagt: keine Panik. Es sieht nur deshalb so komplex aus, weil es viele Möglichkeiten gibt, aus der man jeweils eine auswählen kann. Immer, wenn man am Zeilenende einen vertikalen Balken sieht, bedeutet dies, dass eine Variante zu Ende ist und die nächste beginnt. Dröseln wir das mal auf: 1. col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT]

[PRIMARY KEY] [reference_definition]

Dies ist die „normale“ Definition einer Spalte. Zunächst der Spaltenname col_name, dann der Typ type. Dieser ist wieder unter einer Reihe von Möglichkeiten auszuwählen: TINYINT[(length)] [UNSIGNED] [ZEROFILL] SMALLINT[(length)] [UNSIGNED] [ZEROFILL] MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL]



   

Sandini Bib

INT[(length)] [UNSIGNED] [ZEROFILL] INTEGER[(length)] [UNSIGNED] [ZEROFILL] BIGINT[(length)] [UNSIGNED] [ZEROFILL] REAL[(length,decimals)] [UNSIGNED] [ZEROFILL] DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL] FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL] DECIMAL(length,decimals) [UNSIGNED] [ZEROFILL] NUMERIC(length,decimals) [UNSIGNED] [ZEROFILL] [NATIONAL] CHAR[ACTER](length) [BINARY] | NCHAR(length) [BINARY] [NATIONAL] VARCHAR[ACTER](length) [BINARY] | NVARCHAR(length) [BINARY] DATE TIME TIMESTAMP DATETIME TINYBLOB BLOB MEDIUMBLOB LONGBLOB TINYTEXT TEXT MEDIUMTEXT LONGTEXT ENUM(value1,value2,value3,...) SET(value1,value2,value3,...)

Diese Liste ist uns schon von der Beschreibung der Datentypen geläufig. NOT NULL bzw. NULL (der Standard) legt fest, ob ein Feld leer bleiben darf oder nicht. DEFAULT default_value definiert, dass das Feld den Wert default_value erhalten soll, wenn es nicht befüllt wird. AUTO_INCREMENT kann für genau ein ganzzahliges Feld der Tabelle definiert werden (von Ausnahmen bei bestimmten Tabellenarten abgesehen) und sorgt dafür, dass dieses Feld automatisch einen bisher nicht genutzten Wert erhält, wenn kein eigener Wert mitgeliefert wird. PRIMARY KEY legt fest, dass dieses Feld Teil des Primärschlüssels ist (siehe Kapitel über Indizes weiter hinten). Die reference_definition dient dazu, Beziehungen dieses Feldes mit dem Inhalt anderer Tabellen herzustellen. Allerdings ist in MySQL nur die Syntax implementiert, ohne wirklich Funktionalität zu bieten und dient dazu, das Übertragen von SQL-Befehlen aus anderen Datenbanksystemen zu erleichtern. Nur der Vollständigkeit halber die Syntax der reference_definition: REFERENCES tbl_name [(index_col_name,...)] [MATCH FULL | MATCH PARTIAL] [ON DELETE reference_option] [ON UPDATE reference_option]

  



Sandini Bib

index_col_name ist dabei der Name einer Spalte in einem Index und reference_option ist eine der folgenden: RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT 2. PRIMARY KEY (index_col_name,...)

Wie weiter oben beschrieben, sollte jede Tabelle einen Primärschlüssel haben, der dafür sorgt, dass jeder Datensatz eindeutig identifizierbar ist. Dabei handelt es sich im Allgemeinen um ein einzelnes Feld, man kann aber auch mehrere Felder zu einem Primärschlüssel kombinieren. Bisher haben wir das Feld bei der Tabellendefinition direkt mit dem Attribut PRIMARY KEY versehen, aber man kann auch (möglichst am Ende der Definition) einen eigenen Abschnitt dafür nutzen. Statt also CREATE TABLE mytab (pfield INT UNSIGNED NOT NULL PRIMARY KEY, ...);

zu schreiben, kann man auch schreiben: CREATE TABLE mytab (pfield INT UNSIGNED NOT NULL, ..., PRIMARY KEY (pfield));

Dies bietet sich an, wenn man gleich noch weitere Indizes (siehe späteres Kapitel) definieren will und so alle Indizes an einer Stelle (am Ende der Definition) versammeln kann. Zudem ist dies die einzige Möglichkeit, mehr als ein Feld für den Primärschlüssel festzulegen. 3. KEY [index_name] (index_col_name,...)

Mit diesem Teil lässt sich direkt bei der Tabellendefinition ein Index festlegen (siehe späteres Kapitel). Dabei ist index_name der Name des Index und index_col_name,... eine Aufzählung der Spalten, die darin vorkommen. Lässt man den Namen weg, wird als Name der Name der ersten Indexspalte verwendet. Ist dieser Name schon in Verwendung, wird er um _2, _3, … erweitert. 4. INDEX [index_name] (index_col_name,...)

Dieser Teil entspricht exakt der Definition von KEY. 5. UNIQUE [INDEX] [index_name] (index_col_name,...)

Ein solchermaßen erzeugter Index muss immer eindeutig sein. Werden in der Tabelle Werte eingetragen, die dafür sorgen würden, dass der Index nicht mehr eindeutig wäre, wird die Änderung abgelehnt und eine Fehlermeldung ausgegeben. Ein solcher eindeutiger Index



   

Sandini Bib

unterscheidet sich von einem Primärschlüssel (per PRIMARY KEY angelegt) dadurch, dass hier durchaus auch NULL-Werte erlaubt sein können. index_name und index_col_name haben die gleiche Bedeutung wie bei KEY. 6. FULLTEXT [INDEX] [index_name] (index_col_name,...)

Mit diesem Konstrukt kann man dafür sorgen, dass eine Volltextsuche auf den angegebenen Spalten möglich ist. Wir werden darauf später im Abschnitt über Abfragen eingehen. Spalten, die abgefragt werden sollen, müssen in index_col_name,... auftauchen. 7. [CONSTRAINT

symbol] FOREIGN [reference_definition]

KEY

index_name

(index_col_name,...)

Mit diesem Teil der Syntax kann man Abhängigkeiten zu anderen Tabellen und Spalten beschreiben. Allerdings ist diese Beschreibung wirkungslos, da solche Regeln und Fremdschlüssel in MySQL nicht umgesetzt sind. Sie dienen lediglich zur leichteren Übernahme von Befehlen aus anderen Datenbanksystemen. 8. CHECK (expr)

Die CHECK-Syntax ist genauso wie CONSTRAINT und FOREIGN KEY nicht mit Funktionalität ausgestattet und dient nur der einfacheren Übernahme aus anderen Datenbanksystemen. Als Nächstes wollen wir zu den Tabellenoptionen table_options kommen. Sie bieten die folgenden Möglichkeiten: { TYPE = {BDB | HEAP | ISAM | InnoDB | MERGE | MRG_MYISAM | MYISAM} | AUTO_INCREMENT = number | AVG_ROW_LENGTH = number | CHECKSUM = {0 | 1} | COMMENT = "string" | MAX_ROWS = number | MIN_ROWS = number | PACK_KEYS = {0 | 1} | PASSWORD = "string" | DELAY_KEY_WRITE = {0 | 1} | ROW_FORMAT = {default | dynamic | fixed | compressed} | RAID_TYPE = {1 | STRIPED | RAID0} RAID_CHUNKS = num_chunks RAID_CHUNKSIZE = num_size | UNION = (table_name,...) | INSERT_METHOD = {NO | FIRST | LAST} | DATA DIRECTORY = "directory" | INDEX DIRECTORY = "directory" }

  



Sandini Bib

Auch hier eine kurze Beschreibung der Optionen: 1. TYPE = {BDB | HEAP | ISAM | InnoDB | MERGE | MRG_MYISAM | MYISAM}

Mit diesem Attribut kann man den Typ der Tabelle festlegen. MySQL unterstützt verschiedene Tabellenarten, die in den unterschiedlichen Situationen sinnvoll sind und bestimmte Features unterstützen. Zudem sind abhängig von der Distribution, dem Betriebssystem und den eventuell angepassten Kompilierungsoptionen bestimmte Tabellentypen nicht verfügbar. In solch einem Fall wird der „nächstmögliche“ Typ verwendet. Standard ist MYISAM. 2. AUTO_INCREMENT = number

der Wert, der bei Verwendung von AUTO_INCRMENT für eine Tabellenspalte als Nächstes genutzt werden soll (nur für Tabellen des Typs MyISAM). 3. AVG_ROW_LENGTH = number

eine Vorabschätzung, wie lang die einzelnen Tabellenzeilen ungefähr sein werden. Diese Angabe macht normalerweise nur Sinn, wenn es sich um eine sehr große Tabelle mit vielen Feldern variabler Länge handelt. 4. CHECKSUM = {0 | 1}

Wird dieser Parameter auf 1 gesetzt, wird für jede Zeile der Tabelle eine Prüfsumme mit abgespeichert. Dadurch werden Datenänderungen zwar etwas langsamer, kaputte Tabellen lassen sich aber leichter auffinden (nur für Tabellen des Typs MyISAM). 5. COMMENT = "string"

Angabe eines Kommentars zur Tabelle, der maximal 60 Zeichen lang sein kann. 6. MAX_ROWS = number

Angabe der geplanten maximalen Anzahl an Datensätzen für diese Tabelle. Das Produkt aus MAX_ROWS und AVG_ROW_LENGTH wird genutzt, um zum Beispiel die Größe der Zeiger in den Indexdateien festzulegen. 7. MIN_ROWS = number

Angabe der geplanten minimalen Anzahl an Datensätzen für diese Tabelle. 8. PACK_KEYS = {0 | 1}

ermöglicht das Verwalten der Indizes auch bei ganzzahligen Feldern in gepackter Form (PACK_KEYS = 1). Standard ist, dass nur Zeichenket-



   

Sandini Bib

ten gepackt abgelegt werden. Dadurch werden Aktualisierungen der Daten etwas verlangsamt, das Suchen von Werten aber beschleunigt und Platz gespart (nur für Tabellen des Typs MyISAM und ISAM). 9. PASSWORD = "string"

Verschlüsseln der Tabellendatei .frm mit einem Kennwort. Allerdings passiert bei der Standardversion von MySQL nichts. 10. DELAY_KEY_WRITE = {0 | 1}

Wird diese Option aktiviert (DELAY_KEY_WRITE = 1), werden die Schlüssel einer Tabelle nach dem Einfügen oder Ändern von Daten nicht sofort auf die Festplatte geschrieben, sondern erst, wenn die Tabellendatei geschlossen wird. Vorteil ist, dass deutlich weniger Schreibvorgänge für die Schlüsselfelder notwendig sind, wodurch sich die Geschwindigkeit steigern kann. Diese Option ist allerdings nur dann wirksam, wenn für den Datenbank-Server mysqld die Option delay_key_write auf ON gesetzt und die Tabelle vom Typ MyISAM ist. Nutzt man diese Tabellenoption, sollte vor dem Starten des MySQLServers immer das Tool myisamchk ausgeführt werden, um sicherzustellen, dass keine Inkonsistenzen entstanden sind, falls der Server einmal „unsanft“ beendet wurde. Allerdings ist nicht zu befürchten, dass durch die Nutzung dieser Option Daten verloren gehen, da sich die Index-Daten (um die es hier geht) immer aus den Originaldaten wiederherstellen lassen. 11. ROW_FORMAT = {default | dynamic | fixed | compressed}

Mit diesem Parameter lässt sich angeben, wie eine Tabelle gespeichert werden soll. Bisher geht dies nur für Tabellen des Typs MyISAM und zudem nur für die Optionen dynamic und fixed. 12. RAID_TYPE = {1 | STRIPED | RAID0} RAID_CHUNKS = num_chunks RAID_CHUNKSIZE

= num_size

Mit dieser Option kann man die maximale Dateigröße von MyISAMTabellen umgehen, die vom Betriebssystem vorgegeben ist. Gibt man einen RAID-Typ an (1, STRIPED und RAID0 sind Synonyme), werden num_chunks Unterverzeichnisse für die Tabelle mit den Namen 00, 01, 02, … angelegt, dort wiederum werden Tabellendateien angelegt und die Tabellendaten in Häppchen der Größe 1024·num_size auf die Dateien verteilt. Vorraussetzung dafür ist, dass der MySQL-Server mit der Option --with-raid gestartet wurde. Zudem bleibt die Indexdatei als eine einzelne Datei bestehen.

  



Sandini Bib

13. UNION = (table_name,...)

Diese Option ist nur für Tabellen des Typs MERGE nötig. Sie gibt an, welche Tabellen zur neuen Tabelle verbunden werden sollen. 14. INSERT_METHOD = {NO | FIRST | LAST}

Auch diese Option wird nur für Tabellen des Typs MERGE benötigt und legt fest, in welche der Tabellen, die die Merge-Tabelle darstellen, Daten eingefügt werden sollen. Wird dieser Parameter nicht mit angegeben oder auf NO gesetzt, kann man keine Daten einfügen. 15. DATA DIRECTORY = "directory"

Dieser Parameter legt fest, in welchem Verzeichnis die Tabellendatei abgelegt werden soll. Normalerweise landet sie im Verzeichnis der gewählten Datenbank. Hier ist ein absoluter Pfad anzugeben. 16. INDEX DIRECTORY = "directory"

Mit diesem Wert kann man angeben, wo die Indexdatei der Tabelle zu liegen kommen soll. Auch hier ist ein absoluter Pfad notwendig. Hiermit ist das erste „richtige“ Kapitel abgeschlossen. Wir haben ein Modell für unser Szenario entwickelt, uns mit einem MySQL-Server verbunden, eine Datenbank und Tabellen angelegt und die Beziehungen zwischen den Tabellen in einem ER-Diagramm dargestellt. Im nächsten Kapitel geht es nun um die Hauptakteure – die Daten selber. Wir werden lernen, wie man Daten einfügt, abfragt, aktualisiert und auch wieder löscht.

2.6.5 Exkurs: Normalisierung Wir haben beim Erstellen der Tabellen auf verschiedene Dinge geachtet, die mit Normalisierung zusammenhängen. Normalisieren bedeutet, die Tabellen so zu entwerfen, dass gewisse Regeln eingehalten werden, die Redundanzen vermeiden und den Verlust von Daten verhindern sollen. Gehen wir einmal davon aus, dass wir die Informationen für eine Unterrichtsgruppe in einer Form festhalten würden, wie sie vielleicht auf einer Karteikarte Sinn machen würde: Instrument

Lehrer

Termin

Raum

Max. Belegung

Schüler

Querflöte

G. Mosler, Lange Reihe 11, 20123 Hamburg

Dienstag, 17:0017:45

A5.35

6

T. Schmidt, R. Herms, A. Demmig

Es gibt hier verschiedene Punkte, die nachteilig sind, und an Hand derer wir die Normalisierung in ihren verschiedenen Stufen aufzeigen wollen.



  

Sandini Bib

1. Normalform Bei der ersten Normalform wird dafür gesorgt, dass in jedem Feld nur einfache („atomare“) Werte stehen. Dies ist bei unserem Beispiel an zwei Stellen definitiv verletzt:

1. Normalform

1. In der Spalte Lehrer steht neben dem Namen des Lehrers auch dessen

Adresse. Diese sollte in einzelnen Feldern gespeichert sein. (Wobei es diskussionswürdig bleibt, ob zum Beispiel die Hausnummer auch ein einzelnes Feld braucht. Dies hängt sicherlich neben dem Eifer, mit dem man „sauber“ entwerfen will, auch mit dem Einsatzzweck zusammen: Möchte man automatisch Postleitzahlen ermitteln, ist ein eigenes Feld für die Hausnummer ausgesprochen hilfreich.) 2. Die Schüler stehen als Liste in einem Feld. Das hat neben den unbe-

quemen Bearbeitungsmöglichkeiten noch den unangenehmen Effekt, dass durch die begrenzte Feldlänge die Anzahl der Einträge immer eingeschränkt ist. Auch eine Aufteilung in Schüler1, Schüler2, Schüler3 usw. hilft nicht wirklich weiter, da man immer nur eine feste Zahl an Feldern angeben kann. Was aber, wenn es dann doch einmal mehr Teilnehmer werden sollen? Daher erzeugt man eine neue Tabelle (wie wir es auch in unserem Modell mit der Tabelle unterricht_tn gemacht haben) und hat nun die Möglichkeit, so viele Schüler für einen Kurs einzutragen, wie tatsächlich vorhanden sind. Als Verweis auf die eigentliche Unterrichtstabelle benötigt man eine eindeutige Identifizierung, zum Beispiel aus Instrument, Raum und Termin. Zunächst die Tabelle Unterricht: Instrument

Lehrer

L_Strasse

L_PLZ

L_Ort

Raum

Max_ Bel

Termin

Querflöte

G. Mosler

Lange Reihe 11

2012 3

Hamburg

A5.35

6

Dienstag, 17:00-17:45

Nun die davon „abhängige“ Tabelle Unterricht_Teilnehmer: Instrument

Raum

Termin

Schüler

Querflöte

A5.35

Dienstag, 17:00-17:45

T. Schmidt

Querflöte

A5.35

Dienstag, 17:00-17:45

R. Herms

Querflöte

A5.35

Dienstag, 17:00-17:45

A. Demmig

2. Normalform Die zweite Normalform besagt, dass alle Attribute einer Tabelle nur komplett vom Primärschlüssel abhängen, und nicht nur von Teilen davon. Dies gilt hier eben nicht für die maximale Belegung des Raumes. Sie ist nur vom Raum selber abhängig, nicht von den beiden anderen

  

2. Normalform



Sandini Bib

Elementen des Primärschlüssels, nämlich Instrument und Termin. Es hat zwei Vorteile, eine Tabelle in die zweite Normalform zu bringen: 1. Ändert sich der Wert des abhängigen Attributs, muss er in allen Da-

tensätzen nachgezogen werden. In unserem Beispiel: Passen in den Raum mehr Personen hinein, muss das Feld Max_Bel in allen Datensätzen geändert werden, die den Raum beinhalten. Bei einer eigenen Tabelle für den Raum muss der Wert nur an dieser Stelle einmal geändert werden. 2. Werden alle Datensätze mit dem entsprechenden Attribut entfernt

(weil in unserem Beispiel in einem Halbjahr kein Unterricht in einem Raum stattfindet), verschwinden auch die Informationen aus den abhängigen Feldern. Es ist nirgendwo mehr vermerkt, dass der Raum A5.35 für sechs Personen ausgelegt ist. Auch dies wird durch eine eigene Tabelle vermieden. Wir bringen also unser Beispiel in die zweite Normalform: Instrument

Lehrer

L_Strasse

L_PLZ

L_Ort

Raum

Termin

Querflöte

G. Mosler

Lange Reihe 11

20123

Hamburg

A5.35

Dienstag, 17:00-17:45

Die davon „abhängige“ Tabelle Unterricht_Teilnehmer: Instrument

Raum

Termin

Schüler

Querflöte

A5.35

Dienstag, 17:00-17:45

T. Schmidt

Querflöte

A5.35

Dienstag, 17:00-17:45

R. Herms

Querflöte

A5.35

Dienstag, 17:00-17:45

A. Demmig

Dazu jetzt die neue Tabelle für die Raumdaten: Raum

Max_Belegung

A5.35

6

3. Normalform In der dritten Normalform gibt es auch keine Abhängigkeiten mehr zwischen Attributen, die keine Schlüsselfelder sind. Hier gelten im Prinzip die gleichen Vorteile wie bei der zweiten Normalform. In unserem Beispiel stehen noch die Felder L_Strasse, L_PLZ und L_Ort der dritten Normalform im Weg. Diese müssten auch in eine eigene Tabelle Lehrer ausgelagert werden:

3. Normalform



Instrument

Lehrer

Raum

Termin

Querflöte

G. Mosler

A5.35

Dienstag, 17:00-17:45

   

Sandini Bib

Die davon „abhängige“ Tabelle Unterricht_Teilnehmer: Instrument

Raum

Termin

Schüler

Querflöte

A5.35

Dienstag, 17:00-17:45

T. Schmidt

Querflöte

A5.35

Dienstag, 17:00-17:45

R. Herms

Querflöte

A5.35

Dienstag, 17:00-17:45

A. Demmig

Die Tabelle für die Raumdaten: Raum

Max_Belegung

A5.35

6

Nun die Tabelle Lehrer: Lehrer

L_Strasse

L_PLZ

L_Ort

G. Mosler

Lange Reihe 11

20123

Hamburg

In den meisten Fällen reicht die dritte Normalform für den praktischen Gebrauch. Es gibt noch drei weitere Normalformen, die Boyce-CoddNormalform (BCNF, eine etwas schärfere Version der dritten Normalform, bei der auch die Schlüssel untereinander nicht abhängig sein dürfen) sowie die vierte und fünfte Normalform. Diese sind aber eher von akademischer Natur und werden normalerweise nicht in der Praxis verwendet. Auf zwei Punkte möchte ich hier noch kurz eingehen: • Primärschlüssel sollten möglichst „sinnfrei“ sein, wie schon oben geschildert. Hier ist ein gutes Beispiel dafür, warum dies empfohlen wird. Ändert sich nämlich zum Beispiel der Raum oder der Termin, müssen auch wieder alle Werte in den abhängigen Tabellen nachgezogen werden (eine sogenannte Änderungsanomalie). • Das Normalisieren von Datenstrukturen geht fast immer auch mit einer Verschlechterung der Performance einher. Will man zum Beispiel die in die dritte Normalform gebrachten Daten wieder zusammen ausgeben (weil sie dann schließlich am lesbarsten sind), benötigt man eine Verknüpfung über vier Tabellen. Solche Verknüpfungen sind – gerade bei vielen beteiligten Tabellen – sehr speicher- und leistungsfressend. Daher kann es in bestimmten Fällen durchaus Sinn machen, Daten redundant abzulegen, wenn sie selten geändert werden. Das muss aber gut durchdacht und sauber umgesetzt werden (um Inkonsistenzen im Datenbestand zu vermeiden). Man könnte zum Beispiel die maximale Belegungszahl der Räume mit in der Unterrichtstabelle ablegen, um schnellen Zugriff darauf zu

  



Sandini Bib

gewährleisten (wenn beispielsweise neue Teilnehmer hinzugefügt werden sollen). Dann sollte aber weiterhin die Raum-Tabelle existieren. Erstens geht damit die Belegungszahl nicht verloren, wenn kein Unterricht in einem Raum stattfindet und zweitens kann man diese Tabelle als Quelle für das Eintragen des Werts in die Unterrichtstabelle nutzen, wenn ein Kurs neu angelegt wird. Man muss nur darauf achten, dass bei einer Änderung der Belegungszahl nicht nur die Raum-Tabelle aktualisiert wird, sondern auch die Unterrichtstabelle. Dies wird aber ausgesprochen selten vorkommen, sofern man nicht monatlich die Räume umbaut…

2.7

Fragen

1. Welche Bereiche gibt es innerhalb der Sprache SQL und wie unter-

scheiden sie sich? 2. Was für Vorteile hat es, ein normalisiertes Datenmodell zu nutzen? 3. Worin unterscheiden sich die String-Datentypen VARCHAR und VARCHAR

BINARY? 4. Wie kann man dafür sorgen, dass ein Datensatz eine eindeutige Ken-

nung erhält, ohne dass der Benutzer selber darauf achten muss? 5. Wie kann man sich mit der Datenbank musikschule auf einem MySQL-

Server verbinden, der auf dem Rechner truhe.tdemmig.local läuft? Der Benutzer soll demo sein, welcher das Kennwort geheim besitzt.



   

Sandini Bib

3

Daten

le

n e rn

Wofür war eine Datenbank noch mal gedacht? Richtig, sie dient dazu, Daten zu verwalten. Bisher haben wir allerdings noch gar keine Daten gesehen. Lediglich die dafür notwendigen Behältnisse – die Datenbanken und Tabellen – haben wir genauer kennen gelernt. Nun ist es an der Zeit, endlich das tatsächliche Datenhandling zu erlernen und umzusetzen.

3.1

Was Sie in diesem Kapitel lernen

Um mit den Daten umzugehen, müssen diese zunächst einmal in die Datenbank wandern. Dazu lernen wir den Befehl INSERT kennen. Um die Daten dann auch wieder auslesen zu können, verwenden wir den Befehl SELECT. Änderungen an den Daten müssen natürlich ebenso möglich sein, wie das Löschen von Informationen. Dazu dienen die Befehle UPDATE und DELETE. Wir werden zunächst Daten einfügen, abfragen, aktualisieren und löschen – ohne großen Firlefanz und ohne alle möglichen Optionen auszureizen. Danach werden wir uns detailliert mit den einzelnen Befehlen INSERT, SELECT, UPDATE und DELETE befassen. Beim Abfragen der Daten werden wir neben den nutzbaren Funktionen auch die Abfrage über mehrere Tabellen (eine Grundlage relationaler Datenbanken) behandeln – die sogenannten „Joins“.

:DV 6LH LQ GLHVHP .DSLWHO OHUQHQ



Sandini Bib

3.2

Eintragen, Abfragen, Ändern, Löschen – die Basis

Um Daten in die bisher leeren Tabellen einzutragen, nutzt man den Befehl INSERT. In seiner einfachsten Form sieht er wie folgt aus:

INSERT

INSERT INTO raum VALUES (0, 'A5.35', '5', 4);

Damit trägt man in die Tabelle raum für die Raum-Kennung 0, für den Namen „A5.35“, für die Etage 5 und für die Personenanzahl 4 ein. Diese Version setzt aber voraus, dass man die Reihenfolge der Spalten kennt. Zudem kann man keine Felder auslassen, die eventuell automatisch gefüllt werden sollen. Besser ist da schon, die Spalten mit anzugeben, die gefüllt werden sollen: INSERT INTO raum (raum_name, etage, personen) VALUES ('A5.33', '5', 4);

Damit ist man auch dann auf der sicheren Seite, wenn sich die Tabellendefinition einmal ändern sollte (zumindest soweit, wie die entsprechenden Felder dann noch vorhanden sind …). Was passiert nun mit den Feldern, die man nicht mit angibt? In diesem Fall gibt es nur das Feld raum_id, welches fehlt. Für solche Fälle haben wir in der Tabellendefinition das Attribut DEFAULT, dessen Wert dann genutzt wird. Hier handelt es sich allerdings um einen Primärschlüssel mit dem Attribut AUTO_INCREMENT. In diesem Fall wird dann der Mechanismus zum Generieren einer neuen Zahl gestartet. Man sieht noch eine weitere Regel: Zeichenketten müssen immer in Anführungszeichen gesetzt werden (ob einfach oder doppelt ist egal, sie müssen nur am Anfang und Ende des Strings gleich sein), Zahlen hingegen nicht. Wie sieht nun also das Ergebnis aus, wenn der obige Befehl gestartet wird? Um sich den Inhalt einer Tabelle ausgeben zu lassen, nutzt man den Befehl SELECT, hier in seiner einfachsten Form (später mehr):

SELECT

mysql> SELECT * FROM raum; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | +---------+-----------+-------+----------+

Wie wir sehen, wird die Raum-Kennung raum_id automatisch erstellt. Da es sich um den ersten Datensatz handelt, der in diese Tabelle eingetragen wird, erhält er den Wert 1 (hätten wir bei der Tabellendefinition einen anderen Wert als Startwert angegeben, würde dieser jetzt hier auf-

NULL



'DWHQ

Sandini Bib

tauchen). Der nächste Satz bekommt dann die 2 und so weiter. Dieser Mechanismus wird nur dann aktiv, wenn für raum_id NULL oder 0 angegeben wird. Dabei sieht man aus dem obigen Beispiel, dass ein Feld den Wert NULL erhält, wenn es bei INSERT nicht mit aufgeführt wird. Man kann auch explizit ein Feld mit dem Wert NULL versehen: INSERT INTO raum (raum_name, etage, personen) VALUES ('B2.02', NULL, 20);

Wie man sieht, gibt man einfach den Begriff „NULL“ an, ohne Anführungszeichen oder Ähnliches. Schaut man sich nun den Inhalt der Tabelle raum erneut an, sieht dieser wie folgt aus: mysql> SELECT * FROM raum; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | NULL | 20 | +---------+-----------+-------+----------+

Auch in der Ausgabe findet sich hier die Bezeichnung „NULL“. Hier gilt es, etwas aufzupassen, denn auch die Zeichenkette 'NULL' sieht genauso aus: mysql> INSERT INTO raum (raum_name, etage, personen) VALUES ('NULL', NULL, 11); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM raum; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | NULL | 20 | | 3 | NULL | NULL | 11 | +---------+-----------+-------+----------+

Dies kann natürlich zu Verwirrung führen, aber im Gebrauch gibt es selten Probleme. Man erhält bei Abfrage der Datenbank über andere Schnittstellen wie PHP oder Perl durchaus unterscheidbare Resultate zwischen NULL und 'NULL'. Und zudem kann man bei einem SELECTBefehl filtern, ob man auch Datensätze erhalten will, die in einem bestimmten Feld den Wert NULL stehen haben.

      



Sandini Bib

Man kann auch mit einem INSERT-Befehl mehrere Datensätze auf einmal eintragen. Das folgende Beispiel zeigt, wie das geht: mysql> INSERT INTO raum (raum_name, etage, personen) VALUES -> ('A5.21', '5', 4), -> ('A5.23', '5', 4), -> ('CE.48', 'E', 6); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM raum; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | NULL | 20 | | 3 | NULL | NULL | 11 | | 4 | A5.21 | 5 | 4 | | 5 | A5.23 | 5 | 4 | | 6 | CE.48 | E | 6 | +---------+-----------+-------+----------+

Die Daten werden als sogenannte „Tupel“ eingetragen, deren Informationen in Klammern stehen und die durch Kommas getrennt sind. Man erhält bei der Eingabe gleich auch die Information, wie viele Datensätze erfasst wurden und ob es Probleme bei den Sätzen gab (wie zum Beispiel doppelte Datensätze). Jetzt haben wir in der Tabelle zwei Datensätze, deren Informationen nicht vollständig sind (die Etage ist nicht angegeben) bzw. deren Raumbezeichnung nicht sinnvoll ist (aus dem Beispiel NULL vs. 'NULL'). Wie findet man solche Datensätze? Nun, in der aktuellen Raum-Tabelle sieht man die unschönen Daten noch auf einen Blick, schließlich ist die Tabelle nicht sehr groß. Aber bei einer wirklich großen Tabelle (zum Beispiel wird die Schülertabelle sicher umfangreich werden) ist dies nicht mehr so einfach. Wir wünschen uns nun also, nur bestimmte Datensätze auszugeben. Dazu kann man den SELECT-Befehl um eine WHERE-Klausel erweitern, in der Filterkriterien angegeben werden: mysql> SELECT * FROM raum WHERE raum_id = 2; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 2 | B2.02 | NULL | 20 | +---------+-----------+-------+----------+

Man gibt also eine Spalte an, die gefiltert werden soll, einen Operator (hier das Gleichheitszeichen) und einen Vergleichswert. Ein anderes Beispiel ist:



'DWHQ

Sandini Bib

mysql> SELECT * FROM raum WHERE raum_id < 4; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | NULL | 20 | | 3 | NULL | NULL | 11 | +---------+-----------+-------+----------+

Hier ist der Vergleichsoperator das Kleiner-Zeichen ( (größer), >= (größer oder gleich), SELECT * FROM raum WHERE raum_id < 4 AND personen SELECT * FROM raum WHERE etage IS NULL; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 2 | B2.02 | NULL | 20 | | 3 | NULL | NULL | 11 | +---------+-----------+-------+----------+

      



Sandini Bib

Analog dazu kann man sich mit IS NOT NULL die Sätze ausgeben lassen, bei denen ein Feld nicht NULL ist:

IS NOT NULL

mysql> SELECT * FROM raum WHERE etage IS NOT NULL; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 4 | A5.21 | 5 | 4 | | 5 | A5.23 | 5 | 4 | | 6 | CE.48 | E | 6 | +---------+-----------+-------+----------+

Eine besondere Eigenschaft von NULL ist, dass Vergleiche mit ihm immer den Wert NULL ergeben. Dazu muss man beachten, dass unter SQL die übliche Ergebnismenge eines Vergleichs (a > b, c = d, usw.) nicht nur aus TRUE und FALSE besteht, sondern noch einen dritten Wert, eben NULL, besitzt. Ist nun einer der Vergleichswerte NULL, lautet auch das Ergebnis NULL. Man kann dies sehr schön sehen, indem man die folgenden Zeilen in mysql eingibt: mysql> SELECT 2 = 0; +-------+ | 2 = 0 | +-------+ | 0 | +-------+ mysql> SELECT 2 = 2; +-------+ | 2 = 2 | +-------+ | 1 | +-------+ mysql> SELECT 2 = NULL; +----------+ | 2 = NULL | +----------+ | NULL | +----------+

Der erste Vergleich liefert eine 0 zurück, dem SQL-Äquivalent für FALSE. Der zweite Vergleich ergibt 1, was TRUE entspricht. Der dritte Vergleich schließlich ergibt NULL, obwohl man im ersten Moment geneigt ist, auf FALSE zu tippen (denn 2 ist ja nun wirklich ungleich NULL). Dies ist nun eine Besonderheit der dreiwertigen Logik.



'DWHQ

Sandini Bib

Nun haben wir also unsere beiden Datensätze ohne Etage gefunden. Aber wie aktualisiert man diese? Dazu dient der Befehl UPDATE. Um zum Beispiel den ersten der beiden Sätze zu ändern, gibt man folgendes Kommando ein:

UPDATE

UPDATE raum SET etage = '2' WHERE raum_id = 2;

Dies bedeutet, dass man in der Tabelle raum die Etage aller Datensätze auf '2' setzen möchte, deren raum_id den Wert 2 hat. Dies kann in diesem Fall natürlich nur ein Datensatz sein, da die Spalte raum_id eindeutig ist. Es ist aber auch kein Problem, mehrere Datensätze gleichzeitig damit zu aktualisieren, wenn die WHERE-Bedingung auf mehrere Zeilen gleichzeitig zutrifft. Man kann sogar alle Datensätze einer Tabelle aktualisieren, wenn man die WHERE-Klausel ganz weglässt, allerdings ist dies nur in manchen besonderen Situationen sinnvoll. Umgekehrt kann man aber auch mehrere Felder gleichzeitig verändern, was wir für den zweiten betroffenen Datensatz vornehmen wollen: UPDATE raum SET raum_name = 'C4.16', etage = '4' WHERE raum_id = 3;

Als Ergebnis erhält man übrigens wieder eine etwas detailliertere Auskunft über die Anzahl der geänderten Datensätze: Rows matched: 1 Changed: 1 Warnings: 0

Damit kann man kontrollieren, ob eventuell zu viele oder gar kein Datensatz geändert wurde. Wenn nämlich die WHERE-Bedingung auf keinen Datensatz zutrifft, gibt es keine Fehlermeldung, da es sich dabei um keinen wirklichen Fehler handelt: mysql> UPDATE raum SET etage = '1' WHERE raum_id = 14; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0

Hintergrund dieses etwas seltsam anmutenden Verhaltens ist, dass in SQL die Datensätze als Mengen angesehen werden. Aus diesen Mengen wird mittels der WHERE-Klausel eine Teilmenge herausgeschnitten, die durchaus leer sein kann. Die Verarbeitung als Mengen ist auch der Grund dafür, dass es keine vorgegebene Ausgabereihenfolge gibt, sofern man nicht explizit (mittels ORDER BY) eine bestimmt. Dass bei ähnlichen Abfragen meist die gleiche Reihenfolge erscheint, liegt an der Implementierung im Datenbank-Server, ist aber unter SQL nicht garantiert! Bei den genutzten Mengen ist allerdings zu beachten, dass Elemente mehrfach vorkommen können (was sich durch geeignete Abfragen auch wieder ändern lässt …).

      



Sandini Bib

Als Ergebnis dieser Aktualisierungen sieht unsere Raumtabelle deutlich „gepflegter“ aus: mysql> SELECT * FROM raum; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | | 4 | A5.21 | 5 | 4 | | 5 | A5.23 | 5 | 4 | | 6 | CE.48 | E | 6 | +---------+-----------+-------+----------+

Nachdem wir nun Daten eingetragen, abgefragt und aktualisiert haben, wollen wir auch noch welche löschen. Der Raum CE.48 im Erdgeschoss wird leider anderweitig gebraucht und steht nicht mehr als Übungsraum zur Verfügung. Daher wollen wir ihn aus der Tabelle raum löschen. Der zugehörige Befehl ist wieder sehr einfach und „sprechend“:

DELETE

DELETE FROM raum WHERE raum_id = 6;

Hier ist wohl keine große Erklärung notwendig. Es sei nur darauf hingewiesen, dass man bei WHERE durchaus nicht immer auf den Primärschlüssel filtern muss. Aber wenn man ihn schon kennt, ist es auch besser, ihn zu nutzen, da man dann eindeutig weiß, was man bearbeitet oder löscht. In anderen Fällen kann es durchaus der Fall sein, dass der gewünschte Eintrag mehrfach existiert und man dadurch unabsichtlich auch mehr als einen Datensatz löscht. Nachdem wir nun die vier wichtigsten SQL-Befehle (INSERT, SELECT, UPDATE und DELETE) kennen gelernt haben, wollen wir uns nun intensiver mit jedem einzelnen von ihnen beschäftigen. Dies wird in den nächsten Abschnitten geschehen.

3.3

Daten eintragen

Widmen wir uns als Erstes ausführlicher dem Einfügen von Daten. Der zugehörige Befehl lautet – wie wir jetzt schon wissen – INSERT. Dessen Syntax ist nicht wirklich schwierig, allerdings gibt es drei sehr verschiedene Möglichkeiten. Alle drei beginnen mit dem folgenden Teil:

INSERT

INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name ... tbl_name ist natürlich der Name der Tabelle, in die die Daten eingetragen

werden sollen. Es können immer nur Daten in einer einzelnen Tabelle gespeichert werden, nicht in mehreren gleichzeitig (zumindest nicht



'DWHQ

Sandini Bib

mit einem einzelnen Befehl). Die einzelnen optionalen Attribute haben folgende Bedeutung: • LOW_PRIORITY: Ruft man INSERT mit diesem Attribut auf, wird so lange mit dem Einfügen von Daten gewartet, bis niemand anderes mehr Daten aus der entsprechenden Tabelle liest. Das kann natürlich einige Zeit dauern. • DELAYED: Mit diesem Parameter geschieht eher das Gegenteil des vorher Beschriebenen. Es wird zwar auch gewartet, bis niemand sonst mehr auf die Tabelle zugreift, aber der Client, welcher den Befehl aufgerufen hat, kann sofort weiterarbeiten und muss nicht auf die Ausführung des Befehls warten. Die Daten werden derweil im Arbeitsspeicher des Datenbank-Servers gesammelt und (wenn sie von mehreren Clients kommen) gesammelt abgespeichert. Dies beschleunigt den ganzen Vorgang, aber nur dann, wenn tatsächlich stark auf die Tabelle zugegriffen wird. Bei einer „normal“ belasteten Tabelle ist DELAYED aufgrund des zusätzlichen Aufwands eher etwas langsamer. • IGNORE: Fügt man einen Datensatz mit einem Primärschlüssel oder anderen eindeutigen Spalten in eine Tabelle ein, der schon existiert, gibt es normalerweise eine Fehlermeldung. Diese kann vermieden werden, indem man den Parameter IGNORE nutzt. Solche Sätze werden dann zwar immer noch nicht eingetragen (dass würde schließlich gegen die Eindeutigkeit dieser Spalte verstoßen), aber immerhin bricht die Verarbeitung dann nicht mit einem Fehler ab – die entsprechenden Sätze werden einfach ignoriert. Dies kann besonders dann hilfreich sein, wenn man viele Sätze auf einmal einträgt. • INTO: Das Weglassen dieses Wortes ist einfach nur eine Vereinfachung der üblichen Syntax INSERT INTO... Im Weiteren unterscheiden sich die Befehlsvarianten nun doch deutlich. Die bekannteste und vermutlich am häufigsten genutzte Variante lautet wie folgt: INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name [(col_name,...)] VALUES (expression,...),(...),...

Hiermit lassen sich ein oder mehrere Datensätze einfügen. col_name,... ist eine Liste von Spalten, für die man im Folgenden Werte angeben möchte. expression,... ist dann die Liste der Werte für einen Datensatz in der Spaltenreihenfolge, die vorher angegeben wurde. Fehlt die Angabe der betroffenen Spalten, muss man für alle Spalten der Tabelle einen Wert angeben. Die Reihenfolge muss zudem auch bekannt sein (und kann notfalls vorher mit DESCRIBE ermittelt werden). Empfehlenswerter ist allerdings, immer die Liste der Spalten mit anzugeben, um vor einer eventuellen Änderung der Spaltendefinitionen einigermaßen gefeit zu sein. Beispiele für diese Syntax sind weiter oben bereits aufgeführt.

  



Sandini Bib

Eine zweite, ähnliche Variante sieht so aus: INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name SET col_name = expression, col_name = expression, ...

Diese Syntax lehnt sich stärker an die des Befehls UPDATE an, ist allerdings im SQL-Umfeld recht ungewöhnlich. Die Nähe zur UPDATE-Syntax ist in manchen Situationen natürlich durchaus hilfreich. Ein Beispiel für diese Syntax wäre: INSERT INTO raum SET raum_name = 'EE.01', etage = 'E', personen = 8;

Die letzte der drei Varianten ist sehr hilfreich, wenn man zum Beispiel Daten aus einer Tabelle in eine andere übertragen will: INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name [(col_name,...)] SELECT ...

Man nimmt als Eingabewerte also die Ausgabe einer Abfrage mit SELECT. Dadurch lassen sich beispielsweise alte Werte aus einer Tabelle in eine Archiv-Tabelle kopieren, um sie danach aus der Haupttabelle entfernen und diese damit schneller machen zu können. Dies ist für die Raumtabelle aufgrund ihrer Größe natürlich nicht sehr sinnvoll, daher hier ein Beispiel mit Tabellen, die nicht zur Musikschule gehören: INSERT IGNORE INTO bestellung_archiv (bestell_id, bestell_datum, kunden_id, liefer_datum, gesamt_preis, bezahlt) SELECT bestell_id, bestell_datum, kunden_id, liefer_datum, gesamt_preis, bezahlt FROM bestellung WHERE YEAR(bestell_datum) < YEAR(NOW()) - 1 AND bezahlt = 1;

Hier werden alle Bestellungen (aus der Tabelle bestellung) in eine Archivierungstabelle bestellung_archiv kopiert, die aus dem vorletzten Kalenderjahr oder davor sind (zu Funktionen in der WHERE-Klausel kommen wir später) und komplett bezahlt wurden (bezahlt = 1). IGNORE wurde mit aufgenommen, um Fehler bei einer eventuellen wiederholten Ausführung zu unterbinden. Nachdem dieser Befehl erfolgreich ausgeführt wurde, können diese Daten aus der Tabelle bestellung gelöscht werden. Das obige Beispiel sieht recht komplex aus. Das liegt aber nur daran, dass auf eine sichere Ausführung geachtet wurde (IGNORE, Spaltenliste) und die Formatierung wenigstens ein wenig Übersichtlichkeit verschaffen soll. Wenn man nur mal eben eine Kopie einer Tabelle benötigt (sei



'DWHQ

Sandini Bib

es, um eine Sicherung zu haben, falls man bei den kommenden Aktionen Unfug anstellt, oder um den Zustand zu einem bestimmten Zeitpunkt für einen späteren Vergleich zu haben), kann solch ein Befehl auch so aussehen: INSERT INTO backuptab SELECT * FROM tab;

Soviel zum Einfügen von Daten. Wie Sie sehen, ist dies nicht sonderlich schwierig. Damit wollen wir zum nächsten Befehl kommen: SELECT. Dabei dürfte es sich um den am häufigsten verwendeten SQL-Befehl handeln. Da er zudem auch sehr viele Möglichkeiten bietet, wird der folgende Abschnitt recht lang werden. Aber keine Bange, auch da kommt man durch und es ist eigentlich alles logisch und sinnvoll.

3.4

Daten abfragen

Wir haben weiter oben in Kapitel 3.1 den Befehl SELECT schon kennen gelernt. Er bietet im Prinzip die einzige Möglichkeit, auf Daten in der Datenbank zuzugreifen. Allerdings ist er auch so universell, dass sich fast jede Information über ihn direkt ermitteln lässt. Die Syntax lautet allgemein wie folgt:

SELECT

SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [HIGH_PRIORITY] [DISTINCT | DISTINCTROW | ALL] select_expression,... [INTO {OUTFILE | DUMPFILE} 'file_name' export_options] [FROM table_references [WHERE where_definition] [GROUP BY {unsigned_integer | col_name | formula} [ASC | DESC], ...] [HAVING where_definition] [ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] ,...] [LIMIT [offset,] rows] [PROCEDURE procedure_name] [FOR UPDATE | LOCK IN SHARE MODE]]

Man sieht, dass es tatsächlich eine ganze Menge Elemente in diesem Befehl gibt, die wir im Folgenden nach und nach aufdröseln wollen. Das Element, welches jeder SELECT-Befehl braucht, ist die select_expression, die im nächsten Abschnitt behandelt wird. In ihr findet sich die Angabe, welche Felder ausgegeben werden sollen. Danach folgt mit FROM (in Kapitel 3.4.2) die Angabe, aus welcher Tabelle (bzw. aus welchen Tabellen) die Daten gelesen werden sollen. Schließlich kann man mit WHERE (in Kapitel 3.4.3) festlegen, ob die Tabellen in geeigneter Weise gefiltert werden sollen. Um die Ausgabe wie gewünscht zu sortieren, verwendet man ORDER BY (in Kapitel 3.4.4). Even-

   



Sandini Bib

tuell möchte man mehrere Datensätze zu einem zusammenfassen. Dazu eignet sich die GROUP BY-Klausel (in Kapitel 3.4.5), zu der noch zum Filtern auf solchermaßen zusammengefasste Daten HAVING gehört (in Kapitel 3.4.6). Möchte man schließlich nur einen Ausschnitt der Datensätze zu Gesicht bekommen, kann LIMIT (in Kapitel 3.4.6) manchmal sehr hilfreich sein.

3.4.1 Select-Ausdruck Zunächst geht es also um die gewünschten Felder. Man gibt sie einfach in der gewünschten Reihenfolge an, durch Komma getrennt: SELECT raum_id, raum_name ...

Interessiert man sich für alle Felder der Tabelle, kann man auch einfach ein * als Feldauswahl angeben: SELECT * ...

Es ist dabei durchaus möglich, Felder mehr als einmal anzugeben oder auch * und Feldnamen zu kombinieren: SELECT raum_name, * ...

Manchmal interessiert man sich nur dafür, welche Feldinhalte vorkommen, aber nicht, wie oft diese vorhanden sind. Möchte man zum Beispiel wissen, auf welchen Etagen Räume vorhanden sind, könnte man zunächst eingeben: SELECT etage FROM raum;

Das Ergebnis ist dann: +-------+ | etage | +-------+ | 5 | | 2 | | 4 | | 5 | | 5 | +-------+

Leider taucht die fünfte Etage mehrfach auf. Dies kann man vermeiden, indem man vor die Feldliste ein DISTINCT oder DISTINCTROW setzt. Dadurch erreicht man, dass alle Werte (oder Wertkombinationen bei mehreren Feldern) nur einmal aufgeführt werden. Mit DISTINCT sieht das Beispiel dann so aus, wie wir es uns wünschen:



'DWHQ

Sandini Bib

mysql> SELECT DISTINCT etage FROM raum; +-------+ | etage | +-------+ | 5 | | 2 | | 4 | +-------+

Das Gegenteil von DISTINCT bewirkt ALL, d.h. es werden alle Werte ausgegeben, auch wenn sie mehrfach vorkommen. Dies ist die „normale“ Vorgehensweise und ALL ist im Allgemeinen nicht notwendig. Es ist auch möglich, in der Feldliste Aliase zu vergeben, die dann als Spaltenüberschrift ausgegeben werden und die auch in den weiteren Elementen von SELECT genutzt werden können. Dazu gibt man nach dem eigentlichen Spaltennamen den gewünschten Alias an. Auf Wunsch kann man auch noch das Wort AS dazwischen schreiben. So lässt sich die Spalte raum_name in der Ausgabe (und den weiteren Elementen dieses Befehls) umbenennen in Raum: mysql> SELECT raum_name AS Raum, etage FROM raum; +-------+-------+ | Raum | etage | +-------+-------+ | A5.33 | 5 | | B2.02 | 2 | | C4.16 | 4 | | A5.21 | 5 | | A5.23 | 5 | +-------+-------+

Wenn man mit mehreren Tabellen in einer Abfrage arbeitet (siehe dazu Kapitel 3.8.2 über Joins), kann es vorkommen, dass Felder in mehreren Tabellen vorkommen. Dann ist es notwendig, festzulegen, welches Feld aus welcher Tabelle man eigentlich meint. Dies geschieht, indem man den Tabellennamen (oder dessen Alias, denn auch Tabellen können einen Aliasnamen haben, siehe Kapitel 3.4.2), gefolgt von einem Punkt vor den Spaltennamen setzt: SELECT raum.raum_id ...

Muss man auch noch die Datenbank mit angeben, kommt auch diese, durch einen Punkt getrennt, vor den Tabellennamen: SELECT musikschule.raum.raum_id ...

   



Sandini Bib

Man kann schon mit diesem ersten Element des SELECT-Befehls sinnvolle Aufgaben durchführen, ohne noch Tabellen anzugeben, die genutzt werden sollen. So lässt sich zum Beispiel SELECT als Taschenrechner nutzen: mysql> SELECT 1 + 2 * 3; +-----------+ | 1 + 2 * 3 | +-----------+ | 7 | +-----------+

Dieses Beispiel ist zwar nicht allzu sinnvoll (so schwer ist die Aufgabe dann doch nicht), zeigt aber, dass man auch Berechnungen mit den Feldern anstellen kann. Dies geht natürlich auch mit Tabellenfeldern: mysql> SELECT *, personen * 2 / 3 AS bequem, personen * 3 / 2 AS eng -> FROM raum; +---------+-----------+-------+----------+--------+-------+ | raum_id | raum_name | etage | personen | bequem | eng | +---------+-----------+-------+----------+--------+-------+ | 1 | A5.33 | 5 | 4 | 2.67 | 6.00 | | 2 | B2.02 | 2 | 20 | 13.33 | 30.00 | | 3 | C4.16 | 4 | 11 | 7.33 | 16.50 | | 4 | A5.21 | 5 | 4 | 2.67 | 6.00 | | 5 | A5.23 | 5 | 4 | 2.67 | 6.00 | +---------+-----------+-------+----------+--------+-------+

Dazu gibt es noch viele Funktionen, die man nutzen kann und die weiter unten in Kapitel 3.6.2 beschrieben sind. Einige davon machen auch (oder teilweise nur) dann Sinn, wenn man sie ohne Tabellenangabe aufruft: SELECT USER();

gibt den aktuellen Benutzer aus: +------------------+ | USER() | +------------------+ | demmig@localhost | +------------------+

Eine andere Funktion ist NOW(), die das aktuelle Datum und die Uhrzeit anzeigt: mysql> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2002-09-12 22:26:27 | +---------------------+



'DWHQ

Sandini Bib

3.4.2 FROM In der FROM-Klausel des SELECT-Befehls wird angegeben, aus welchen Tabellen die Daten ausgelesen und gefiltert werden sollen. Wir werden Abfragen über mehrere Tabellen erst weiter unten (in Kapitel 3.8.2) besprechen, daher soll hier auch nicht die komplette Syntax von table_references aufgeführt werden. Greift man nur auf eine Tabelle zu, gibt man sie hier an:

FROM

SELECT * FROM raum;

Genau wie bei den Spalten kann man auch bei Tabellen angeben, aus welcher Datenbank sie genommen werden sollen (falls man auf Tabellen anderer Datenbanken zugreifen möchte und darf): SELECT * FROM musikschule.raum;

Und auch wie bei den Spalten lassen sich Aliase für eine Tabelle definieren: SELECT * FROM raum AS zimmer;

Die Tabelle raum kann in diesem Befehl nun auch über den Alias zimmer angesprochen werden. Auch bei Tabellenaliasen ist AS optional. Bei solchen einfachen SELECT-Befehlen ist ein Tabellenalias nicht unbedingt sinnvoll oder nützlich, aber wenn es um Verknüpfungen zwischen mehreren Tabellen – womöglich noch in unterschiedlichen Datenbanken – geht, ist ein Alias häufig sehr nützlich und manchmal auch schlicht notwendig (zum Beispiel bei einem sogenannten Self-Join, d.h. einer mehrfachen Verwendung einer Tabelle in einer Abfrage).

3.4.3 WHERE In der WHERE-Klausel werden Filterkriterien angegeben. Dabei wird man vor allem Feldinhalte vergleichen:

WHERE

SELECT * FROM raum WHERE etage = '5';

Mit diesem Befehl werden alle Räume ausgegeben, die in der fünften Etage liegen. Als Vergleichsoperatoren gibt es = (gleich), < (kleiner als), > (größer als), = (größer oder gleich), (ungleich) und != (ungleich). Ist die Bedingung erfüllt, wird 1 (TRUE) zurückgeliefert, ist sie es nicht, wird 0 (FALSE) zurückgegeben. Normale Zeichenkettenfelder unterscheiden beim Vergleichen von Werten keine Groß- und Kleinschreibung. So ist der Ausdruck 'A' = 'a' wahr. Möchte man dies vermeiden, muss man die Stringfelder vom Typ [VAR]CHAR BINARY wählen.

   



Sandini Bib

Aufgrund der oben schon erwähnten dreiwertigen Logik mit NULL wird immer dann NULL zurückgegeben, wenn einer der beiden zu vergleichenden Werte selbst NULL ist. Um NULL-Werte zu finden, nutzt man eben nicht = NULL (weil das schließlich NULL zurückliefert), sondern IS NULL. Dies ergibt TRUE (1), wenn der entsprechende Vergleichswert NULL ist, ansonsten FALSE (0). Vergleicht man zwei verschiedene Spalten per = miteinander, kann es passieren, dass beide NULL sind. Man könnte annehmen, dass der Vergleich dann TRUE zurückgibt, da ja links und rechts das Gleiche steht. Da NULL aber besonders ist, liefert auch NULL = NULL den Wert NULL zurück. Für diese Fälle gibt es noch einen weiteren Vergleichsoperator: (NULL-sicheres gleich). Dabei ist es nicht mehr relevant, ob links oder rechts ein NULL steht, es wird tatsächlich so verglichen, als ob NULL ein „normaler“ Wert wäre. In Tabelle 3.1 ist das unterschiedliche Verhalten von = und aufgeführt. A

B

A=B

A B

0

0

1

1

0

1

0

0

0

NULL

NULL

0

1

0

0

0

1

1

1

1

1

NULL

NULL

0

NULL

0

NULL

0

NULL

1

NULL

0

NULL

NULL

NULL

1

Tabelle 3.1: Unterschiedliches Verhalten der Vergleichsoperatoren = und

Es lassen sich auch mehrere Vergleiche kombinieren. Dazu verwendet man AND und OR. Bei AND müssen die linke und die rechte Bedingung erfüllt sein, damit der gesamte Ausdruck wahr ist: mysql> SELECT * FROM raum WHERE etage = '5' AND raum_id SELECT * FROM raum WHERE raum_name LIKE 'A%'; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 4 | A5.21 | 5 | 6 | | 5 | A5.23 | 5 | 6 | +---------+-----------+-------+----------+

Der Vorteil von LIKE ist, dass man Platzhalter im Vergleichsstring nutzen kann. Gibt man diese nicht mit an, entspricht LIKE dem Vergleich mit =. Als Platzhalter kann man das Prozentzeichen (%) und den Unterstrich (_) nutzen. Das Prozentzeichen steht als Ersatz für beliebig viele Zeichen (so wie der Stern [*] bei Dateioperationen unter dem Betriebssystem), der Unterstrich für ein einzelnes Zeichen (analog zum Fragezeichen [?] auf Betriebssystem-Ebene). Man kann die Platzhalter an beliebiger Stelle im Vergleichsstring nutzen. Alle Räume mit einer „3“ am Ende findet man mit mysql> SELECT * FROM raum WHERE raum_name LIKE '%3'; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 5 | A5.23 | 5 | 6 | +---------+-----------+-------+----------+

Allerdings ist zu beachten, dass eventuelle Indizes zum Beschleunigen der Abfrage nur dann in Aktion treten können, wenn ein Platzhalter nicht am Anfang steht. Weitere Vergleichsoperatoren werden in Kapitel 3.7.2 aufgeführt.

3.4.4 ORDER BY Wie schon weiter oben geschrieben, ist die Ausgabe der gefundenen Daten mehr oder weniger zufällig. Meist möchte man aber die Datensätze in einer gewissen Reihenfolge sehen. Dazu dient die ORDER BY-Klausel. Ihr folgt eine Liste von Spalten, nach denen die gefundenen Datensätze ausgegeben werden sollen. Zunächst wird nach der ersten Spalte sortiert. Falls diese für mehrere Sätze identisch ist, werden diese nach der zweiten Spalte sortiert usw. Ein Beispiel dafür ist:

   

ORDER BY



Sandini Bib

mysql> SELECT * FROM raum ORDER BY etage, raum_name; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | | 4 | A5.21 | 5 | 4 | | 5 | A5.23 | 5 | 4 | | 1 | A5.33 | 5 | 4 | +---------+-----------+-------+----------+

Man kann durchaus auch nach Spalten sortieren, die nicht mit ausgegeben werden, allerdings wird dies nicht allzu häufig Sinn machen. (Es macht zum Beispiel dann Sinn, wenn man eine Auswahlliste anbieten möchte, die anders sortiert ist als nach den Auswahlwerten. So kann man mit einer Sortierspalte dafür sorgen, dass die wichtigsten Einträge ganz oben landen.) Die normale Sortierung sorgt dafür, dass „aufwärts“ sortiert wird. Sie kann auch durch Angabe von ASC erreicht werden. Für eine umgekehrte Sortierung muss man dem Spaltennamen den Begriff DESC folgen lassen: mysql> SELECT * FROM raum ORDER BY personen DESC, raum_id ASC; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | | 1 | A5.33 | 5 | 4 | | 4 | A5.21 | 5 | 4 | | 5 | A5.23 | 5 | 4 | +---------+-----------+-------+----------+

Bei ORDER BY lassen sich Aliase oder die „normalen“ Tabellen- und Feldnamen nutzen, zudem sind auch hier die MySQL-Funktionen verfügbar. Weiterhin kann man anstelle von Spalten- oder Aliasnamen auch die Position der Spalte in der Abfrage als ganzzahligen Wert angeben: SELECT * FROM raum ORDER BY 4 DESC, 1 ASC;

3.4.5 Aggregatsfunktionen und GROUP BY GROUP BY und der nächste Abschnitt, HAVING, gehören direkt zusammen.

GROUP BY

Sie dienen dazu, Datensätze zusammenzufassen. Wenn man zum Beispiel wissen möchte, wie viele Räume es in den verschiedenen Etagen gibt und wie viele Personen insgesamt darin Platz finden, kann man sich zunächst die vorhandenen Etagen ausgeben lassen:



'DWHQ

Sandini Bib

mysql> SELECT DISTINCT etage FROM raum ORDER BY etage; +-------+ | etage | +-------+ | 2 | | 4 | | 5 | +-------+

Nun kann man sich für jede Etage die vorhandenen Sätze ausgeben lassen, um die Daten selber zusammenzuzählen (hier am Beispiel der fünften Etage): mysql> SELECT personen FROM raum WHERE etage = '5'; +----------+ | personen | +----------+ | 4 | | 4 | | 4 | +----------+

Das macht zusammen zwölf Personen, die in der fünften Etage unterrichtet werden können. Es gibt aber einen deutlich einfacheren Weg: Man nutzt dazu sogenannte Aggregatsfunktionen. Diese fassen die Informationen mehrer Datensätze zusammen. Die einfachste ist COUNT. Mit dieser Funktion kann man sich angeben lassen, wie viele Werte es in einer Spalte gibt. Die bekannteste Variante davon ist COUNT(*), die einfach die Zahl der betroffenen Datensätze zählt: mysql> SELECT COUNT(*) FROM raum WHERE etage = '5'; +----------+ | COUNT(*) | +----------+ | 3 | +----------+

Diese Version von COUNT(*) wird sehr häufig genutzt, um die Anzahl der Datensätze in einer Tabelle zu ermitteln. MySQL ist sogar dahingehend optimiert, dass bei Aufruf von COUNT(*) für eine Tabelle ohne eine WHEREKlausel nicht jeder Datensatz in der Tabelle gezählt wird, sondern einfach (und viel schneller) diese Zahl aus den internen Verwaltungstabellen ermittelt wird. Man kann auch die Werte in einer Spalte zählen lassen:

   



Sandini Bib

mysql> SELECT COUNT(etage) FROM raum; +--------------+ | COUNT(etage) | +--------------+ | 5 | +--------------+

Das macht meist nicht sehr viel Sinn, da sich dieser Wert auch über ein COUNT(*) ermitteln ließe. Allerdings gibt es einen feinen Unterschied: COUNT(*) zählt alle Datensätze, COUNT(feld) nur die, die nicht NULL sind. Mit COUNT(DISTINCT feld) werden übrigens nur die unterschiedlichen Werte gezählt: mysql> SELECT COUNT(DISTINCT etage) FROM raum; +-----------------------+ | COUNT(DISTINCT etage) | +-----------------------+ | 3 | +-----------------------+

Es gibt also (wie wir weiter oben schon per Hand zählen konnten) drei verschiedene Etagen. Weitere Aggregatsfunktionen sind SUM (Summe der Felder), MIN und MAX (minimaler und maximaler Wert der Felder) und noch ein paar weitere Funktionen. Diese werden im Abschnitt über die Funktionen noch besprochen. Kommen wir aber nun zurück zu unserer Fragestellung am Anfang dieses Abschnitts: Wir könnten jetzt natürlich für jede Etage die Anzahl der Datensätze (entsprechend der Anzahl der Räume) und die Summe der Personen ermitteln, hier am Beispiel für die fünfte Etage: mysql> SELECT COUNT(*), SUM(personen) FROM raum WHERE etage = '5'; +----------+---------------+ | COUNT(*) | SUM(personen) | +----------+---------------+ | 3 | 12 | +----------+---------------+

Es gibt aber auch die Möglichkeit, die Datensätze von der Datenbank selber zusammenfassen zu lassen. Dazu teilen wir dem SELECT-Befehl im Abschnitt GROUP BY mit, nach welchen Spalten zusammengefasst werden soll: mysql> SELECT etage, COUNT(*), SUM(personen) FROM raum GROUP BY etage; +-------+----------+---------------+ | etage | COUNT(*) | SUM(personen) | +-------+----------+---------------+ | 2 | 1 | 20 | | 4 | 1 | 11 | | 5 | 3 | 12 | +-------+----------+---------------+



'DWHQ

Sandini Bib

Man erhält so mit einer Abfrage die Ergebnisse für alle Etagen. Die aggregierten Sätze werden automatisch nach den Gruppierungsfeldern sortiert. Die Sortierreihenfolge lässt sich auch hier mit DESC umdrehen. Die weiter oben aufgeführten Aggregatsfunktionen arbeiten auch – wie wir gesehen haben – ohne Probleme, wenn kein GROUP BY angegeben wurde. Dann werden einfach alle Datensätze als eine Gruppe angenommen. Möchte man das Ergebnis noch besonders sortiert bekommen, kann man zudem ein ORDER BY verwenden. Man muss allerdings beachten, dass dieses immer erst nach einem GROUP BY (und dem eventuell genutzten HAVING) angegeben werden darf. Das folgende Beispiel nutzt zugleich Aliase, um die Spalten etwas „sprechender“ zu benennen: mysql> SELECT etage, COUNT(*) AS zimmer, SUM(personen) AS platz -> FROM raum GROUP BY etage ORDER BY platz DESC; +-------+--------+-------+ | etage | zimmer | platz | +-------+--------+-------+ | 2 | 1 | 20 | | 5 | 3 | 12 | | 4 | 1 | 11 | +-------+--------+-------+

3.4.6 HAVING Im vorigen Abschnitt haben wir quasi neue Datensätze erzeugt, indem die vorhandenen aggregiert wurden. Möchte man nun auch aufgrund der neu berechneten Spalten filtern, kann man kein WHERE verwenden. Stattdessen gibt es HAVING, welches analog zum WHERE auf alle Spalten einer solchermaßen zusammengefassten Ausgabe angewandt werden kann. Wollen wir zum Beispiel nur die Etagen ausgegeben haben, die mindestens 12 Personen Platz bieten, kann man die folgende Abfrage starten:

HAVING

mysql> SELECT etage, COUNT(*) AS zimmer, SUM(personen) AS platz -> FROM raum GROUP BY etage HAVING platz >= 12 -> ORDER BY platz DESC; +-------+--------+-------+ | etage | zimmer | platz | +-------+--------+-------+ | 2 | 1 | 20 | | 5 | 3 | 12 | +-------+--------+-------+ HAVING kann im Prinzip auch auf die „normalen“ Spalten (wie hier etage)

angewandt werden. Dies ist allerdings nicht empfehlenswert, da dann

   



Sandini Bib

keinerlei Optimierung greifen kann. Wann immer möglich, sollte WHERE verwendet werden. Ein Beispiel wäre eine Ausgabe nur für die Etagen, die höher als die dritte liegen: mysql> SELECT etage, COUNT(*) AS zimmer, SUM(personen) AS platz -> FROM raum WHERE etage > '3' GROUP BY etage HAVING platz >= 12; +-------+--------+-------+ | etage | zimmer | platz | +-------+--------+-------+ | 5 | 3 | 12 | +-------+--------+-------+

3.4.7 LIMIT Mit diesem Element des SELECT-Befehls hat man die Möglichkeit, Datensätze aufgrund ihrer Position in der Ausgabe wegzufiltern. Man kann eine oder zwei (durch Komma getrennte) Ganzzahlen angeben. Bei zwei Zahlen steht die erste Zahl für die Position des ersten auszugebenden Datensatzes (beginnend bei 0), die zweite für die Anzahl der Datensätze, die auszugeben sind. Fehlt der erste Wert, wird von vorne begonnen. Das folgende Beispiel liefert nur die Datensätze an Position 2-3:

LIMIT

mysql> SELECT * FROM raum LIMIT 1,2; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | +---------+-----------+-------+----------+

Um nur die ersten drei Sätze zu erhalten, reicht dies: mysql> SELECT * FROM raum LIMIT 3; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | +---------+-----------+-------+----------+

Mit LIMIT kann man leicht nur eine „Probemenge“ ausgeben lassen. Es wird auch gerne genutzt, um zum Beispiel HTML-Seiten, die mit Daten aus der MySQL-Datenbank gefüllt werden, in ihrer Länge zu begrenzen. Man kann dann pro Seite nur 20, 50, 100, … Sätze ausgeben lassen und erst beim Blättern auf die nächste Seite die folgenden ausgeben:



'DWHQ

Sandini Bib

SELECT ... FROM ... LIMIT 0,50; SELECT ... FROM ... LIMIT 50,50; SELECT ... FROM ... LIMIT 100,50; ...

Eine andere Verwendungsmöglichkeit ist das Beschränken auf einen Satz, wenn die Möglichkeit vorhanden ist, dass mehr Sätze existieren, dies aber nicht so wichtig ist oder die entscheidenden Werte sowieso gleich sind. Man umgeht damit häufiger komplexe Abfragen, die mit anderen Datenbank-Systemen notwendig werden (allerdings bei MySQL teilweise auch nicht funktionieren würden).

3.4.8 Weitere Elemente und Optionen Es gibt noch einige Elemente von SELECT, die der Vollständigkeit halber hier aufgeführt werden sollen: • INTO {OUTFILE | DUMPFILE} 'file_name' export_options Mit diesem Element kann man die Daten in eine Datei ausgeben lassen. Die Datei wird auf dem Server angelegt und darf noch nicht existieren. Damit wird verhindert, dass bestehende wichtige Dateien (wie zum Beispiel /etc/passwd unter Unix) überschrieben werden können. Dabei kann man in den export_options festlegen, wie die Datensätze ausgegeben werden sollen. Folgende Optionen sind möglich: – FIELDS [TERMINATED BY 'fielddiv'] [[OPTIONALLY] ENCLOSED BY 'enc_char'] [ESCAPED BY 'esc_char']

Alle drei Elemente (TERMINATED BY, ENCLOSED BY und ESCAPED BY) sind optional, lässt man sie alle drei weg, darf aber auch FIELDS nicht angegeben werden. Mit TERMINATED BY kann man festlegen, wie die Werte untereinander getrennt werden sollen. Standardmäßig wird '\t' genutzt, d.h. die Felder sind durch ein Tabulatorzeichen getrennt. ENCLOSED BY gibt an, womit die Werte umschlossen sein sollen. Fehlt diese Angabe, werden die Felder nicht umschlossen. Nutzt man zusätzlich OPTIONALLY, werden nur Stringfelder mit diesem Zeichen umschlossen. Mit ESCAPED BY lässt sich festlegen, wie Sonderzeichen (Tabulator, Zeilenumbruch, …) in Werten behandelt werden sollen. Standard hierfür ist der Backslash (der allerdings selber mit dem Fluchtzeichen \ maskiert werden muss). Gibt man also nichts an, ist dies genauso, also ob man FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\' angegeben hätte. – LINES TERMINATED BY 'linedev' Mit dieser Option kann man festlegen, wie die einzelnen Datensätze getrennt werden sollen. Standard ist hier '\n', d.h. jeder Datensatz steht in einer eigenen Zeile.

   



Sandini Bib

Normalerweise wird man INTO OUTFILE nutzen, denn dann werden alle Daten, die sonst ausgegeben würden, in die Datei geschrieben. Verwendet man stattdessen INTO DUMPFILE, wird nur ein Datensatz (die Ergebnismenge darf auch nicht größer sein, notfalls muss man mit LIMIT 1 arbeiten) ohne weitere Begrenzer oder Fluchtzeichen in die Datei geschrieben. Dies kann nützlich sein, wenn man den Inhalt eines binären Blob-Feldes in einer Datei speichern möchte (zum Beispiel Bilder oder Klänge). • PROCEDURE procedure_name Mit Hilfe dieses Elements kann man MySQL erweitern. Es lassen sich externe Prozeduren aufrufen, die die Daten des SELECT-Befehls verarbeiten und selber wiederum eine Ergebnismenge zurückgeben. • FOR UPDATE | LOCK IN SHARE MODE Gibt man FOR UPDATE mit an, werden auf die entsprechenden Datensätze in der Tabelle auch Schreib-Locks gesetzt (was sonst nur bei INSERT und UPDATE geschieht), sofern der Tabellentyp dies unterstützt. Mit LOCK IN SHARE MODE kann man bei Tabellen des Typs InnoDB sicher gehen, dass die eben gelesenen Daten auch dann noch da sind, wenn man danach Datenänderungen vornimmt. Dies ist im Zusammenhang mit Transaktionen sinnvoll. • STRAIGHT_JOIN Diese Option ist nur bei mehreren genutzten Tabellen sinnvoll. Sie sorgt dafür, dass die Verknüpfung zwischen den Tabellen anders vorgenommen wird als vom MySQL-Server geplant. • SQL_SMALL_RESULT, SQL_BIG_RESULT Diese beiden Parameter dienen dazu, den MySQL-Server bei größeren Aggregationsaktionen in Zusammenhang mit GROUP BY oder DISTINCT zu unterstützen. SQL_SMALL_RESULT teilt dem Server mit, dass es sich um nicht allzu viele Daten handeln wird und der Server sie möglichst in einer schnellen temporären Tabelle zwischenspeichern soll, anstatt sie aufwändig zu sortieren. Diese Option ist in den aktuellen Versionen nur noch sehr selten notwendig. SQL_BIG_RESULT ist das genaue Gegenteil davon. Es wird sich voraussichtlich um viele Daten handeln, so dass der MySQL-Server für diesen Befehl gleich auf temporäre Tabellen umsteigen sollte, die auf der Festplatte zwischengespeichert sind. • SQL_BUFFER_RESULT Mit dieser Option wird der Server angewiesen, die Daten aus der eigentlichen Tabelle möglichst schnell in einer temporären Tabelle zwischenzuspeichern, um die Tabelle selber wieder freigeben zu kön-



'DWHQ

Sandini Bib

nen. Dies kann dann nützlich sein, wenn es im Anschluss lange dauert, die Daten an den Client zu übertragen (zum Beispiel wegen einer langsamen Leitung), der Server aber viele Zugriffe verzeichnet. • HIGH_PRIORITY Durch diese Option wird dafür gesorgt, dass der SELECT-Befehl auch dann bevorzugt behandelt wird, wenn eigentlich ein UPDATE-Befehl schon darauf wartet, auf die Tabelle zugreifen zu können (und er im Normalfall auch eher dran käme). Diese Option sollte nur dann verwendet werden, wenn die Abfragen sehr schnell sind und möglichst hintereinander durchgeführt werden sollen.

3.5

Daten ändern

Nach dem langen Abschnitt über das Selektieren von Daten geht es jetzt wieder etwas ruhiger zur Sache. Es gibt zwei Möglichkeiten, Daten zu ändern: zum einen den bekannten Befehl UPDATE, zum anderen den Befehl REPLACE, mit dem Daten angelegt oder geändert werden können.

3.5.1 UPDATE Hier die komplette Syntax des UPDATE-Befehls:

UPDATE

UPDATE [LOW_PRIORITY] [IGNORE] tbl_name SET col_name1=expr1, [col_name2=expr2, ...] [WHERE where_definition] [LIMIT number] tbl_name ist natürlich der Name der Tabelle, in der Daten aktualisiert werden sollen. col_name1 gibt die erste Spalte an, die mit expr1 geändert wer-

den soll. Durch Komma getrennt kann man beliebig viele weitere Kombinationen aus Spaltenname und Wert angeben. In der WHERE-Klausel wird festgelegt, welche Datensätze geändert werden sollen. Fehlt diese Angabe, werden alle Datensätze aktualisiert. Weiter oben haben wir ja schon Beispiele für das Aktualisieren von Daten gesehen. Hier noch mal ein einfaches: mysql> SELECT * FROM raum WHERE raum_id = 5; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 5 | A5.23 | 5 | 4 | +---------+-----------+-------+----------+ 1 row in set (0.01 sec) mysql> UPDATE raum SET personen = 6 WHERE raum_id = 5;

  



Sandini Bib

Query OK, 1 row affected (0.14 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> SELECT * FROM raum WHERE raum_id = 5; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 5 | A5.23 | 5 | 6 | +---------+-----------+-------+----------+ 1 row in set (0.00 sec)

Man kann auch die bestehenden Daten als Grundlage nehmen. So lässt sich zum Beispiel die Kapazität von Raum A5.21 um 50% erhöhen: mysql> SELECT * FROM raum WHERE raum_name = 'A5.21'; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 4 | A5.21 | 5 | 4 | +---------+-----------+-------+----------+ 1 row in set (0.00 sec) mysql> UPDATE raum SET personen = personen * 1.5 -> WHERE raum_name = 'A5.21'; Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> SELECT * FROM raum WHERE raum_name = 'A5.21'; +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 4 | A5.21 | 5 | 6 | +---------+-----------+-------+----------+ 1 row in set (0.00 sec)

Es gibt noch drei weitere Optionen: • Wie schon erwähnt, kann man auch mehrere Datensätze gleichzeitig aktualisieren. Mit LIMIT lässt sich dann ähnlich wie bei SELECT (allerdings ohne Startwert) begrenzen, wie viele Sätze geändert werden sollen. • LOW_PRIORITY sorgt dafür, dass die Daten erst dann aktualisiert werden, wenn kein anderer Client mehr auf die Tabelle zugreift. • Versucht man Primärschlüssel oder Felder mit eindeutigen Indizes zu ändern, kann es passieren, dass Feldinhalte mehrfach vorkommen. Da das in diesem Fall nicht vorkommen darf, wird eine Fehlermeldung ausgegeben. Mit IGNORE wird diese Fehlermeldung unterdrückt, die entsprechenden Datensätze dann aber natürlich trotzdem nicht aktualisiert.



'DWHQ

Sandini Bib

3.5.2 REPLACE REPLACE ist eine Kombination aus INSERT und UPDATE. Neue Datensätze werden eingefügt. Ist ein Satz mit dem angegebenen Primärschlüssel schon vorhanden, wird der alte Satz gelöscht und der neue eingefügt. Die Syntax gibt es analog zu INSERT mit drei Varianten:

REPLACE

REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name [(col_name,...)] VALUES (expression,...),(...),... REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name SET col_name = expression, col_name = expression, ... REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name [(col_name,...)] SELECT ...

Es gibt die gleichen Optionen wie bei INSERT, nur IGNORE ist nicht vorhanden (und auch nicht sinnvoll, da identische Primärschlüssel eben nicht ignoriert, sondern gesondert behandelt werden).

3.6

Daten löschen

Auch das Löschen von Daten ist nicht so kompliziert, wie wir schon weiter oben gesehen haben. Neben DELETE gibt es noch den Befehl TRUNCATE.

3.6.1 DELETE Die vollständige Syntax des DELETE-Befehls lautet: DELETE [WHERE [ORDER [LIMIT

DELETE

[LOW_PRIORITY | QUICK] FROM table_name where_definition] BY order_by_definition] number]

Die „einfache“ Form ist klar: table_name ist der Name der Tabelle, aus der gelöscht werden soll. Die where_definition legt die betroffenen Datensätze in bekannter Art und Weise fest. Gibt man sie nicht an, wird die Tabelle komplett geleert. Dies geht besonders schnell, da nicht alle Datensätze einzeln gelöscht werden, sondern die Tabelle einfach neu aufgebaut wird (was gerade bei großen Tabellen deutlich schneller geht). Normalerweise wird bei DELETE zurückgemeldet, wie viele Datensätze gelöscht wurden. Bei DELETE FROM ... ohne WHERE geschieht dies nicht. Will man diese Zahl trotzdem erfahren, muss man eine WHERE-Klausel angeben, die immer wahr ist: DELETE FROM mytable WHERE 1>0;

Es hat allerdings den Nachteil, dass es deutlich länger dauert.

  



Sandini Bib

Mit LIMIT kann man wieder die maximale Anzahl der zu löschenden Sätze festlegen. In diesem Zusammenhang macht auch eine Angabe von ORDER BY Sinn: Die Sätze werden in der festgelegten Reihenfolge gelöscht. Ist dies normalerweise nicht relevant, kann eine Angabe von ORDER BY mytimestamp LIMIT 1 dafür sorgen, dass nur der älteste Datensatz gelöscht wird (wenn mytimestamp ein Feld vom Typ TIMESTAMP ist). Schließlich gibt es noch die Optionen LOW_PRIORITY und QUICK: • Mit LOW_PRIORITY wartet der Befehl so lange, bis kein anderer Client mehr auf die Tabelle zugreift. • QUICK sorgt dafür, dass der Index während des Löschvorgangs nicht aktualisiert wird, was in bestimmten Fällen für einen schnelleren Ablauf sorgen kann.

3.6.2 TRUNCATE TRUNCATE TABLE

Der Befehl TRUNCATE TABLE wurde von Oracle übernommen und hat die gleiche Funktion wie ein DELETE FROM ohne WHERE-Klausel. Die Syntax lautet: TRUNCATE TABLE table_name;

3.7

Funktionen in SQL

Es gibt in MySQL viele Funktionen, die man in SELECT, WHERE, GROUP BY, HAVING und ORDER BY nutzen kann. Allen ist gemein, dass man ihnen einen oder mehrere Werte übergibt (oder auch eine Liste von Werte, wie bei den aggregierenden Funktionen) und als Ergebnis ein einzelner Wert zurückgeliefert wird. Es handelt sich also immer um skalare Funktionen. Die Funktionen lassen sich in mehrere Gruppen unterteilen: Operatoren (die wir schon weiter oben in Kapitel 3.4.3 besprochen haben), Stringfunktionen, numerische Funktionen, Datums- und Zeitfunktionen, andere Funktionen und aggregierende Funktionen.

3.7.1 Stringfunktionen In Tabelle 3.2 sind alle Stringfunktionen aufgeführt. Funktion

Beschreibung

ASCII(str)

liefert den ASCII-Wert eines Zeichens zurück

ORD(str)

liefert den Ordinalwert eines Zeichens zurück (auch bei Multi-Byte-Zeichensätzen)

Tabelle 3.2: Stringfunktionen in MySQL



,903

Sandini Bib

Funktion

Beschreibung

CONV(n, from_base, to_base)

wandelt eine Zahl von einer Basis in eine andere um

BIN(n)

liefert die Binärdarstellung einer Zahl

OCT(n)

liefert die Oktaldarstellung einer Zahl

HEX(n)

liefert die Hexadezimaldarstellung einer Zahl

CHAR(n, ...)

liefert einen String zurück, der aus den Zeichen mit den angegebenen ASCIIWerten besteht

CONCAT(str1, str2, ...)

verbindet die angegebenen Strings zu einem neuen String

CONCAT_WS(seperator, str1, str2, ...)

verbindet die angegebenen Strings zu einer durch ein Trennzeichen unterteilten Liste

LENGTH(str) OCTET_LENGTH(str) CHAR_LENGTH(str) CHARACTER_LENGTH(str)

gibt die Länge eines Strings zurück

LOCATE(substr, str[, pos]) POSITION(substr IN str) INSTR(str, substr)

liefert die Position eines Substrings in einem String zurück

LPAD(str, len, padstr)

füllt einen String von vorne mit Zeichen auf

RPAD(str, len, padstr)

füllt einen String von hinten mit Zeichen auf

LEFT(str, len)

liefert einen Teil vom Anfang eines Strings zurück

RIGHT(str, len)

liefert einen Teil vom Ende eines Strings zurück

SUBSTR(str, pos[, len]) SUBSTR(str FROM pos [FOR len]) MID(str, pos, len)

liefert einen Teil aus einem String zurück

SUBSTR_INDEX(str, delim, count)

liefert einen Teil aus einem String zurück. Der entsprechende Teil bestimmt sich über das Vorkommen eines anderen Strings.

LTRIM(str)

entfernt führende Leerzeichen aus einem String

RTRIM(str)

entfernt Leerzeichen aus einem String

TRIM([[BOTH | LEADING | TRAILING] [remstr] FROM] str)

entfernt Zeichen vom Anfang und/oder Ende eines Strings

SOUNDEX(str)

liefert einen Wert zurück, der sich an der Aussprache eines Strings orientiert

SPACE(n)

liefert Leerzeichen zurück

Tabelle 3.2: Stringfunktionen in MySQL (Forts.)

 



Sandini Bib

Funktion

Beschreibung

REPLACE(str, from_str, to_str)

ersetzt das Vorkommen eines Substrings in einem String durch einen anderen Text

REPEAT(str, count)

wiederholt einen String mehrfach

REVERSE(str)

dreht einen String um

INSERT(str, pos, len, newstr)

fügt einen String in einen anderen String ein

ELT(n, str1, str2, str3, ...)

liefert abhängig von einer Zahl einen der aufgeführten Strings zurück

FIELD(str, str1, str2, str3, ...)

liefert eine Zahl in Abhängigkeit vom Vorkommen eines Strings in einer Reihe von Strings zurück

FIND_IN_SET(str, strlist)

liefert eine Zahl in Abhängigkeit vom Vorkommen eines Strings in einem Set zurück

MAKE_SET(bits, str1, str2, ...)

stellt ein Set aus einer Zahl und Strings zusammen

EXPORT_SET(bits, on, off[, seperator[, number_of_bits]])

liefert eine Reihe von Wahr/Falsch-Werte zurück

LCASE(str) LOWER(str)

liefert einen String in Kleinbuchstaben zurück

UCASE(str) UPPER(str)

liefert einen String in Großbuchstaben zurück

LOAD_FILE(file_name)

liefert den Inhalt einer Datei als String zurück

Tabelle 3.2: Stringfunktionen in MySQL (Forts.)

3.7.2 Stringvergleiche Neben den „normalen“ Vergleichen (=, >,

verschiebt die Bits einer Zahl nach rechts

~

invertiert die Bits einer Zahl

Tabelle 3.8: Bit-Operatoren

3.8

Komplexe Abfragen

Die bisherigen Abfragen in den Kapiteln 3.2 und 3.4 bezogen sich immer nur auf eine einzelne Tabelle. Der Witz an relationalen DatenbankSystemen ist aber gerade, dass man die vielen Tabellen, in denen man die Daten abgelegt hat, geschickt verknüpft. Um dies auch sinnvoll demonstrieren zu können, müssen natürlich erst einmal mehr Daten vorhanden sein, als wir bisher eingegeben haben. Damit nicht jedes Mal alle Befehle erneut abgetippt werden müssen, kann man auch Dateien mit SQL-Befehlen ausführen.

3.8.1 Intermezzo – Kommentare und SQL-Dateien Kommentare

SQL-Befehle lassen sich problemlos in einer Textdatei speichern, um diese dann in oder mit MySQL aufzurufen. Um später auch noch zu wissen, wozu die entsprechende Datei eigentlich gut war, sollte man sie natürlich mit einem möglichst sinnigen Namen versehen. Meist reicht das aber nicht, um auch nach längerer Zeit den Inhalt zu verstehen. Zusätzlich hat man daher noch die Möglichkeit, Kommentare in die Befehle einzufügen. Es gibt drei verschiedene Varianten: • Man leitet einen Kommentar mit -- ein, also zwei Minus-Zeichen hintereinander. Alles, was danach bis zu Ende der Zeile geschrieben ist, wird als Kommentar angesehen und nicht weiter beachtet. Nach den zwei Strichen muss unbedingt noch ein Leerzeichen folgen. Diese Kommentar-Variante entspricht nahezu (von der Leerzeichenpflicht abgesehen) dem ANSI-SQL-Standard. • Ein Kommentar kann ebenso mit einem # eingeleitet werden. Auch solche Kommentare gelten dann bis zum Ende der Zeile. • Schließlich kann man beliebig große Bereiche mit einem /* und */ umschließen. Der Bereich kann innerhalb einer Zeile sein (so dass der Rest der Zeile dann wieder verarbeitet wird, aber auch über mehrere Zeilen gehen.



'DWHQ

Sandini Bib

Folgende Beispiele sollen das noch einmal verdeutlichen: SELECT * FROM raum; -- Ein Kommentar, der bis zum Zeilenende reicht SELECT * FROM raum; # Noch ein Kommentar bis zum Zeilenende SELECT * /* also alles */ FROM raum; /* Ein mehrzeiliger Kommentar, in dem man einiges an Informationen unterbringen kann. */

Um die Befehle aus einer solchen Datei aufrufen zu können, kann man diese entweder in mysql ausführen lassen oder mysql direkt damit starten.

SQL-Dateien

• Befindet man sich in mysql, nutzt man den Befehl SOURCE (der kein Semikolon am Ende benötigt), gefolgt von der Angabe des Dateinamens: mysql> SOURCE kap02.sql

• Man kann auch mysql direkt die Befehle mitgeben, indem man die Standardeingabe mit < aus einer Datei befüllt: prompt> mysql < kap02.sql

Natürlich kann man vor dem < noch Parameter angeben, zum Beispiel für die Datenbank (die aber auch mit USE ... in der Datei aktiviert werden kann), den Benutzer oder das Kennwort. Möchte man unter Windows Dateinamen mit einem Pfad angeben, darf man nicht den Backslash (\) nutzen. Stattdessen muss der normale Schrägstrich (/) verwendet werden! Ich gehe davon aus, dass Sie die entsprechenden Tabellen aus Kapitel 2 alle angelegt haben. Wenn nicht, sollten Sie dies jetzt nachholen. Daten haben wir bisher nur für die Tabelle raum angelegt. Nun wollen wir auch die anderen Tabellen mit Leben füllen. Die entsprechenden Dateien finden Sie auch auf der beiliegenden CDROM als kap02.sql und kap03.sql im Verzeichnis Beispiel.

 



Sandini Bib

3.8.2 Joins Joins

Wie werden nun mehrere Tabellen in einer Abfrage verknüpft? Dies geschieht über sogenannte Joins. Sie legen fest, wie die Tabellen verbunden sein sollen. Die einfachste Variante ist, dass man mehrere Tabellen auf einmal angibt: mysql> SELECT * FROM leihinstrument, instrument;

Leider ist die Ausgabe dabei nicht sehr sinnvoll. In Abbildung 3.1 sieht man, dass jeder Datensatz aus der Tabelle leihinstrument mit jedem Datensatz der Tabelle instrument verknüpft wurde. +-------------------+---------------+------------+--------+--------+---------------+------------+--------------+ | leihinstrument_id | instrument_id | hersteller | typ | kosten | instrument_id | instr_name | instr_gruppe | +-------------------+---------------+------------+--------+--------+---------------+------------+--------------+ | 1 | 1 | Perl | PF-501 | 10.00 | 1 | Querflöte | Holzbläser | | 2 | 1 | Yamaha | YF-311 | 15.00 | 1 | Querflöte | Holzbläser | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 1 | Querflöte | Holzbläser | | 4 | 8 | Korg | KGX2A | 14.00 | 1 | Querflöte | Holzbläser | | 1 | 1 | Perl | PF-501 | 10.00 | 2 | Klarinette | Holzbläser | | 2 | 1 | Yamaha | YF-311 | 15.00 | 2 | Klarinette | Holzbläser | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 2 | Klarinette | Holzbläser | | 4 | 8 | Korg | KGX2A | 14.00 | 2 | Klarinette | Holzbläser | | 1 | 1 | Perl | PF-501 | 10.00 | 3 | Violine | Streicher | | 2 | 1 | Yamaha | YF-311 | 15.00 | 3 | Violine | Streicher | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 3 | Violine | Streicher | | 4 | 8 | Korg | KGX2A | 14.00 | 3 | Violine | Streicher | | 1 | 1 | Perl | PF-501 | 10.00 | 4 | Viola | Streicher | | 2 | 1 | Yamaha | YF-311 | 15.00 | 4 | Viola | Streicher | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 4 | Viola | Streicher | | 4 | 8 | Korg | KGX2A | 14.00 | 4 | Viola | Streicher | | 1 | 1 | Perl | PF-501 | 10.00 | 5 | Posaune | Blechbläser | | 2 | 1 | Yamaha | YF-311 | 15.00 | 5 | Posaune | Blechbläser | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 5 | Posaune | Blechbläser | | 4 | 8 | Korg | KGX2A | 14.00 | 5 | Posaune | Blechbläser | | 1 | 1 | Perl | PF-501 | 10.00 | 6 | Trompete | Blechbläser | | 2 | 1 | Yamaha | YF-311 | 15.00 | 6 | Trompete | Blechbläser | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 6 | Trompete | Blechbläser | | 4 | 8 | Korg | KGX2A | 14.00 | 6 | Trompete | Blechbläser | | 1 | 1 | Perl | PF-501 | 10.00 | 7 | Klavier | Tasten | | 2 | 1 | Yamaha | YF-311 | 15.00 | 7 | Klavier | Tasten | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 7 | Klavier | Tasten | | 4 | 8 | Korg | KGX2A | 14.00 | 7 | Klavier | Tasten | | 1 | 1 | Perl | PF-501 | 10.00 | 8 | Keyboard | Tasten | | 2 | 1 | Yamaha | YF-311 | 15.00 | 8 | Keyboard | Tasten | | 3 | 2 | Keilwerth | KW-123 | 12.50 | 8 | Keyboard | Tasten | | 4 | 8 | Korg | KGX2A | 14.00 | 8 | Keyboard | Tasten | +-------------------+---------------+------------+--------+--------+---------------+------------+--------------+

Abbildung 3.1: Ein Join zwischen zwei Tabellen ohne Verknüpfung

Das kann zwar in manchen Fällen Sinn machen, ist aber im Allgemeinen nicht das, was man sich wünscht. Man muss also die beiden Tabellen miteinander verknüpfen. Dies kann auf die folgende Art und Weise geschehen: mysql> SELECT * FROM leihinstrument, instrument WHERE -> leihinstrument.instrument_id = instrument.instrument_id;

Da das Ergebnis für dieses Buch immer etwas breit ist, werden wir nur die wichtigsten Felder ausgeben. Zugleich nutzen wir Aliase, um nicht jedes Mal die langen Tabellennamen schreiben zu müssen:



,903

Sandini Bib

mysql> SELECT instr_name, instr_gruppe, hersteller, typ, kosten -> FROM leihinstrument l, instrument i -> WHERE l.instrument_id = i.instrument_id; +------------+--------------+------------+--------+--------+ | instr_name | instr_gruppe | hersteller | typ | kosten | +------------+--------------+------------+--------+--------+ | Querflöte | Holzbläser | Perl | PF-501 | 10.00 | | Querflöte | Holzbläser | Yamaha | YF-311 | 15.00 | | Klarinette | Holzbläser | Keilwerth | KW-123 | 12.50 | | Keyboard | Tasten | Korg | KGX2A | 14.00 | +------------+--------------+------------+--------+--------+

Die Verknüpfung zwischen den beiden Tabellen erfolgt über die WHEREKlausel. Es werden also aus der großen Liste des ersten Versuchs all die Datensätze ausgegeben, bei denen die instrument_id für beide Tabellen identisch ist. Damit MySQL weiß, woher die entsprechenden Felder genommen werden sollen (schließlich sind ihre Namen identisch), muss man ihnen den Namen der entsprechenden Tabelle (bzw. deren Alias, falls vorhanden) mitgeben. Dies geschieht, indem man den Tabellennamen vor den Spaltennamen setzt und beide mit einem Punkt trennt. Was passiert nun, wenn wir die instrument_id mit ausgeben wollen? mysql> SELECT instrument_id, instr_name, instr_gruppe, -> hersteller, typ, kosten -> FROM leihinstrument l, instrument i -> WHERE l.instrument_id = i.instrument_id; ERROR 1052: Column: 'instrument_id' in field list is ambiguous

Man erhält einen Fehler, weil MySQL nicht weiß, welches der beiden Felder mit dem Namen instrument_id genutzt werden soll. Natürlich sind bei dieser Abfrage immer beide Felder identisch, da wir die WHEREBedingung entsprechend gewählt haben. Aber dies ist nicht zwingend der Fall, und daher muss man immer angeben, aus welcher Tabelle ein Feld genommen werden soll, wenn es in mehreren Tabellen vorkommt: mysql> SELECT l.instrument_id, instr_name, hersteller, typ, kosten -> FROM leihinstrument l, instrument i -> WHERE l.instrument_id = i.instrument_id; +---------------+------------+------------+--------+--------+ | instrument_id | instr_name | hersteller | typ | kosten | +---------------+------------+------------+--------+--------+ | 1 | Querflöte | Perl | PF-501 | 10.00 | | 1 | Querflöte | Yamaha | YF-311 | 15.00 | | 2 | Klarinette | Keilwerth | KW-123 | 12.50 | | 8 | Keyboard | Korg | KGX2A | 14.00 | +---------------+------------+------------+--------+--------+

 



Sandini Bib

So etwas kann man natürlich auch mit mehreren Tabellen machen: mysql> SELECT CONCAT(LEFT(vorname, 1), '. ', nachname) AS lehrer, -> instr_name AS instrum, raum_name AS raum, -> CASE wochentag WHEN 0 THEN 'Mo' WHEN 1 THEN 'Di' -> WHEN 2 THEN 'Mi' WHEN 3 THEN 'Do' WHEN 4 THEN 'Fr' -> WHEN 5 THEN 'Sa' WHEN 6 THEN 'So' END AS tag, -> CONCAT(TIME_FORMAT(uhrzeit_von, '%H:%i'), '-', -> TIME_FORMAT(uhrzeit_bis, '%H:%i')) AS zeit, -> kosten AS preis, einzel -> FROM unterricht AS u, lehrer AS l, instrument AS i, raum AS r -> WHERE u.lehrer_id = l.lehrer_id -> AND u.instrument_id = i.instrument_id -> AND u.raum_id = r.raum_id; +------------+-----------+-------+------+-------------+-------+--------+ | lehrer | instrum | raum | tag | zeit | preis | einzel | +------------+-----------+-------+------+-------------+-------+--------+ | G. Mosler | Posaune | A5.23 | Mi | 19:15-20:00 | 25.00 | 0 | | H. Knarer | Querflöte | C4.16 | Fr | 17:00-17:45 | 30.00 | 1 | | K. Hansen | Violine | B2.02 | Do | 15:35-16:20 | 20.00 | 0 | | K. Hansen | Viola | B2.02 | Do | 16:30-17:15 | 20.00 | 0 | | P. Ottbein | Klavier | A5.33 | Sa | 10:00-11:00 | 35.00 | 1 | +------------+-----------+-------+------+-------------+-------+--------+

Lassen Sie sich nicht von dem ausgesprochen langen Abschnitt für die Felder verwirren. Ich zeige hier gleich die verschiedenen Möglichkeiten, Funktionen einzusetzen. An dieser Stelle sollte eher die WHERE-Klausel von Interesse sein. In ihr werden alle angegebenen Tabellen miteinander verbunden. Der Lehrer wird über die lehrer_id hinzugeholt, das Instrument über die instrument_id und der Raum über die raum_id. Man kann die Verknüpfung zwischen Tabellen auch direkt über Joins abbilden. Dazu gibt es verschiedene Syntax-Varianten: • table_reference, table_reference Dies ist die oben schon aufgeführte Variante (table_reference steht hier und in den folgenden Beispielen für eine Tabelle und einen eventuellen Alias), bei der man die Verknüpfung zwischen den Tabellen in der WHERE-Bedingung angeben muss. Tut man dies nicht, erhält man eine Ergebnismenge, in der alle möglichen Verbindungen aufgeführt sind. Dies ist ein sogenannter Cross Join. Mit vorhandener Vergleichsangabe (die hier das gewünschte Ergebnis brachte), wird es zu einem Inner Join, bei dem nur Werte ausgegeben werden, wenn es für sie Datensätze in beiden beteiligten Tabellen gibt. Diesen Inner Join kann man auch eleganter mit der folgenden Syntax festlegen:



,903

Sandini Bib

• table_reference INNER JOIN table_reference join_condition

INNER JOIN

Die Tabellen werden explizit als Inner Join verknüpft. In der join_condition wird festgelegt, wie die Tabellen verbunden werden sollen. Am bekanntesten dafür ist die Verwendung von ON condition, wobei letztere dem Vergleich in der WHERE-Klausel entspricht: SELECT ... FROM leihinstrument AS l INNER JOIN instrument AS i ON l.instrument_id = i.instrument_id

Damit erhält man das gleiche Ergebnis wie weiter oben mit WHERE. Man sollte auch beachten, dass in ON ... keine anderen Filterkriterien eingebunden werden sollten als die, die zum Verknüpfen der Tabellen notwendig sind. Alle weiteren Einschränkungen gehören in die WHERE-Klausel. Eine andere Variante für die join_condition ist USING(column1, column2, ...). Die angegebenen Spalten müssen in beiden Tabellen vorhanden sein. Sie werden dann alle auf Gleichheit geprüft. Um das selbe Ergebnis wie oben zu erhalten, lautet der Befehl dann: SELECT ... FROM leihinstrument AS l INNER JOIN instrument AS i USING (instrument_id)

• table_reference [CROSS] JOIN table_reference

CROSS JOIN

Benötigt man tatsächlich einmal einen Cross Join, kann man diese Syntax nutzen. Sie ist identisch zur Nutzung des Komma zwischen zwei Tabellen. Man kann allerdings auch hier in der WHERE-Klausel doch wieder dafür sorgen, dass nur passende Datensätze aus den beiden Tabellen angezeigt werden: SELECT ... FROM leihinstrument l JOIN instrument i WHERE l.instrument_id = i.instrument_id

• table_reference STRAIGHT_JOIN table_reference

STRAIGHT_JOIN

STRAIGHT_JOIN ist identisch zu [CROSS] JOIN, nur dass hier der Optimierer von MySQL anders vorgeht und die erste Tabelle komplett einliest, bevor er sie mit der zweiten verknüpft. Das ist in den meisten Fällen zwar eher unvorteilhaft für die Geschwindigkeit, aber es gibt durchaus Situationen, in denen ein Straight Join schneller ist als ein normaler Join.

 



Sandini Bib

OUTER JOIN

• table_reference {LEFT | RIGHT} [OUTER] JOIN table_reference join_condition

Kommen wir nun zu den Outer Joins. Bisher haben wir immer nur Datensätze ausgegeben, die mit Daten aus beiden beteiligten Tabellen befüllt waren. Bei den Outer Joins kann man auch Sätze aus einer Tabelle ausgeben, die kein passendes Äquivalent in der anderen Tabelle haben. Nehmen wir einmal an, wir wollen für eine Übersicht alle Räume mit ihren Belegungsdaten ausgeben lassen. Als ersten Versuch würden wir vielleicht das Folgende nehmen: mysql> SELECT raum_name, wochentag, uhrzeit_von, uhrzeit_bis -> FROM raum INNER JOIN unterricht USING (raum_id); +-----------+-----------+-------------+-------------+ | raum_name | wochentag | uhrzeit_von | uhrzeit_bis | +-----------+-----------+-------------+-------------+ | C4.16 | 4 | 17:00:00 | 17:45:00 | | B2.02 | 3 | 15:35:00 | 16:20:00 | | B2.02 | 3 | 16:30:00 | 17:15:00 | | A5.33 | 5 | 10:00:00 | 11:00:00 | | A5.23 | 2 | 19:15:00 | 20:00:00 | +-----------+-----------+-------------+-------------+

Schon ganz hübsch, aber es wäre natürlich auch schön, wenn man auch noch die Räume angezeigt bekäme, die gar nicht belegt sind. Das geht natürlich bei einem Inner Join nicht, weil dort schließlich nur Datensätze ausgegeben werden, die ihren Ursprung in beiden Tabellen haben. Eine Lösung dazu bieten Outer Joins. Finden sie in der einen der beiden Tabellen keinen passenden Datensatz, füllen sie die entsprechenden Felder mit NULL auf und geben trotzdem die Daten aus der anderen Tabelle aus. In Abhängigkeit von der Position der „optionalen“ Tabelle spricht man von eine Left Outer Join (Tabelle, deren Daten auf jeden Fall ausgegeben werden sollen, steht links) oder einem Right Outer Join (Tabelle, deren Daten auf jeden Fall ausgegeben werden sollen, steht rechts). Um nun unser oben aufgeführtes Problem zu lösen, kann man folgenden Befehl nutzen: mysql> SELECT raum_name, wochentag, uhrzeit_von, uhrzeit_bis -> FROM raum LEFT OUTER JOIN unterricht USING (raum_id); +-----------+-----------+-------------+-------------+ | raum_name | wochentag | uhrzeit_von | uhrzeit_bis | +-----------+-----------+-------------+-------------+ | A5.33 | 5 | 10:00:00 | 11:00:00 | | B2.02 | 3 | 15:35:00 | 16:20:00 | | B2.02 | 3 | 16:30:00 | 17:15:00 | | C4.16 | 4 | 17:00:00 | 17:45:00 | | A5.21 | NULL | NULL | NULL | | A5.23 | 2 | 19:15:00 | 20:00:00 | +-----------+-----------+-------------+-------------+



,903

Sandini Bib

Wie man sieht, sind alle Räume mit ihren Belegungszeiten vorhanden (Raum B2.02 auch zwei Mal, da es zwei Termine in ihm gibt), aber auch der Raum A5.21, obwohl kein Unterricht in ihm stattfindet. wochentag, uhrzeit_von und uhrzeit_bis sind dementsprechend auch mit NULL gefüllt. Das Gegenstück dazu wäre der Right Outer Join, mit dem man das gleiche Ergebnis auch erhalten kann, wenn man die beiden Tabellen umgekehrt definiert: mysql> SELECT raum_name, wochentag, uhrzeit_von, uhrzeit_bis -> FROM unterricht RIGHT OUTER JOIN raum USING (raum_id); +-----------+-----------+-------------+-------------+ | raum_name | wochentag | uhrzeit_von | uhrzeit_bis | +-----------+-----------+-------------+-------------+ | A5.33 | 5 | 10:00:00 | 11:00:00 | | B2.02 | 3 | 15:35:00 | 16:20:00 | | B2.02 | 3 | 16:30:00 | 17:15:00 | | C4.16 | 4 | 17:00:00 | 17:45:00 | | A5.21 | NULL | NULL | NULL | | A5.23 | 2 | 19:15:00 | 20:00:00 | +-----------+-----------+-------------+-------------+

Viele andere Datenbank-Server kennen keine Right Outer Join (man kann ihn ja schließlich auch genauso gut durch einen Left Outer Join ersetzen), daher sollte man auch in MySQL den Left Outer Join bevorzugen, wenn man ein wenig auf Portabilität achten möchte. Vertauscht man in unserem Fall übrigens die beiden Tabellendefinitionen, erhält man das gleiche Ergebnis wie beim Inner Join, da es keinen Unterricht in einem Raum gibt, der nicht in der Tabelle eingetragen ist: mysql> SELECT raum_name, wochentag, uhrzeit_von, uhrzeit_bis -> FROM unterricht LEFT OUTER JOIN raum USING (raum_id); +-----------+-----------+-------------+-------------+ | raum_name | wochentag | uhrzeit_von | uhrzeit_bis | +-----------+-----------+-------------+-------------+ | C4.16 | 4 | 17:00:00 | 17:45:00 | | B2.02 | 3 | 15:35:00 | 16:20:00 | | B2.02 | 3 | 16:30:00 | 17:15:00 | | A5.33 | 5 | 10:00:00 | 11:00:00 | | A5.23 | 2 | 19:15:00 | 20:00:00 | +-----------+-----------+-------------+-------------+

• table_reference NATURAL [{LEFT | RIGHT} [OUTER]] JOIN table_reference

NATURAL JOIN

Der Natural Join ist eine Vereinfachung des Joins (Inner wie Outer Join): Bei ihm muss keine Verbindung zwischen den beiden Tabellen

 



Sandini Bib

angegeben werden, sei es per ON ... oder per USING (...). Es werden einfach alle Felder, die in beiden Tabellen den gleichen Namen tragen, intern in die USING-Klausel eingetragen. So entspricht also in unserem Beispiel SELECT ... FROM leihinstrument AS l INNER JOIN instrument AS i ON l.instrument_id = i.instrument_id

in der Version als Natural Join: SELECT ... FROM leihinstrument AS l NATURAL JOIN instrument AS i

Auf die gleiche Art und Weise funktioniert es auch mit Left oder Right Outer Join. Dies ist natürlich sehr bequem, allerdings verliert man damit die direkte Kontrolle darüber, welche Spalten für den Join berücksichtigt werden sollen. Es kann durchaus Fälle geben, in denen es in beiden Tabellen eine Spalte mit gleichem Namen gibt, die aber für den Join nicht genutzt werden soll (weil sie zum Beispiel unterschiedliche Werte enthält). Wenn dies von Anfang an der Fall ist, wird man natürlich keinen Natural Join nutzen (der dann schließlich auf Anhieb nicht funktionieren würde). Schlimmer wäre es aber, wenn ein Natural Join zunächst funktioniert, im Rahmen einer Strukturänderung (siehe nächstes Kapitel) aber in beiden Tabellen ein Feld gleichen Namens, aber unterschiedlichen Inhalts hinzukommt. Dann klappt es plötzlich nicht mehr mit der Verbindung und es ist meist sehr schwierig, solche Fehler zu finden.

3.9

Fragen

1. Was passiert, wenn man den Wert NULL mit einem anderen Boole-

schen Wert vergleicht? 2. Wie kann man vom (ganzzahligen) Feld meinwert der Tabelle meine-

tabelle für alle Datensätze 15 abziehen. Falls der Wert des Feldes danach unter Null liegen würde, soll er auf Null gesetzt werden. 3. Was ist der Unterschied zwischen INSERT IGNORE ... und REPLACE ...? 4. Wie kann man mit einem SELECT-Befehl herausfinden, wieviele Räume

es pro Etage gibt und wieviele Personen insgesamt darin Platz finden? 5. Erzeugen Sie eine Liste mit allen Instrumenten und den Unterrichts-

terminen dazu. Dabei sollen auch die Instrumente ausgegeben werden, für die kein Unterricht gegeben wird.



,903

Sandini Bib

4

Strukturelles

le

n e rn

Wir haben bereits im zweiten Kapitel Strukturen erzeugt, nämlich Datenbanken und Tabellen. Aber bekanntlich ändert sich vieles im Laufe der Zeit, auch die Anforderungen an eine Datenbankstruktur. Daher kommt man auch bei sorgfältigster Planung nicht umhin, Strukturen in MySQL ändern zu müssen. Zudem stellt man bei längerem Betrieb häufig fest, dass bestimmte Abfragen oft aufgerufen werden, aber leider nicht die schnellsten sind. Um solche Situationen zu entschärfen, kann man Indizes verwenden.

4.1

Was Sie in diesem Kapitel lernen

Zunächst werden wir uns mit Strukturänderungen befassen. Dabei geht es um das Hinzufügen, Ändern und Löschen von Spalten in einer bestehenden Tabelle mit ALTER TABLE. Zudem werden kurz die Gegenstücke zu CREATE DATABASE und CREATE TABLE besprochen: DROP DATABASE bzw. DROP TABLE. Im zweiten Abschnitt geht es um Indizes: wofür man sie nutzen kann, wie man sie anlegt (und löscht), und was man beachten sollte, damit die Datenbank nicht langsamer wird als vorher. Dann wollen wir uns mit den Systeminformationen von MySQL befassen, die alle Informationen über die bestehenden Strukturen der Datenbanken liefern. Schließlich gibt es noch einen Abschnitt über die Möglichkeit, die Speicherung von Tabellen zu optimieren.

:DV 6LH LQ GLHVHP .DSLWHO OHUQHQ 

Sandini Bib

4.2

Strukturänderungen

Nun haben wir eine wunderschöne Datenbank- und Tabellenstruktur. Aber natürlich stellen wir beim Arbeiten mit den Daten fest, dass noch die eine oder andere Unschönheit besteht. Daher möchten wir die Strukturen ändern. Dazu gibt es hauptsächlich einen Befehl: ALTER TABLE. Mit ihm kann man Tabellenfelder hinzufügen, entfernen, umbenennen, den Datentyp ändern, die Tabelle umbenennen, Indizes anlegen und einiges anderes tun. Die bisher eingegebenen Daten kann man wundervoll nutzen, um zum Beispiel eine Einladung zu einem Schulkonzert an alle Schüler zu schicken. Die Adressen erhält man mit: mysql> SELECT CONCAT(vorname, ' ', nachname) AS name, strasse, -> CONCAT(plz, ' ', ort) AS stadt FROM schueler -> ORDER BY stadt; +--------------------+--------------------+----------------+ | name | strasse | stadt | +--------------------+--------------------+----------------+ | Thomas Schmidt | Hauptstr. 16 | 28219 Bremen | | Friederike Schulze | Relaisstr. 432 | 41564 Kaarst | | Anke Mayer | Leher Heerstr. 342 | 68219 Mannheim | | Frank Meier | Waldweg 30 | 69190 Walldorf | +--------------------+--------------------+----------------+

Etwas fehlt aber noch, um daraus alle Daten für ein Anschreiben zu erhalten: die Anrede. Wir sehen zwar schon, ob es sich um einen Mann oder eine Frau handelt, aber der Computer weiß das nicht (und auch ein Mensch kann sich durchaus irren, zum Beispiel bei „Andrea“). Also sollten wir noch ein Feld aufnehmen, in dem das Geschlecht abgespeichert ist. Dabei reicht als Datentyp BOOL, da wir hier der Einfachheit halber nur zwischen weiblich und männlich unterscheiden wollen. ALTER TABLE

Um diese Spalte hinzuzufügen, nutzt man folgenden Befehl: ALTER TABLE schueler ADD COLUMN geschlecht BOOL;

Diese Syntax ist eigentlich selbsterklärend. Der Tabelle schueler wird eine Spalte geschlecht vom Typ BOOL spendiert. Diese landet als neue letzte Spalte in der Tabellendefinition. Kontrollieren wir dies mit SHOW COLUMNS, dessen Ergebnis wir in Abbildung 4.1 sehen.



6WUXNWXUHOOHV

Sandini Bib

mysql> SHOW COLUMNS FROM schueler; +--------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+------------------+------+-----+---------+----------------+ | schueler_id | int(10) unsigned | | PRI | NULL | auto_increment | | nachname | varchar(30) | | | | | | vorname | varchar(30) | YES | | NULL | | | geburtsdatum | date | YES | | NULL | | | strasse | varchar(30) | YES | | NULL | | | plz | varchar(6) | YES | | NULL | | | ort | varchar(40) | YES | | NULL | | | telefon | varchar(20) | YES | | NULL | | | mobil | varchar(20) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | | einzug | tinyint(1) | YES | | 0 | | | bankname | varchar(58) | YES | | NULL | | | blz | varchar(8) | YES | | NULL | | | kontonr | varchar(10) | YES | | NULL | | | kontoname | varchar(30) | YES | | NULL | | | geschlecht | tinyint(1) | YES | | NULL | | +--------------+------------------+------+-----+---------+----------------+

Abbildung 4.1: Spalten der Tabelle schueler

Am Ende der Definition findet sich unsere neue Spalte. Dass sie den Typ TINYINT(1) erhalten hat, liegt daran, dass BOOL darauf abgebildet wird. Wenn man sich nun den Inhalt der Spalte anschaut, stellt man fest, dass alle Werte in ihr NULL sind: mysql> SELECT schueler_id, nachname, vorname, geschlecht -> FROM schueler; +-------------+----------+------------+------------+ | schueler_id | nachname | vorname | geschlecht | +-------------+----------+------------+------------+ | 1 | Schmidt | Thomas | NULL | | 2 | Mayer | Anke | NULL | | 3 | Meier | Frank | NULL | | 4 | Schulze | Friederike | NULL | +-------------+----------+------------+------------+

Der Grund dafür ist, dass wir keinen Standardwert mit angegeben haben. Hätten wir die Spaltendefinition um DEFAULT 1 erweitert, würden wir diesen Wert in jedem Feld der Spalte wiederfinden. Aber dies ist gar nicht unser Ziel. Schließlich wollen wir das Geschlecht selber angeben. Wir legen einmal fest, dass die 0 für die Männer und die 1 für die Frauen steht. Dann brauchen wir zwei UPDATEs, um die Daten anzupassen: mysql> UPDATE schueler SET geschlecht = 0 WHERE schueler_id IN (1, 3); Query OK, 2 rows affected (0.14 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql> UPDATE schueler SET geschlecht = 1 WHERE schueler_id IN (2, 4); Query OK, 2 rows affected (0.01 sec) Rows matched: 2 Changed: 2 Warnings: 0

6WUXNWXUlQGHUXQJHQ



Sandini Bib

Nun können wir die entsprechende Ansprache gleich mit aufnehmen: mysql> SELECT schueler_id, CONCAT(IF(geschlecht = 0, -> 'Sehr geehrter Herr ', 'Sehr geehrte Frau '), nachname) AS anrede -> FROM schueler; +-------------+----------------------------+ | schueler_id | anrede | +-------------+----------------------------+ | 1 | Sehr geehrter Herr Schmidt | | 2 | Sehr geehrte Frau Mayer | | 3 | Sehr geehrter Herr Meier | | 4 | Sehr geehrte Frau Schulze | +-------------+----------------------------+

Wie schon erwähnt, hat der Befehl ALTER TABLE sehr viele Variationsmöglichkeiten. Daher werden wir die einzelnen Elemente im Folgenden aufführen.

4.2.1 ALTER TABLE ALTER TABLE

Die Syntax von ALTER TABLE lautet wie folgt: ALTER [IGNORE] TABLE tbl_name alter_spec [, alter_spec ...]

Einfach, nicht wahr? tbl_name ist logischerweise der Name der Tabelle, an der herumgeschraubt werden soll. IGNORE kann genutzt werden, um dafür zu sorgen, dass Probleme nicht zum Abbruch des Vorgangs führen, sondern ignoriert werden. Und wo liegt nun das Komplexe und Variantenreiche? Natürlich in der Definition von alter_spec. Diese ist ähnlich umfassend wie die Möglichkeiten bei CREATE TABLE: ADD [COLUMN] create_definition [FIRST | AFTER column_name] | ADD [COLUMN] (create_definition, create_definition, ...) | ADD {INDEX|KEY} [index_name] (index_col_name, ...) | ADD PRIMARY KEY (index_col_name, ...) | ADD UNIQUE [index_name] (index_col_name, ...) | ADD FULLTEXT [index_name] (index_col_name, ...) | ADD [CONSTRAINT symbol] FOREIGN KEY index_name (index_col_name, ...) [reference_definition] | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} | CHANGE [COLUMN] old_col_name create_definition [FIRST | AFTER column_name] | MODIFY [COLUMN] create_definition [FIRST | AFTER column_name] | DROP [COLUMN] col_name | DROP PRIMARY KEY | DROP {INDEX|KEY} index_name | RENAME [TO] new_tbl_name | ORDER BY col, ... | table_options



6WUXNWXUHOOHV

Sandini Bib

Der Reihe nach: 1. ADD [COLUMN] create_definition [FIRST | AFTER column_name]

Mit dieser Option fügen Sie der Tabelle eine Spalte hinzu. Die create_definition bietet die Möglichkeiten, die Sie auch mit CREATE TABLE besitzen. So wird zum Beispiel der Tabelle raum noch eine Spalte für die Angabe des Gebäudes hinzugefügt, die standardmäßig auf Zentrale gesetzt wird: ALTER TABLE raum ADD COLUMN gebaeude VARCHAR(10) DEFAULT 'Zentrale';

Die Angabe von COLUMN ist optional und macht den Befehl „lesbarer“, aber auch länger… Normalerweise werden neue Spalten immer hinter den bestehenden Spalten einer Tabelle angelegt. Nutzt man FIRST oder AFTER column_name, wird die neue Spalte dagegen als erste angelegt bzw. nach der bestehenden Spalte column_name eingefügt. 2. ADD [COLUMN] (create_definition, create_definition, ...)

Dies ist eine Abwandlung der vorigen Syntax. Statt einer einzigen Spalte lassen sich mehrere Spalten auf einmal anlegen. Es handelt sich eigentlich nur um eine verkürzte Version von ALTER TABLE ... ADD create_definition, ADD create_definition, ... 3. ADD {INDEX|KEY} [index_name] (index_col_name, ...)

Diese Variante fügt der Tabelle einen Index mit dem Namen index_name hinzu, der über die Spalten index_col_name, ... geht. Fehlt dieser, wird er aus dem Namen der ersten Indexspalte gebildet, eventuell ergänzt um eine Ziffer zur Unterscheidung. Siehe auch Kapitel 4.3 über Indizes. 4. ADD PRIMARY KEY (index_col_name, ...)

Hiermit lässt sich nachträglich ein Primärschlüssel für die Tabelle festlegen. Eigentlich sollte man dies ja immer direkt beim Anlegen machen, aber wenn es mal unterlassen wurde, kann man das hiermit nachholen. 5. ADD UNIQUE [index_name] (index_col_name, ...)

erstellt einen zusätzlichen eindeutigen Index. Die Syntax ist (abgesehen vom UNIQUE) identisch mit ADD INDEX. Siehe auch Kapitel 4.3 über Indizes. 6. ADD FULLTEXT [index_name] (index_col_name, ...)

legt einen Index an, über den Volltextrecherche betrieben werden kann. Die Syntax ist auch hier identisch zu ADD INDEX. Siehe auch Kapitel 4.3.3 über die Volltext-Recherche.

6WUXNWXUlQGHUXQJHQ



Sandini Bib

7. ADD [CONSTRAINT symbol] FOREIGN KEY index_name

(index_col_name,

...)

[reference_definition]

Diese Syntax ist ebenso gültig wie wirkungslos. Sie dient – wie schon ihr Äquivalent bei CREATE TABLE – nur dazu, bestehende Skripten aus anderen SQL-Servern leichter anpassen zu können. 8. ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}

Mit dieser Variante kann man der Spalte col_name entweder mit SET DEFAULT literal einen Standardwert verpassen oder mit DROP DEFAULT einen bestehenden entziehen. Entfernt man eine Standardwertdefinition, werden dabei allerdings die schon angelegten Datensätze mit Standardwert nicht geändert. 9. CHANGE [COLUMN] old_col_name create_definition [FIRST | AFTER column_name]

benennt die alte Spalte old_col_name um und ermöglicht die Angabe einer neuen Spaltendefinition (die den neuen Spaltennamen enthält). Möchte man nur den Typ einer Spalte ändern, muss man den Spaltennamen daher zweimal angeben (wobei dann die Variante mit MODIFY sinnvoller ist): ALTER TABLE mytab CHANGE COLUMN mycol mycol VARCHAR(30); FIRST und AFTER ermöglichen wieder die genaue Angabe der Position der geänderten Spalte nach der Änderung. 10. MODIFY [COLUMN] create_definition [FIRST | AFTER column_name]

ermöglicht das Ändern der Definition einer bestehenden Spalte (ohne den Namen anzupassen) 11. DROP [COLUMN] col_name

entfernt eine Spalte aus einer Tabelle 12. DROP PRIMARY KEY

löscht die Definition des Primärschlüssels. Die entsprechenden Spalten werden dabei nicht geändert, es wird nur die Schlüsselangabe gelöscht. 13. DROP {INDEX|KEY} index_name

löscht einen bestehenden Index mit dem Namen index_name. Siehe auch Kapitel 4.3 über Indizes. 14. RENAME [TO] new_tbl_name

benennt die Tabelle um und gibt ihr den Namen new_tbl_name



6WUXNWXUHOOHV

Sandini Bib

15. ZRDER BY col, ...

sortiert die Tabelle in der angegebenen Reihenfolge. Manche Abfragen und Befehle arbeiten schneller, wenn die Tabelle vorsortiert ist. Da es sich dabei um einen einmaligen Vorgang handelt, kann sich die Sortierung durch Einfügen, Ändern und Löschen von Daten schnell wieder ändern. Zudem kann man sich auch bei einer vorsortieren Tabelle nicht darauf verlassen, dass die Daten bei einer Abfrage in dieser Reihenfolge wieder ausgegeben werden. 16. table_options

Hier lassen sich die gleichen Tabellenoptionen wie bei CREATE TABLE angeben. Sie sind hier nur der Vollständigkeit halber noch mal aufgeführt, die Beschreibung ist aber dort nachzulesen. TYPE = {BDB | HEAP | ISAM | InnoDB | MERGE | MRG_MYISAM | MYISAM} | AUTO_INCREMENT = number | AVG_ROW_LENGTH = number | CHECKSUM = {0 | 1} | COMMENT = "string" | MAX_ROWS = number | MIN_ROWS = number | PACK_KEYS = {0 | 1} | PASSWORD = "string" | DELAY_KEY_WRITE = {0 | 1} | ROW_FORMAT = {default | dynamic | fixed | compressed} | RAID_TYPE = {1 | STRIPED | RAID0} RAID_CHUNKS = num_chunks RAID_CHUNKSIZE = num_size | UNION = (table_name,...) | INSERT_METHOD = {NO | FIRST | LAST} | DATA DIRECTORY = "directory" | INDEX DIRECTORY = "directory"

4.2.2 RENAME TABLE Mit diesem Befehl kann man eine Tabelle umbenennen. Seine Syntax lautet:

RENAME TABLE

RENAME TABLE tbl_name TO new_table_name[, tbl_name2 TO new_table_name2, ...]

Will man nur eine Tabelle umbenennen, hat dieser Befehl den gleichen Effekt wie ALTER TABLE table_name RENAME TO new_table_name;

Man kann aber mit RENAME TABLE auch mehrere Tabellen gleichzeitig umbenennen. Da diese Aktion in einem Schritt, also „atomar“ durchgeführt wird, gehen keinerlei Daten verloren, wenn man zum Beispiel die

6WUXNWXUlQGHUXQJHQ



Sandini Bib

bisherigen Daten in eine Sicherheitstabelle bringen will und die eigentliche Tabelle wieder leer sein soll: CREATE TABLE mytable_new (...); RENAME TABLE mytable TO mytable_backup, mytable_new TO mytable;

Damit kann direkt danach wieder auf die nun leere Tabelle zugegriffen werden. Es gibt noch zwei weitere Befehle, die in diesem Zusammenhang beschrieben werden sollten: DROP TABLE und DROP DATABASE.

4.2.3 DROP TABLE DROP TABLE

Mit diesem Befehl kann man eine oder mehrere komplette Tabellen entfernen. Es werden also nicht nur – wie bei TRUNCATE TABLE – alle Daten gelöscht, sondern auch die Tabellendefinition entsorgt. Man sollte also sehr vorsichtig damit umgehen, da es kein Zurück gibt (von Datensicherungen einmal abgesehen…). Die Syntax lautet: DROP TABLE [IF EXISTS] tbl_name [, tbl_name,...] [RESTRICT | CASCADE] IF EXISTS dient wieder dazu, Fehlermeldungen zu unterdrücken, wenn

die Tabellen gar nicht vorhanden sind. Danach folgt die Liste der zu löschenden Tabellen. RESTRICT oder CASCADE gehören dem Bereich der Fremdschlüssel an und sind daher wie in den anderen DDL-Befehlen ohne Funktion.

4.2.4 DROP DATABASE DROP DATABASE

Um eine komplette Datenbank zu löschen, nutzt man den Befehl DROP DATABASE. Es werden alle Tabellen, Daten und anderen Angaben zu dieser Datenbank gelöscht. Aus dem entsprechenden Verzeichnis werden zudem alle Dateien gelöscht, die von MySQL angelegt worden sind (oder auch nur sein können, also Vorsicht, falls man [aus welchem Grund auch immer] eigene Dateien in den Datenverzeichnissen erstellt hat, die eine von MySQL genutzte Endung tragen). Die Syntax lautet: DROP DATABASE [IF EXISTS] db_name

Auch hier dient IF EXISTS dazu, eventuelle Fehlermeldungen zu unterdrücken, falls die Datenbank nicht existiert. Mehr noch als bei DROP TABLE sollte man hier aufpassen, was man tut, da der Befehl nicht rückgängig zu machen ist (wie alle DDL-Befehle).



6WUXNWXUHOOHV

Sandini Bib

4.3

Indizes

Indizes sind ein wichtiger Bestandteil jedes Datenbank-Systems. Sie passen zwar nicht in die „reine“ Datenbank-Mengenlehre (auf der SQL ja eigentlich aufbaut), sind aber ein ausgesprochen wichtiger Baustein jeder Optimierungsbemühung. Nur durch sie lassen sich große Datenbestände in vernünftiger Zeit durchsuchen.

4.3.1 Sinn und Funktionsweise von Indizes Wie findet MySQL Daten in einer Tabelle? Die Daten sind (normalerweise) ungeordnet. Im Prinzip muss es die Tabelle immer vom Anfang an durchsuchen, bis es den passenden Satz findet. Selbst wenn man sie mit ALTER TABLE ... ORDER BY ... ordnet, muss die Suche für ein Feld, welches nicht in dieser Sortierreihenfolge ganz vorne steht, über die gesamte Tabelle gehen. Geht man davon aus, dass sich der gesuchte Datensatz an einer beliebigen Stelle in der Tabelle befindet (mal ganz am Anfang, mal in der Mitte, mal am Ende), muss man im Schnitt die Hälfte der Datensätze durchsuchen, bis der erste Satz gefunden wurde. Da man bei SQL aber mengenorientiert vorgeht, muss immer die komplette Tabelle durchforstet werden, um alle betroffenen Datensätze zu erhalten. Dies ist bei kleinen Datenmengen kein Problem. Aber was, wenn in einer Tabelle hunderttausende Sätze eingetragen sind, auf die auch noch häufig zugegriffen werden muss. Ohne eine Unterstützung bei der Suche würden die Abfragen sehr lange dauern und der Server wäre ziemlich ausgelastet. Nun ist es meistens so, dass bestimmte Abfragen häufig vorkommen. In einer Kundentabelle sucht man meist nach dem Namen, aber auch ab und zu nach PLZ und Ort (für Mailingaktionen). Wäre es da nicht praktisch, wenn es eine vorsortierte Liste mit den Namen gäbe, die man sehr schnell durchsuchen könnte? Dazu eine weitere Liste, die nach PLZ und Ort sortiert ist? Natürlich sind solche Listen sinnvoll, wenn sie automatisch gepflegt werden und der Geschwindigkeitssteigerung dienen. Dies ist der Hintergrund eines Indizes. Definiert man einen Index über ein Feld, wird neben der eigentlichen Tabelle eine Datenstruktur angelegt, in der nur dieses Feld (bzw. der Anfangsteil davon) sortiert abgelegt ist. Jeder Eintrag enthält einen Verweis auf den echten Datensatz in der eigentlichen Tabelle. Werden neue Daten eingefügt, bestehende geändert oder Daten gelöscht, wird auch die Indexstruktur angepasst, so dass sie immer auf dem gleichen Stand wie die Tabelle (zumindest bezüglich des Indexfeldes), aber immer auch sortiert ist.

,QGL]HV



Sandini Bib

Kommt nun eine Anfrage über dieses Feld, wird zunächst in der Indexstruktur nachgeschaut. Aufgrund der Sortierung ist das Nachschlagen sehr schnell. MySQL nimmt sich dabei das Element, welches in der Mitte der Indexstruktur liegt. Entspricht der Wert dem gesuchten, ist man fertig. Ist er zu groß, nimmt man sich die „linken“ Elemente vor, ist er zu klein, die rechten. Nun hat man die Zahl der Elemente schon halbiert und wiederholt mit diesen das gleiche Spiel: mittleres Element holen, untersuchen, und entweder gefunden haben oder den linken/ rechten Teilbereich untersuchen. So hat man nach spätestens log2 n Versuchen (bei n Datensätzen) den passenden Datensatz gefunden. Diese Zahl ist bei steigender Anzahl von Datensätzen viel kleiner (siehe Tabelle 4.1) und sorgt dafür, dass der Index so schnell ist. n

log2 n

10

3,3

100

6,6

1.000

10,0

10.000

13,3

100.000

16,6

1.000.000

19,9

Tabelle 4.1: Anzahl Suchvorgänge bei einer Binärsuche

Wie man sieht, reduziert sich die Anzahl der Vergleichsschritte massiv und macht sich besonders bei umfangreichen Datenmengen bemerkbar. Solch eine Suche wird als Binärsuche bezeichnet. MySQL selbst (wie auch alle anderen „richtigen“ Datenbank-Systeme) hat noch ausgefeiltere Techniken, die aber meist auf dem gleichen Prinzip beruhen. Nun muss man sich bei einem Index nicht auf ein einzelnes Feld beschränken. Man kann mehrere angeben, die einfach hintereinander abgespeichert werden. Sucht man zum Beispiel regelmäßig nach Nachname und Vorname, kann man einen Index mit diesen beiden Feldern anlegen. In der Indexstruktur stehen die Werte dann zusammengefügt als ein Wert. Zudem lassen sich auch für eine Tabelle mehrere Indizes anlegen, die unterschiedliche Felder enthalten. Wann wird ein solcher Index verwendet? Es gibt verschiedene Einsatzgebiete, die aber alle darauf hinauslaufen, dass Werte verglichen werden: • In einer WHERE-Klausel. Findet sich ein Feld (oder auch mehrere) aus der WHERE-Klausel in einem passenden Index, wird dieser verwendet. So lassen sich Abfragen wie die folgende massiv beschleunigen: SELECT * FROM schueler WHERE nachname = 'Schmidt';



6WUXNWXUHOOHV

Sandini Bib

Nutzt man LIKE, muss man berücksichtigen, dass ein Index nur für den Teil verwendet werden kann, der vor dem ersten Platzhalter (% oder _) steht. Auch Funktionen sorgen dafür, dass ein Index nicht wirksam werden kann. Die folgende Abfrage ist also nicht durch einen Index unterstützt, auch wenn auf dem Feld groesse ein Index liegt: SELECT * FROM kunden WHERE POWER(groesse, 2) > 1000;

Stattdessen sollte man wann immer möglich versuchen, Funktionen bei Suchen auf Literale anzuwenden (falls die Abfrage zu langsam ist), damit ein Index wirken kann. SELECT * FROM kunden WHERE groesse > SQRT(1000);

• Beim Verknüpfen von Tabellen per Join. In diesem Fall werden meistens Schlüssel miteinander verglichen. Wenn eine Tabelle einen Primärschlüssel hat, existiert dafür automatisch ein Index. Dann sollte man nur noch Indizes für die Fremdschlüssel anlegen. • Wenn man den maximalen oder minimalen Wert eines Feldes sucht. Dabei kann MySQL in bestimmten Fällen noch weiter optimieren. • Wenn man eine Abfrage sortiert oder gruppiert. Dabei ist allerdings zu beachten, dass die Sortierreihenfolge der Reihenfolge der Felder im Index entsprechen sollte, damit er komplett wirksam wird. Allerdings wird auch ein Teil eines Index genutzt. Existiert zum Beispiel ein Index über die Felder nachname, vorname und ort in dieser Reihenfolge und man hat eine Abfrage SELECT * FROM schueler ORDER BY nachname, ort, vorname;

dann wird der Index zwar verwendet, kann aber nur für das Feld nachname genutzt werden. • In bestimmten Fällen ist durch einen Index der Zugriff auf die eigentliche Tabelle vollkommen überflüssig und wird automatisch wegoptimiert. Dies ist dann der Fall, wenn im SELECT-Befehl nur Felder vorkommen, die auch im Index existieren. Bei einem angenommenen Index über nachname, vorname und ort auf die Tabelle schueler würde die folgende Abfrage diese Optimierung zulassen: SELECT nachname, vorname FROM schueler WHERE ort = 'Bremen';

• Wenn eine Spalte nur eindeutige Werte enthalten soll, wird dies durch einen Index sichergestellt. Dies kann durch UNIQUE oder PRIMARY KEY erfolgen. Nach all diesen tollen Vorteilen, die ein Index bringt, wollen wir auch nicht dessen dunkle Seite verschweigen: Er kostet Zeit, wenn man Daten anlegt, ändert oder löscht. Gibt es keinen Index, muss nur die Tabelle selber angepasst werden. Existieren aber Indizes für eine Tabelle, müssen

,QGL]HV



Sandini Bib

eventuell alle Indizes aktualisiert werden, was durchaus (bei vielen Indizes) länger dauern kann als das Eintragen in die Tabelle selber. Daher die Faustregel: nur die Indizes anlegen, die wirklich häufig gebraucht werden. Ein weiterer Tipp: falls man viele Daten auf einmal anlegen oder löschen will, kann es Sinn machen, die Indizes zunächst zu löschen, dann die Änderungen vorzunehmen und am Ende die Indizes wieder anzulegen. Denn der Aufwand für das Anlegen eines Index über 100.000 Datensätze ist meist geringer als der zusätzliche Zeitaufwand, der durch das Pflegen der Indexdaten für 100.000 einzelne Änderungen entsteht. Dies sollte man natürlich nur bei wirklich großen Aktionen machen (sogenannten Bulk-Loads). In die gleiche Richtung noch ein ähnlicher Tipp: Muss man innerhalb einer kürzeren Zeit viele ähnliche Abfragen machen, die aber nach diesem Zeitraum nicht mehr vorkommen, kann man einen Index seiner Wahl anlegen, die Abfragen machen und ihn danach wieder löschen. Auch dies ist bei entsprechenden Datenmengen und ansonsten ungünstigen bestehenden Indizes ein häufig schnellerer Weg als die vielen Abfragen ohne den Index. Nach diesem langen Vorlauf soll es nun aber endlich an das (recht unspektakuläre) Anlegen von Indizes gehen.

4.3.2 Anlegen von Indizes Es gibt mehrere Möglichkeiten, Indizes anzulegen: entweder direkt beim Erstellen der Tabelle mit CREATE TABLE, durch Nutzung einer der vielen Optionen von ALTER TABLE oder ganz klassisch mit CREATE INDEX. Ich werde sie hier der Reihe nach besprechen. Anlegen eines Index per CREATE TABLE CREATE TABLE

Will man Indizes gleich zusammen mit der Tabelle anlegen, bietet sich CREATE TABLE an. Dabei gibt man statt einer Spaltendefinition eine der folgenden Zeilen an: PRIMARY KEY (index_col_name,...) KEY [index_name] (index_col_name,...) INDEX [index_name] (index_col_name,...) UNIQUE [INDEX] [index_name] (index_col_name,...)

Mit der ersten Möglichkeit legt man einen Primärschlüssel fest. In Klammern werden die einzelnen Felder angegeben, die den Primärschlüssel bilden sollen. Handelt es sich nur um ein einzelnes Feld, kann man stattdessen auch bei der entsprechenden Spaltendefinition die Option PRIMARY KEY angeben. So bewirken also die folgenden Befehle dasselbe:



6WUXNWXUHOOHV

Sandini Bib

CREATE TABLE raum (raum_id SMALLINT UNSIGNED raum_name VARCHAR(10) etage CHAR(3), personen MEDIUMINT UNSIGNED CREATE TABLE raum (raum_id SMALLINT UNSIGNED raum_name VARCHAR(10) etage CHAR(3), personen MEDIUMINT UNSIGNED PRIMARY KEY (raum_id));

NOT NULL AUTO_INCREMENT PRIMARY KEY, NOT NULL, DEFAULT 1); NOT NULL AUTO_INCREMENT, NOT NULL, DEFAULT 1,

Beide Varianten haben ihre Vorteile: bei der ersten sieht man direkt bei der entsprechenden Spalte, dass es sich um den Primärschlüssel handelt (dort steht schließlich auch die Option AUTO_INCREMENT), bei der zweiten Variante kann man dagegen alle Schlüsseldefinitionen (und PRIMARY KEY gehört genauso dazu) an einer Stelle zusammenfassen. Man sollte allerdings daran denken, dass die erste Variante nur funktioniert, wenn ein Feld alleine den Primärschlüssel darstellt. Die zweite und dritte Version (KEY und INDEX) sind – abgesehen vom Schlüsselwort – identisch. Man kann optional einen Namen für den Index vergeben. Fehlt diese Angabe, wird der Name aus der ersten angegebenen Indexspalte gebildet, eventuell ergänzt um eine fortlaufende Ziffer, um die Eindeutigkeit des Namens zu wahren. Es ist aber immer empfehlenswert, den Indizes sinnvolle Namen zu geben, möglichst sogar ein Konzept für die Indexnamen zu haben (zum Beispiel idx_schueler_name). Es folgt die Liste der Spalten, die den Index in der angegebenen Reihenfolge bilden sollen. Ein Beispiel dafür ist: CREATE TABLE schueler (schueler_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, nachname VARCHAR(30) NOT NULL, vorname VARCHAR(30), geburtsdatum DATE, strasse VARCHAR(30), plz CHAR(6), ort VARCHAR(40), telefon VARCHAR(20), mobil VARCHAR(20), email VARCHAR(50), einzug BOOL DEFAULT 0, bankname VARCHAR(58), blz CHAR(8), kontonr CHAR(10), kontoname VARCHAR(30), INDEX idx_schueler_name (nachname, vorname), INDEX idx_schueler_wohn (plz, ort));

,QGL]HV



Sandini Bib

Möchte man bei einem String-Feld (CHAR oder VARCHAR) nur einen Teil des Feldes für den Index nutzen, kann man beim Spaltennamen in Klammern noch die gewünschte Länge angeben. Das macht zum Beispiel dann Sinn, wenn sich die meisten Daten schon in den ersten Zeichen deutlich unterscheiden und eine komplette Aufnahme in den Index nur Platz kosten würde. Möchte man zum Beispiel einen Index auf PLZ und Ort anlegen (wie schon im vorigen Beispiel geschehen), kann man vom Ort auch nur die ersten fünf Zeichen nehmen, da die PLZ meistens sowieso schon ausreicht, um einen Ort eindeutig zu finden (aber eben nicht immer…): CREATE TABLE schueler (..., INDEX idx_schueler_wohn (plz, ort(5));

Die vierte Variante ist der eindeutige Index. Er sorgt genauso wie ein Primärschlüssel dafür, dass die Spaltenkombination eindeutig ist, d.h. keine zwei Felder (oder Feldkombinationen bei mehreren Spalten) den gleichen Wert besitzen. Das kann dann von Nutzen sein, wenn man neben dem Primärschlüssel auch noch einen „sprechenden“ Schlüssel besitzt, der ebenso eindeutig sein soll. Ein Beispiel dafür wäre eine Verwaltung von Benutzern. Deren Anmeldenamen müssen auch eindeutig sein, aber trotzdem sollte man sie nicht unbedingt direkt als Primärschlüssel verwenden. Es kann schließlich sein, dass der Name einmal geändert werden soll. Hat man dann Tabellen, die von der Benutzertabelle abhängig sind, müsste man in allen den Namen ebenfalls ändern. Daher bietet sich in solchen Fällen ein eigener Primärschlüssel und ein Anmeldename mit eindeutigem Index an: CREATE TABLE benutzer (benutzer_id INT UNSIGNED AUTO_INCREMENT, benutzer_name VARCHAR(20), ..., PRIMARY KEY (benutzer_id), UNIQUE INDEX idx_benutzer_name (benutzer));

Anlegen eines Index per ALTER TABLE ALTER TABLE

Hat man die Tabelle schon definiert, möchte aber trotzdem noch zusätzlich Indizes definieren, kann man auch den Befehl ALTER TABLE nutzen. Dabei stehen einem die gleichen Optionen wie bei CREATE TABLE zur Verfügung, allerdings ergänzt durch ALTER TABLE raum ADD INDEX idx_raum_etage (etage);



6WUXNWXUHOOHV

Sandini Bib

Die einzelnen Optionen lauten also: ADD ADD ADD ADD

INDEX [index_name] (index_col_name,...) KEY [index_name] (index_col_name,...) PRIMARY KEY (index_col_name,...) UNIQUE [index_name] (index_col_name,...)

Anlegen eines Index per CREATE INDEX Schließlich bleibt einem noch die Möglichkeit, Indizes per CREATE INDEX für eine bestehende Tabelle anzulegen. Die Syntax dazu lautet:

CREATE INDEX

CREATE [UNIQUE|FULLTEXT] INDEX index_name ON tbl_name (col_name, ...)

Der Indexname ist in diesem Fall nicht optional. Allerdings sollte er – wie schon erwähnt – eigentlich immer mit angegeben werden. Eindeutige Indizes werden mit der Option UNIQUE angelegt. Primärschlüssel kann man mit diesem Befehl nicht erstellen, aber die sollten eigentlich sowieso direkt beim Anlegen der Tabelle angegeben werden. FULLTEXT dient dazu, einen Volltextindex anzulegen. Diese werden in Ka-

pitel 4.3.4 besprochen.

4.3.3 Anzeigen von Indizes Möchte man sich anzeigen lassen, welche Indizes mit welchen Feldern für eine Tabelle definiert wurden, kann man den Befehl SHOW INDEX nutzen:

SHOW INDEX

SHOW INDEX FROM table_name;

Dabei wird eine recht umfangreiche Liste mit Informationen ausgegeben (siehe Abbildung 4.2). mysql> SHOW INDEX FROM schueler; +----------+------------+-------------------+--------------+ | Table | Seq_in_index | | Non_unique | Key_name +----------+------------+-------------------+--------------+ | schueler | 0 | PRIMARY | 1 | | schueler | 1 | 1 | idx_schueler_name | | schueler | 2 | 1 | idx_schueler_name | | schueler | 1 | 1 | idx_schueler_wohn | 2 | 1 | idx_schueler_wohn | | schueler | +----------+------------+-------------------+--------------+ Fortsetzung +-----------+-------------+----------+--------+---------+ | Collation | Cardinality | Sub_part | Packed | Comment | +-----------+-------------+----------+--------+---------+ | A | 4 | NULL | NULL | | | A | | 4 | NULL | NULL | | A | | 4 | NULL | NULL | | A | 4 | NULL | NULL | | | A | 4 | 5 | NULL | | +-----------+-------------+----------+--------+---------+ Abbildung 4.2: Ausgabe des Befehls SHOW INDEX

,QGL]HV



Sandini Bib

Die Bedeutung der einzelnen Spalten ist in Tabelle 4.2 beschrieben. Spalte

Beschreibung

Table

Name der Tabelle, für die der Index definiert wurde

Non_unique

0: Index ist eindeutig, 1: Index ist nicht eindeutig

Key_name

Name des Index (PRIMARY, wenn Primärschlüssel)

Seq_in_index

Position der Spalte im Index

Column_name

Name der Spalte, die im Index aufgenommen ist

Collation

Sortierreihenfolge dieser Spalte. Kann nur A (aufsteigend) oder NULL (unsortiert) sein.

Cardinality

Anzahl der eindeutigen Werte in diesem Index. Diese Zahl basiert auf Statistiken und muss gerade bei kleinen Tabellen nicht exakt dem tatsächlichen Wert entsprechen, den man mit SELECT COUNT(DISTINCT ...) erhalten würde.

Sub_part

Anzahl der Zeichen, die bei einer nur teilweise aufgenommenen Spalte verwendet werden sollen. Ist die Spalte komplett enthalten, steht hier NULL.

Packed

Angabe, ob der Index gepackt ist oder nicht

Comment

Platz für verschiedene Angaben. Zur Zeit steht hier nur, ob es sich um einen Volltextindex handelt.

Tabelle 4.2: Bedeutung der Spalten in SHOW INDEX

Zum Thema Kardinalität noch eine kurze Anmerkung: Ist diese in einer Spalte nicht sehr groß (im Vergleich zur Gesamtzahl an Werten), ist ein Index nicht sehr effektiv. Anders herum: je unterschiedlicher die Werte in einer Spalte sind, desto besser kann ein Index arbeiten. So macht es selbst in einer Tabelle mit 10.000 Ansprechpartnern bei Kunden keinen Sinn, auf ein eventuell vorhandenes Feld geschlecht zu indizieren, da dessen Kardinalität im Allgemeinen bei 2 liegt (nur im medizinischen Bereich machen vier unterschiedliche Werte Sinn: männlich, weiblich, anderes, und unbekannt, aber selbst dann ist der Index ineffektiv).

4.3.4 Volltext-Recherche Mit MySQL lassen sich auch Volltextrecherchen durchführen. Dazu legt man für die gewünschten Spalten einen Volltextindex an und nutzt einen speziellen Suchoperator, der nicht nur die Datensätze mit den passenden Wörtern findet, sondern auch noch eine Relevanz bestimmt, um möglichst die passendsten Datensätze zuerst zu bringen. Um Beispiele für die Volltextrecherche zu bringen, müssen wir allerdings ein wenig von unserem Musikschul-Szenario abkommen, da sich dort kein richtig passendes Beispiel bietet. Wir werden stattdessen eine Tabelle mit Textinhalten erzeugen und füllen:



6WUXNWXUHOOHV

Sandini Bib

CREATE TABLE fulltexttest (chapter VARCHAR(10), title VARCHAR(50), content TEXT, FULLTEXT (title, content));

In diese Tabelle tragen wir nun noch Werte ein.

FULLTEXT

Die entsprechenden Befehle dazu finden sich auf der beiliegenden CDROM im Verzeichnis Beispiel in der Datei fulltext.sql. Um nun eine Volltextrecherche durchführen zu können, nutzen wir den Operator MATCH AGAINST. Er findet alle Vorkommen des Suchwortes in den gewünschten Spalten und gibt die Datensätze in der Reihenfolge der Relevanz aus. Die Syntax lautet: MATCH (column [, column, ...]) AGAINST ('value')

Man kann mehrere Spalten zum Durchsuchen angeben, die allerdings alle im Volltextindex aufgeführt sein müssen. Zudem müssen sie (falls man in der Abfrage Joins nutzt) aus einer Tabelle kommen. value ist ein Stringwert, der aber konstant sein muss (d.h., man kann nicht gegen den Inhalt einer anderen Spalte vergleichen lassen). Ein Beispiel: mysql> SELECT chapter, title, LEFT(content, 19) -> FROM fulltexttest -> WHERE MATCH (title, content) AGAINST ('MySQL'); +---------+--------------------------------------+---------------------+ | chapter | title | LEFT(content, 19) | +---------+--------------------------------------+---------------------+ | 1.6 | MySQL | Jetzt haben wir uns | | 1.6.4 | Lizenzen und Kosten bei MySQL | Man sagt immer, MyS | | 1.6.1 | Was ist MySQL | MySQL ist ein relat | | 1.6.2 | Die Geschichte und Idee hinter MySQL | Die Entwickler von | | 1.6.3 | Elemente von MySQL | MySQL besteht vor a | | 1 | Datenbanken | Was ist das - eine | +---------+--------------------------------------+---------------------+

Datensätze, in denen das Wort nicht vorkommt, erhalten eine Relevanz von 0 und werden daher nicht angezeigt. Wie groß ist nun die Relevanz? Dazu dient der gleiche Operator, hier als Funktion zum Ausgeben: mysql> SELECT chapter, -> MATCH (title, content) AGAINST ('MySQL') AS relevance -> FROM fulltexttest;

,QGL]HV



Sandini Bib

+---------+------------------+ | chapter | relevance | +---------+------------------+ | 1 | 0.4164290528623 | | 1.1 | 0 | | 1.2 | 0 | | 1.3 | 0 | | 1.4 | 0 | | 1.4.1 | 0 | | 1.4.2 | 0 | | 1.4.3 | 0 | | 1.4.4 | 0 | | 1.4.5 | 0 | | 1.5 | 0 | | 1.6 | 0.84725140746008 | | 1.6.1 | 0.68482611338062 | | 1.6.2 | 0.67439976592822 | | 1.6.3 | 0.66891743559618 | | 1.6.4 | 0.78651414891569 | +---------+------------------+

Wie man sieht, gibt es unterschiedlich große Relevanzen und auch genug Kapitel, die nicht relevant sind. Die Sortierung ist hier nicht vorhanden, da wir MATCH AGAINST nicht in der WHERE-Klausel verwendet haben. Man kann dies aber gleichzeitig tun: mysql> SELECT chapter, -> MATCH (title, content) AGAINST ('MySQL') AS relevance -> FROM fulltexttest -> WHERE MATCH (title, content) AGAINST ('MySQL'); +---------+------------------+ | chapter | relevance | +---------+------------------+ | 1.6 | 0.84725140746008 | | 1.6.4 | 0.78651414891569 | | 1.6.1 | 0.68482611338062 | | 1.6.2 | 0.67439976592822 | | 1.6.3 | 0.66891743559618 | | 1 | 0.4164290528623 | +---------+------------------+

Nun erhält man einerseits die Relevanz sortiert nach der Größe, andererseits aber auch nur die Datensätze, die überhaupt relevant sind. Dass im obigen SQL-Befehl MATCH AGAINST gleich zweimal vorkommt, ist nicht weiter schlimm, da der MySQL-interne Optimierer erkennt, dass es sich bei beiden um den gleichen Aufruf handelt.



6WUXNWXUHOOHV

Sandini Bib

Es gilt bei der Volltextrecherche noch ein paar Punkte zu beachten: • Man kann nicht nach Worten suchen, die kürzer als vier Buchstaben sind oder sich in der Liste mit Stopp-Wörtern befinden. Beides kann man ändern, allerdings nur, indem man MySQL neu kompiliert. • Die Relevanz berechnet sich aus der Häufigkeit der Wörter in der Menge der Datensätze. Je seltener sie vorkommen, desto größer ist ihre Wichtung, aus der sich dann die Relevanz bestimmt. • Kommt ein Wort in mehr als der Hälfte aller Datensätze vor, wird seine Wichtung auf 0 gesetzt, da es dann nicht mehr sinnvoll zur Filterung genutzt werden kann (was man allerdings auch durch ein neues Kompilieren von MySQL ändern kann). • Die Berechnung der Relevanz funktioniert am besten bei großen Datenmengen. Kleine Tabellen können zu seltsamen Ergebnissen führen.

4.3.5 Löschen von Indizes Um Indizes zu löschen, gibt es wieder zwei verschiedene Wege. Löschen eines Index mit ALTER TABLE Um einen Index mit ALTER TABLE wieder zu löschen, kann man eine der folgenden Optionen nutzen: DROP PRIMARY KEY DROP INDEX index_name DROP KEY index_name

Mit ALTER TABLE ... DROP PRIMARY KEY wird logischerweise der Primärschlüssel gelöscht. ALTER TABLE ... DROP INDEX ... oder ALTER TABLE ... DROP KEY ... löscht den entsprechenden Index, egal ob es sich um einen normalen, eindeutigen oder Volltext-Index handelt. Löschen eines Index mit DROP INDEX Die zweite Variante ist DROP INDEX:

DROP INDEX

DROP INDEX index_name ON table_name

Mit diesem Befehl kann man alle Indizes löschen, die angelegt wurden. Primärschlüssel allerdings kann man nur mit ALTER TABLE ... DROP PRIMARY KEY löschen.

,QGL]HV



Sandini Bib

4.4 SHOW

Systeminformationen

Der zentrale Befehl zum Anzeigen von Systeminformationen ist SHOW. Ihn gibt es in diversen Ausführungen, manche von ihnen haben wir schon kennengelernt. Folgende Befehle sind möglich: SHOW DATABASES [LIKE filter] SHOW TABLES [FROM db_name] [LIKE filter] SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE filter] SHOW FIELDS FROM tbl_name [FROM db_name] [LIKE filter] DESC[RIBE] tbl_name [filter] SHOW INDEX FROM tbl_name [FROM db_name] SHOW KEYS FROM tbl_name [FROM db_name] SHOW CREATE TABLE tbl_name SHOW OPEN TABLES [FROM db_name] [LIKE filter] SHOW TABLE STATUS [FROM db_name] [LIKE filter] SHOW STATUS [LIKE filter] SHOW VARIABLES [LIKE filter] SHOW LOGS SHOW [FULL] PROCESSLIST SHOW GRANTS FOR user SHOW MASTER STATUS SHOW SLAVE STATUS SHOW MASTER LOGS

4.4.1 Anzeigen von Strukturdaten Die folgenden Befehle dienen dazu, Informationen über angelegte Strukturen auszugeben. Dabei handelt es sich um Datenbanken, Tabellen, Spalten und Indizes. SHOW DATABASES

1. SHOW DATABASES [LIKE filter]

Mit diesem Befehl kann man sich alle Datenbanken des MySQLServers ausgeben lassen, mit dem man verbunden ist. Als Ergebnis erhält man eine einfache Liste: mysql> SHOW DATABASES; +-------------+ | Database | +-------------+ | musikschule | | mysql | | test | +-------------+



6WUXNWXUHOOHV

Sandini Bib

Mit LIKE filter lässt sich diese Liste eingrenzen. Dabei sind die üblichen Platzhalter aus der LIKE-Syntax nutzbar (% und _): mysql> SHOW DATABASES LIKE 'm%'; +---------------+ | Database (m%) | +---------------+ | musikschule | | mysql | +---------------+ 2. SHOW TABLES [FROM db_name] [LIKE filter]

SHOW TABLES

Auf diesem Weg lassen sich alle Tabellen der aktuell verwendeten Datenbank anzeigen: mysql> SHOW TABLES; +-----------------------+ | Tables_in_musikschule | +-----------------------+ | ausleihe | | instrument | | lehrer | | leihinstrument | | orchester | | orchester_tn | | raum | | schueler | | unterricht | | unterricht_tn | | warteliste | +-----------------------+

Auch hier kann man mit LIKE filter die Ausgabe wieder eingrenzen: mysql> SHOW TABLES LIKE 'l%'; +----------------------------+ | Tables_in_musikschule (l%) | +----------------------------+ | lehrer | | leihinstrument | +----------------------------+

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

Nutzt man FROM db_name, kann man sich auch die Tabellen einer anderen Datenbank ausgeben lassen: mysql> SHOW TABLES FROM mysql; +-----------------+ | Tables_in_mysql | +-----------------+ | columns_priv | | db | | func | | host | | tables_priv | | user | +-----------------+ SHOW COLUMNS

3. SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE filter]

SHOW FIELDS FROM tbl_name [FROM db_name] [LIKE filter] SHOW COLUMNS bzw. SHOW FIELDS geben Informationen über die Spalten ei-

ner Tabelle aus. Ein Beispiel dafür ist am Anfang des Kapitels in Abbildung 4.1 zu sehen. Nutzt man die Option FULL (nur bei SHOW COLUMNS möglich), werden für jede Spalte auch die Rechte mit ausgegeben, die man daran besitzt. Dies ist für eine Mehrbenutzerverwaltung notwendig und wird im übernächsten Kapitel beim Thema Datenschutz besprochen. Ein Beispiel für eine solche Ausgabe ist in Abbildung 4.3 zu sehen. mysql> SHOW FULL COLUMNS FROM schueler; +--------------+------------------+------+-----+---------+----------------+---------------------------------+ | Field | Type | Null | Key | Default | Extra | Privileges | +--------------+------------------+------+-----+---------+----------------+---------------------------------+ | schueler_id | int(10) unsigned | | PRI | NULL | auto_increment | select,insert,update,references | | nachname | varchar(30) | | MUL | | | select,insert,update,references | | vorname | varchar(30) | YES | | NULL | | select,insert,update,references | | geburtsdatum | date | YES | | NULL | | select,insert,update,references | | strasse | varchar(30) | YES | | NULL | | select,insert,update,references | | plz | varchar(6) | YES | MUL | NULL | | select,insert,update,references | | ort | varchar(40) | YES | | NULL | | select,insert,update,references | | telefon | varchar(20) | YES | | NULL | | select,insert,update,references | | mobil | varchar(20) | YES | | NULL | | select,insert,update,references | | email | varchar(50) | YES | | NULL | | select,insert,update,references | | einzug | tinyint(1) | YES | | 0 | | select,insert,update,references | | bankname | varchar(58) | YES | | NULL | | select,insert,update,references | | blz | varchar(8) | YES | | NULL | | select,insert,update,references | | kontonr | varchar(10) | YES | | NULL | | select,insert,update,references | | kontoname | varchar(30) | YES | | NULL | | select,insert,update,references | | geschlecht | tinyint(1) | YES | | NULL | | select,insert,update,references | +--------------+------------------+------+-----+---------+----------------+---------------------------------+

Abbildung 4.3: Ausgabe von SHOW FULL COLUMNS

Die Spalten Field und Type sind sicherlich klar. Null gibt an, ob in einem Feld NULL-Werte erlaubt sind oder nicht. Key zeigt an, ob es sich um einen Primärschlüssel (PRI) oder einen anderen Schlüssel handelt (MUL). In Default ist festgelegt, welcher Wert standardmäßig eingetragen wird (wobei NULL genutzt wird, wenn keine anderen Angaben gemacht wurden). Extra zeigt weitere Optionen des Feldes an (zum Beispiel, ob es sich um ein Autoincrement-Feld handelt). Die Privileges schließlich geben an, welche Berechtigungen man besitzt (siehe übernächstes Kapitel).



6WUXNWXUHOOHV

Sandini Bib

Auch bei diesen Befehlen kann man mit LIKE die auszugebenden Felder eingrenzen: mysql> SHOW FIELDS FROM schueler LIKE 'g%'; +--------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------------+------------+------+-----+---------+-------+ | geburtsdatum | date | YES | | NULL | | | geschlecht | tinyint(1) | YES | | NULL | | +--------------+------------+------+-----+---------+-------+

Und genau wie bei SHOW TABLES lässt sich mit FROM db_name auf Tabellen in einer anderen Datenbank zugreifen: mysql> SHOW COLUMNS FROM user FROM mysql; +-----------------+-----------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+-----------------+------+-----+---------+-------+ | Host | char(60) binary | | PRI | | | | User | char(16) binary | | PRI | | | | Password | char(16) binary | | | | | | Select_priv | enum('N','Y') | | | N | | | Insert_priv | enum('N','Y') | | | N | | | Update_priv | enum('N','Y') | | | N | | | Delete_priv | enum('N','Y') | | | N | | | Create_priv | enum('N','Y') | | | N | | | Drop_priv | enum('N','Y') | | | N | | | Reload_priv | enum('N','Y') | | | N | | | Shutdown_priv | enum('N','Y') | | | N | | | Process_priv | enum('N','Y') | | | N | | | File_priv | enum('N','Y') | | | N | | | Grant_priv | enum('N','Y') | | | N | | | References_priv | enum('N','Y') | | | N | | | Index_priv | enum('N','Y') | | | N | | | Alter_priv | enum('N','Y') | | | N | | +-----------------+-----------------+------+-----+---------+-------+

Alternativ zu tbl_name FROM db_name kann man auch die im SELECT-Befehl übliche Syntax db_name.tbl_name nutzen: mysql> SHOW COLUMNS FROM mysql.host;

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

+-----------------+-----------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+-----------------+------+-----+---------+-------+ | Host | char(60) binary | | PRI | | | | Db | char(64) binary | | PRI | | | | Select_priv | enum('N','Y') | | | N | | | Insert_priv | enum('N','Y') | | | N | | | Update_priv | enum('N','Y') | | | N | | | Delete_priv | enum('N','Y') | | | N | | | Create_priv | enum('N','Y') | | | N | | | Drop_priv | enum('N','Y') | | | N | | | Grant_priv | enum('N','Y') | | | N | | | References_priv | enum('N','Y') | | | N | | | Index_priv | enum('N','Y') | | | N | | | Alter_priv | enum('N','Y') | | | N | | +-----------------+-----------------+------+-----+---------+-------+ DESCRIBE

4. DESC[RIBE] tbl_name [filter]

Dieser Befehl entspricht einem SHOW COLUMNS FROM tbl_name [LIKE filter] und wurde eingeführt, um Oracle-Nutzern die Umstellung zu erleichtern. DESC ist eine Kurzform für DESCRIBE, die Filter funktionieren bei beiden Varianten wie bei SHOW COLUMNS: mysql> DESCRIBE unterricht 'uhrzeit%'; +-------------+------+------+-----+----------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+------+------+-----+----------+-------+ | uhrzeit_von | time | | | 00:00:00 | | | uhrzeit_bis | time | | | 00:00:00 | | +-------------+------+------+-----+----------+-------+

Um auf die Tabellen einer anderen Datenbank zuzugreifen, muss man mit der Punkt-Syntax (db_name.tbl_name) arbeiten, DESCRIBE tbl_name FROM db_name funktioniert nicht. SHOW INDEX

5. SHOW INDEX FROM tbl_name [FROM db_name]

SHOW KEYS FROM tbl_name [FROM db_name]

Dieser Befehl wurde weiter oben in Kapitel 4.3.3 schon beschrieben. SHOW KEYS ist äquivalent zu SHOW INDEX und der Zugriff auf Tabellen aus anderen Datenbanken funktioniert genauso wie bei den anderen SHOW-Befehlen. SHOW CREATE TABLE



6. SHOW CREATE TABLE tbl_name

Dieser Befehl gibt einen CREATE TABLE-Befehl aus, mit dem die Tabelle tbl_name neu angelegt werden kann. Die Ausgabe enthält nur zwei Spalten: Table und Create Table. Allerdings ist die zweite Spalte für sehr lange Werte ausgelegt, so dass sie nicht sinnvoll auf dem Bild-

6WUXNWXUHOOHV

Sandini Bib

schirm zu nutzen ist. Zudem enthält diese Spalte Zeilenumbrüche. Hier macht eine Ausgabeoption vom Clientprogramm mysql Sinn: Anstatt einen Befehl mit einem Semikolon abzuschließen, gibt man am Ende \G ein (mit großen G). Das hat zur Folge, dass die einzelnen Spalten nicht mehr nebeneinander, sondern untereinander ausgegeben werden: mysql> SHOW CREATE TABLE schueler\G *************************** 1. row *************************** Table: schueler Create Table: CREATE TABLE `schueler` ( `schueler_id` int(10) unsigned NOT NULL auto_increment, `nachname` varchar(30) NOT NULL default '', `vorname` varchar(30) default NULL, `geburtsdatum` date default NULL, `strasse` varchar(30) default NULL, `plz` varchar(6) default NULL, `ort` varchar(40) default NULL, `telefon` varchar(20) default NULL, `mobil` varchar(20) default NULL, `email` varchar(50) default NULL, `einzug` tinyint(1) default '0', `bankname` varchar(58) default NULL, `blz` varchar(8) default NULL, `kontonr` varchar(10) default NULL, `kontoname` varchar(30) default NULL, `geschlecht` tinyint(1) default NULL, PRIMARY KEY (`schueler_id`), KEY `idx_schueler_name` (`nachname`,`vorname`), KEY `idx_schueler_wohn` (`plz`,`ort`(5)) ) TYPE=MyISAM

Bei dieser Form der Anzeige wird erst die Zeilennummer ausgegeben (1. row), dann der Spaltenname und Feldinhalt, jeweils pro Spalte durch einen Zeilenumbruch getrennt. In dieser Form ließe sich der CREATE TABLE-Befehl leicht übernehmen. Wie man sieht, sind die Spaltennamen durch Anführungszeichen eingeschlossen, um nicht bei „seltsamen“ Spaltennamen ins Schleudern zu kommen. Stört einen diese Ausgabe, kann man vorher folgenden Befehl eingeben: mysql> SET SQL_QUOTE_SHOW_CREATE = 0;

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

Damit werden die Anführungszeichen unterdrückt: mysql> SHOW CREATE TABLE schueler\G *************************** 1. row *************************** Table: schueler Create Table: CREATE TABLE schueler ( schueler_id int(10) unsigned NOT NULL auto_increment, nachname varchar(30) NOT NULL default '', vorname varchar(30) default NULL, geburtsdatum date default NULL, strasse varchar(30) default NULL, plz varchar(6) default NULL, ort varchar(40) default NULL, telefon varchar(20) default NULL, mobil varchar(20) default NULL, email varchar(50) default NULL, einzug tinyint(1) default '0', bankname varchar(58) default NULL, blz varchar(8) default NULL, kontonr varchar(10) default NULL, kontoname varchar(30) default NULL, geschlecht tinyint(1) default NULL, PRIMARY KEY (schueler_id), KEY idx_schueler_name (nachname,vorname), KEY idx_schueler_wohn (plz,ort(5)) ) TYPE=MyISAM

Dies kann allerdings zu Problemen führen, wenn die Spaltennamen mit definierten MySQL-Befehlen übereinstimmen. Aktiviert werden die Anführungszeichen wieder mit: mysql> SET SQL_QUOTE_SHOW_CREATE = 1;

Die Ausgabe mit \G kann man übrigens auch bei ganz normalen Tabellen nutzen: mysql> SELECT * FROM schueler\G *************************** 1. row *************************** schueler_id: 1 nachname: Schmidt vorname: Thomas geburtsdatum: 1992-06-09 strasse: Hauptstr. 16 plz: 28219 ort: Bremen telefon: 0421/1123456 mobil: NULL email: NULL



6WUXNWXUHOOHV

Sandini Bib

einzug: 0 bankname: NULL blz: NULL kontonr: NULL kontoname: NULL geschlecht: 0 *************************** 2. row *************************** schueler_id: 2 nachname: Mayer vorname: Anke geburtsdatum: 1971-04-24 strasse: Leher Heerstr. 342 plz: 68219 ort: Mannheim telefon: 0621/1129875 mobil: 0171/9998765 email: [email protected] einzug: 1 bankname: SEB AG Mannheim blz: 11122233 kontonr: 9998846 kontoname: Hans Mayer geschlecht: 1 *************************** 3. row *************************** schueler_id: 3 nachname: Meier vorname: Frank geburtsdatum: 1980-07-31 strasse: Waldweg 30 plz: 69190 ort: Walldorf telefon: 06227/1124565 mobil: NULL email: [email protected] einzug: 1 bankname: Sparkasse Rhein Neckar Nord blz: 1112234 kontonr: 9997755 kontoname: NULL geschlecht: 0 *************************** 4. row *************************** schueler_id: 4 nachname: Schulze vorname: Friederike geburtsdatum: 1990-01-07 strasse: Relaisstr. 432

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

plz: ort: telefon: mobil: email: einzug: bankname: blz: kontonr: kontoname: geschlecht:

41564 Kaarst 02131/1125464 NULL [email protected] 0 NULL NULL NULL NULL 1

4.4.2 Anzeigen weiterer Informationen Mit SHOW lassen sich neben den Strukturinformationen auch noch andere Parameter von Tabellen und dem MySQL-Server recherchieren: SHOW OPEN TABLES

1. SHOW OPEN TABLES [FROM db_name] [LIKE filter]

Mit diesem Befehl kann man sich die Tabellen der aktuellen Datenbank anzeigen lassen, die momentan im Tabellencache zwischengespeichert sind. Dieser Cache ist ein Speicherbereich im RAM, in dem Tabelleninhalte aufbewahrt werden, wenn gerade mit ihnen gearbeitet wurde. Da es sehr wahrscheinlich ist, dass die zuletzt verwendeten Tabellen erneut genutzt wird, lässt sich auf diesem Weg der Zugriff deutlich beschleunigen. Im Feld Comment ist angegeben, wie oft die entsprechende Tabelle zwischengespeichert ist und wie oft sie sich gerade in Verwendung befindet: mysql> SHOW OPEN TABLES; +----------------------------+--------------------+ | Open_tables_in_musikschule | Comment | +----------------------------+--------------------+ | lehrer | cached=1, in_use=0 | | schueler | cached=1, in_use=0 | +----------------------------+--------------------+

Mit dem Befehl FLUSH TABLES und seinen Derivaten kann man diesen Cache leeren, falls man das Gefühl hat, dass ein „Neuanfang“ aus Performancegründen besser wäre: mysql> FLUSH TABLES; Query OK, 0 rows affected (0.02 sec) mysql> SHOW OPEN TABLES; Empty set (0.00 sec)



6WUXNWXUHOOHV

Sandini Bib

Mit FROM db_name lassen sich die Tabellen der entsprechenden Datenbank anzeigen und mit LIKE filter kann man die Ausgabe wie üblich einschränken: mysql> SHOW OPEN TABLES FROM test LIKE 'my%'; +---------------------------+--------------------+ | Open_tables_in_test (my%) | Comment | +---------------------------+--------------------+ | mytable | cached=1, in_use=0 | +---------------------------+--------------------+ 2. SHOW TABLE STATUS [FROM db_name] [LIKE filter]

SHOW TABLE

Dieser Befehl zeigt Informationen über verschiedene Tabellenparameter an. Wie in Abbildung 4.4 zu sehen, handelt es sich um eine ganze Reihe von Spalten, daher nutze ich zur Demonstration noch einmal die Variante mit \G für eine einzelne Tabelle:

STATUS

mysql> SHOW TABLE STATUS LIKE 'sch%'\G *************************** 1. row *************************** Name: schueler Type: MyISAM Row_format: Dynamic Rows: 4 Avg_row_length: 117 Data_length: 468 Max_data_length: 4294967295 Index_length: 4096 Data_free: 0 Auto_increment: 5 Create_time: 2002-09-28 21:50:55 Update_time: 2002-09-28 21:50:55 Check_time: 2002-09-28 21:50:55 Create_options: Comment:

Die einzelnen Spalten sind in Tabelle 4.3 erläutert. Spalte

Beschreibung

Name

Name der Tabelle

Type

Tabellentyp (MyISAM, InnoDB, …)

Row_format

Art der Datensatzspeicherung: Fixed, Dynamic oder Compressed (abhängig von den Spaltentypen und Tabellenoptionen)

Rows

Anzahl der Datensätze

Avg_row_length

durchschnittliche Datensatzlänge

Data_length

Größe der Datendatei

Tabelle 4.3: Spalten bei der Ausgabe von SHOW TABLE STATUS

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

Spalte

Beschreibung

Max_data_length

maximal mögliche Größe der Datendatei

Index_length

Größe der Indexdatei

Data_free

Anzahl der reservierten, aber nicht genutzten Bytes

Auto_increment

nächster Wert beim Verwenden des AUTO_INCREMENT-Mechanismus

Create_time

Erstellungszeitpunkt der Tabelle

Update_time

Zeitpunkt der letzten Änderung an der Tabelle

Check_time

Zeitpunkt der letzten Tabellenprüfung

Create_options

Optionen, die bei CREATE TABLE angegeben wurden

Comment

Kommentar, der beim Erstellen der Tabelle angegeben wurde

Tabelle 4.3: Spalten bei der Ausgabe von SHOW TABLE STATUS (Forts.)

mysql> SHOW TABLE STATUS; +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+ | Name | Type | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+ | ausleihe | MyISAM | Fixed | 0 | 0 | 0 | 81604378623 | 1024 | 0 | 8 | 29 | | instrument 236 | 4294967295 | | MyISAM | Dynamic 2048 | | 0 | | lehrer | MyISAM | Dynamic | 4 | 124 | 496 | 4294967295 | 2048 | 0 | | 4 | 34 | | leihinstrument | MyISAM | Dynamic 136 | 4294967295 | 2048 | 0 | | 0 | 0 | | orchester 0 | 4294967295 | | MyISAM | Dynamic 1024 | 0 | 1024 | 0 | 0 | 0 | 73014444031 | | 0 | | orchester_tn | MyISAM | Fixed | 5 | 20 | | raum 100 | 4294967295 | | MyISAM | Dynamic 3072 | 0 | | 4 | 117 | | schueler 468 | | MyISAM | Dynamic 4294967295 | 4096 | 0 | 2048 | | 5 | 0 | 31 | | unterricht 155 | 133143986175 | | MyISAM | Fixed 2048 | | 4 | 0 | 13 | | unterricht_tn | MyISAM | Fixed 52 | 55834574847 | 1024 | | 0 | 0 | 0 | | warteliste 0 | 68719476735 | | MyISAM | Fixed +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+

Fortsetzung +----------------+---------------------+---------------------+---------------------+----------------+---------+ | Update_time | Check_time | Auto_increment | Create_time | Create_options | Comment | +----------------+---------------------+---------------------+---------------------+----------------+---------+ | | 1 | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | NULL | | | | 9 | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | NULL | | | | 5 | 2002-09-19 21:49:54 | 2002-09-21 17:00:18 | NULL | | | | 5 | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | NULL | | | | 1 | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | NULL | | | | 1 | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | NULL | | | | | 6 | 2002-09-24 21:38:22 | 2002-09-24 21:38:22 | 2002-09-24 21:38:22 | | | | 5 | 2002-09-28 21:50:55 | 2002-09-28 21:50:55 | 2002-09-28 21:50:55 | | | 6 | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | NULL | | | | 5 | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | NULL | | | | 1 | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | NULL | | +----------------+---------------------+---------------------+---------------------+----------------+---------+

Abbildung 4.4: Ausgabe des Befehls SHOW TABLE STATUS

Auch bei diesem kann man mit FROM db_name die Datenbank bestimmen, für die die Tabelleninformationen ausgegeben werden sollen (Standard ist die aktuelle Datenbank). Und mit LIKE filter lässt sich ebenso die Ausgabe auf bestimmte Tabellennamen beschränken. SHOW STATUS

3. SHOW STATUS [LIKE filter]

Dieser Befehl gibt diverse Informationen über den aktuellen Stand des MySQL-Servers aus. Die Ausgabe ist wie üblich eine einfache Tabelle, die auch noch mit LIKE filter eingeschränkt werden kann. Die einzelnen Zeilen sollen nicht genauer beschrieben werden, aber es gibt Informationen über Verbindungsabbrüche, Netzwerknutzung, verwendete temporäre Tabellen, Lese- und Schreibanforderungen, verwendete Datei- und Tabellenhandler, Joins, Sortiervorgänge und Threads:



6WUXNWXUHOOHV

Sandini Bib

mysql> SHOW STATUS; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | Aborted_clients | 0 | | Aborted_connects | 0 | | Bytes_received | 0 | | Bytes_sent | 0 | | Com_admin_commands | 0 | | Com_alter_table | 0 | | Com_analyze | 0 | | Com_backup_table | 0 | | Com_begin | 0 | | Com_change_db | 3 | | Com_change_master | 0 | | Com_check | 0 | | Com_commit | 0 | | Com_create_db | 0 | | Com_create_function | 0 | | Com_create_index | 0 | | Com_create_table | 0 | | Com_delete | 0 | | Com_drop_db | 0 | | Com_drop_function | 0 | | Com_drop_index | 0 | | Com_drop_table | 0 | | Com_flush | 1 | | Com_grant | 0 | | Com_insert | 1 | | Com_insert_select | 0 | | Com_kill | 0 | | Com_load | 0 | | Com_load_master_table | 0 | | Com_lock_tables | 0 | | Com_optimize | 0 | | Com_purge | 0 | | Com_rename_table | 0 | | Com_repair | 0 | | Com_replace | 0 | | Com_replace_select | 0 | | Com_reset | 0 | | Com_restore_table | 0 | | Com_revoke | 0 | | Com_rollback | 0 | | Com_select | 6 | | Com_set_option | 0 |

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |



Com_show_binlogs Com_show_create Com_show_databases Com_show_fields Com_show_grants Com_show_keys Com_show_logs Com_show_master_status Com_show_open_tables Com_show_processlist Com_show_slave_status Com_show_status Com_show_tables Com_show_variables Com_slave_start Com_slave_stop Com_truncate Com_unlock_tables Com_update Connections Created_tmp_disk_tables Created_tmp_tables Created_tmp_files Delayed_insert_threads Delayed_writes Delayed_errors Flush_commands Handler_delete Handler_read_first Handler_read_key Handler_read_next Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_update Handler_write Key_blocks_used Key_read_requests Key_reads Key_write_requests Key_writes Max_used_connections Not_flushed_key_blocks Not_flushed_delayed_rows Open_tables Open_files

6WUXNWXUHOOHV

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |

0 0 0 1 0 0 0 0 8 0 0 2 7 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 1 0 0 0 0 45 0 2 0 0 0 0 0 0 0 0 12 24

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |

Sandini Bib

| Open_streams | 0 | | Opened_tables | 32 | | Questions | 31 | | Select_full_join | 0 | | Select_full_range_join | 0 | | Select_range | 0 | | Select_range_check | 0 | | Select_scan | 6 | | Slave_running | OFF | | Slave_open_temp_tables | 0 | | Slow_launch_threads | 0 | | Slow_queries | 0 | | Sort_merge_passes | 0 | | Sort_range | 0 | | Sort_rows | 0 | | Sort_scan | 0 | | Table_locks_immediate | 48 | | Table_locks_waited | 0 | | Threads_cached | 0 | | Threads_created | 1 | | Threads_connected | 1 | | Threads_running | 1 | | Uptime | 12030 | +--------------------------+-------+ 4. SHOW VARIABLES [LIKE filter]

SHOW VARIABLES

Während SHOW STATUS Informationen über den aktuellen Gemütszustand des MySQL-Servers ausgibt, kann man sich mit SHOW VARIABLES die Startoptionen und Systemvariablen von MySQL anzeigen lassen. Diese Werte lassen sich im Allgemeinen per Kommandozeilenoption mysqld mitgeben oder in den entsprechenden Konfigurationsdateien angeben, falls einem die Standardwerte nicht helfen. Auch bei SHOW VARIABLES kann man mit LIKE filter die Ausgabe einschränken. In der folgenden Ausgabe wurde allerdings die Zeile character_sets etwas umformatiert, um die Ausgabe noch halbwegs im Buch unterbringen zu können… +---------------------------------+---------------------------+ | Variable_name | Value | +---------------------------------+---------------------------+ | back_log | 50 | | basedir | d:\mysql\ | | binlog_cache_size | 32768 | | character_set | german1 | | character_sets | latin1 big5 czech euc_kr | | | gb2312 gbk sjis tis620 |

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |



concurrent_insert connect_timeout datadir delay_key_write delayed_insert_limit delayed_insert_timeout delayed_queue_size flush flush_time have_bdb have_gemini have_innodb have_isam have_raid have_openssl init_file interactive_timeout join_buffer_size key_buffer_size language large_files_support log log_update log_bin log_slave_updates log_long_queries long_query_time low_priority_updates lower_case_table_names max_allowed_packet max_binlog_cache_size max_binlog_size max_connections max_connect_errors max_delayed_threads max_heap_table_size max_join_size max_sort_length max_user_connections

6WUXNWXUHOOHV

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |

ujis dec8 dos german1 hp8 koi8_ru latin2 swe7 usa7 cp1251 danish hebrew win1251 estonia hungarian koi8_ukr win1251ukr greek win1250 croat cp1257 latin5 ON 5 d:\mysql\data\ ON 100 300 1000 OFF 1800 NO NO NO YES NO NO 28800 131072 16773120 d:\mysql\share\english\ ON OFF OFF OFF OFF OFF 10 OFF 1 1047552 4294967295 1073741824 100 10 20 16777216 4294967295 1024 0

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |

Sandini Bib

| max_tmp_tables | 32 | | max_write_lock_count | 4294967295 | | myisam_max_extra_sort_file_size | 256 | | myisam_max_sort_file_size | 2047 | | myisam_recover_options | 0 | | myisam_sort_buffer_size | 8388608 | | named_pipe | OFF | | net_buffer_length | 7168 | | net_read_timeout | 30 | | net_retry_count | 10 | | net_write_timeout | 60 | | open_files_limit | 0 | | pid_file | d:\mysql\data\kruemel.pid | | port | 3306 | | protocol_version | 10 | | record_buffer | 131072 | | record_rnd_buffer | 131072 | | query_buffer_size | 0 | | safe_show_database | OFF | | server_id | 1 | | slave_net_timeout | 3600 | | skip_locking | ON | | skip_networking | OFF | | skip_show_database | OFF | | slow_launch_time | 2 | | socket | MySQL | | sort_buffer | 524280 | | sql_mode | 0 | | table_cache | 64 | | table_type | MYISAM | | thread_cache_size | 0 | | thread_stack | 65536 | | transaction_isolation | READ-COMMITTED | | timezone | Westeuropäische Sommerzeit| | tmp_table_size | 33554432 | | tmpdir | D:\WINNT\TEMP\ | | version | 3.23.51-nt | | wait_timeout | 28800 | +---------------------------------+---------------------------+ 5. SHOW LOGS

SHOW LOGS

Dieser Befehl gibt Daten zu Protokolldateien aus, die allerdings nur bei Datenbanken vom Typ „Berkley DB“ vorhanden sind. Ansonsten wird man keine Daten erhalten.

6\VWHPLQIRUPDWLRQHQ



Sandini Bib

SHOW PROCESSLIST

6. SHOW [FULL] PROCESSLIST

Mit SHOW PROCESSLIST lassen sich die aktuell laufenden Prozesse des MySQL-Servers anzeigen. Hat man die Berechtigung process, sieht man alle Prozesse, ansonsten nur die eigenen. (Zu Berechtigungen siehe späteres Kapitel.) Normalerweise werden nur die ersten 100 Zeichen eines Prozesses ausgegeben. Will man die komplette Liste sehen, muss man SHOW FULL PROCESSLIST nutzen. Dieser Befehl kann nützlich sein, wenn der MySQL-Server keine Anfragen mehr annimmt und man sehen möchte, was gerade auf dem Server los ist. Zu diesem Zweck kann auch immer ein Client diese Abfrage ausführen. Ist gerade nichts auf dem Server los, wird man nur die eigene Abfrage sehen (siehe Abbildung 4.5) mysql> SHOW PROCESSLIST; +----+------+-----------+-------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+-------------+---------+------+-------+------------------+ | 1 | ODBC | localhost | musikschule | Query | 0 | NULL | SHOW PROCESSLIST | +----+------+-----------+-------------+---------+------+-------+------------------+

Abbildung 4.5: Ausgabe des Befehls SHOW PROCESSLIST SHOW GRANTS

7. SHOW GRANTS FOR user

Dieser Befehl gibt die Befehle aus, die man angeben muss, um die gleichen Berechtigungen zu erhalten wie die von user. Wir werden uns im späteren Kapitel über Berechtigungen genauer damit befassen. SHOW MASTER/SLAVE

8. SHOW MASTER STATUS

SHOW SLAVE STATUS SHOW MASTER LOGS

Mit diesen Befehlen kann man sich Informationen über den Replikationsstatus ausgeben lassen, wenn man die Replikationsmechanismen von MySQL nutzt. Damit kann man Daten von einem Server auf andere verteilen, um zum Beispiel die Zugriffslast zu reduzieren oder die Zugriffsgeschwindigkeit zu erhöhen. Der Bereich Replikation soll hier aber nicht genauer beschrieben werden.



6WUXNWXUHOOHV

Sandini Bib

4.5

Optimieren von Tabellen

Es gibt zwei Befehle, die dabei helfen, schneller auf Tabelleninhalte zugreifen zu können. OPTIMIZE TABLE ordnet die Daten neu auf der Festplatte, während ANALYZE TABLE die Verteilung der Schlüsselwerte untersucht und die Statistiken über die Tabelle aktualisiert.

4.5.1 OPTIMIZE TABLE Mit diesem Befehl kann man dafür sorgen, dass die Tabelleninhalte auf der Festplatte möglichst sinnvoll und platzsparend gespeichert werden. Die Syntax ist ganz einfach:

OPTIMIZE TABLE

OPTIMIZE TABLE tbl_name [, tbl_name, ...]

Wie man sieht, kann man mehrere Tabellen gleichzeitig angeben, die dann nacheinander optimiert werden. Beim Optimieren wird zunächst der Platz von gelöschten Datensätzen freigegeben und die bestehenden Datensätze zusammenhängend abgespeichert. Aufgeteilte Daten werden wieder zusammengeführt. Sind die Speicherbereiche für die Indizes nicht sortiert abgelegt, wird dies nun durchgeführt. Wenn zudem die Statistiken der Tabellen nicht mehr aktuell sind, werden diese auf den neuesten Stand gebracht. Als Ausgabe erhält man eine Tabelle mit einer Übersicht über die bearbeiteten Tabellen und den Status: mysql> OPTIMIZE TABLE schueler; +----------------------+----------+----------+----------+ | Table | Op | Msg_type | Msg_text | +----------------------+----------+----------+----------+ | musikschule.schueler | optimize | status | OK | +----------------------+----------+----------+----------+

Man sollte OPTIMIZE TABLE dann benutzen, wenn viele Datensätze gelöscht oder in einer Tabelle mit variablen Spalten (VARCHAR usw.) eine Menge Datensätze geändert wurden. In diesem Fall kann es sein, dass viel Speicher fragmentiert ist und nicht richtig wiederverwertet werden kann. Durch die Optimierung der Tabelle wird dieser Speicher wieder zusammenhängend freigegeben.

  



Sandini Bib

4.5.2 ANALYZE TABLE ANALYZE TABLE

ANALYZE TABLE dient dazu, die Statistiken für eine Tabelle zu aktualisieren. Die Syntax ist ebenso einfach wie bei OPTIMIZE TABLE: ANALYZE TABLE tbl_name [, tbl_name, ...]

Auch hier können wieder mehrere Tabellen nacheinander abgearbeitet werden. Bei jeder Tabelle wird die Verteilung der Schlüsselwerte untersucht und abgespeichert. Mit diesen Informationen kann MySQL beim Verknüpfen von Tabellen feststellen, auf welche Weise es vorgehen sollte, um möglichst schnell ein Ergebnis liefern zu können. Auch ANALYZE TABLE liefert eine Tabelle mit Informationen zurück (siehe Abbildung 4.6). mysql> ANALYZE TABLE instrument, lehrer; +------------------------+---------+----------+-----------------------------+ | Table | Op | Msg_type | Msg_text | +------------------------+---------+----------+-----------------------------+ | musikschule.instrument | analyze | status | Table is already up to date | | musikschule.lehrer | analyze | status | OK | +------------------------+---------+----------+-----------------------------+

Abbildung 4.6: Ausgabe von ANALYZE TABLE

4.6

Fragen

1. Wie kann man nach dem Anlegen einer Tabelle dafür sorgen, dass

eine Spalte nur eindeutige Werte enthält? 2. Wo kann man feststellen, wieviele unterschiedliche Werte es in einer

indizierten Spalte gibt? 3. Auf welchem Weg lässt sich der CREATE TABLE-Befehl für eine Tabel-

le rekonstruieren? 4. Warum sollte man Indizes nur für die häufigsten Abfragen erstellen?



6WUXNWXUHOOHV

Sandini Bib

5

Zugriffsmöglichkeiten

le

n e rn

Man kann auf vielerlei Wegen auf die Daten eines MySQL-Servers zugreifen. Bisher haben wir immer das Client-Programm mysql genutzt. Natürlich ist das aber kein sinnvoller Weg für Endbenutzer. Für diese Gruppe schreibt man im Allgemeinen ein eigenes Programm, welches über eine der angebotenen Schnittstellen mit dem MySQL-Server kommuniziert. Zudem gibt es einige Programme, die einen Administrator bei seiner Arbeit mit dem MySQL-Server unterstützen, sei es grafisch oder textuell ausgerichtet. Um die Daten „am Stück“ aus dem Server heraus- und in einen anderen wieder hineinzubekommen, kann man auch verschiedene Wege beschreiten.

5.1

Was Sie in diesem Kapitel lernen

Zunächst wollen wir uns mit den verschiedenen Tools auseinandersetzen, die Zugriff auf den MySQL-Server bzw. die von ihm verwalteten Daten gewähren. Das wichtigste ist dabei natürlich mysql, es gibt aber noch einige weitere, die eher der Administration dienen. Danach werden wir phpMyAdmin vorstellen, ein von Dritten programmiertes Tool, das dem Benutzer eine grafische Oberfläche präsentieren kann, welche den Dialog mit dem Server hübscher verpackt als dies Textprogramme ermöglichen (welche dafür natürlich andere Vorteile haben). Zudem läuft es unter einer Web-Oberfläche und kann somit mit jedem Browser bedient werden. Zum Dritten wollen wir uns mit dem Im- und Export von Daten kümmern. Damit kann man Datenbanken von einem zum anderen Rechner verschieben, aber auch Daten aus anderen Quellen in MySQL importieren.

:DV 6LH LQ GLHVHP .DSLWHO OHUQHQ 

Sandini Bib

Schließlich befassen wir uns mit den Programmierschnittstellen von MySQL. Man kann aus den verschiedensten Programmiersprachen heraus auf den Server zugreifen, aber auch sprachunabhängige Schnittstellen wie ODBC nutzen.

5.2

Admin-Tools zu MySQL

Mit MySQL werden schon viele Tools direkt mitgeliefert. Diese sollen dem Administrator die Arbeit mit dem MySQL-Server erleichtern. Das am häufigsten verwendete dürfte natürlich mysql sein. Wir haben die ganze Zeit damit gearbeitet und werden hier noch ein paar ergänzende Möglichkeiten erwähnen. Weiterhin stehen für den Zugriff auf den MySQL-Server weitere Tools bereit. Im Folgenden werden wir mysqladmin und mysqlshow besprechen. Die Tools mysqldump und mysqlimport werden wir detaillierter in Kapitel 5.4 behandeln, da sie zum Im- und Exportieren von Daten genutzt werden. mysqlhotcopy dient zum Sichern von Daten und wird im nächsten Kapitel besprochen, ebenso wie myisamchk und mysqlcheck, welche zum Prüfen und Reparieren von Tabellen und Datenbanken dienen.

5.2.1 mysql mysql ist das Tool, mit dem man vermutlich außerhalb des eigentlichen Programms für die Endbenutzer am häufigsten arbeiten wird. Wir haben schon sehr viel damit gemacht und werden hier nur noch auf einige weitere Punkte eingehen, die das Leben einfacher machen können. TEE / NOTEE TEE

Mit dem Befehl TEE kann man dafür sorgen, dass die Ausgabe neben dem Bildschirm gleichzeitig auch in eine Datei geschrieben wird. Als Parameter gibt man den Pfad und Namen einer Datei an. Diese Datei wird als Ziel genommen, um die Ein- und Ausgaben, die man auch auf dem Bildschirm sieht, abzuspeichern. Dabei wird immer an das Ende der Datei angehängt, so dass auch nach einem Neustart die alten Daten nicht verloren sind. Mit NOTEE beendet man das Protokollieren. Ein Beispiel für eine solche Log-Datei wäre: Logging to file 'd:/temp/test.txt' mysql> SELECT * FROM raum;



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

+---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | | 4 | A5.21 | 5 | 6 | | 5 | A5.23 | 5 | 6 | +---------+-----------+-------+----------+ 5 rows in set (0.00 sec) mysql> UPDATE schueler SET nachname = 'Meier' WHERE schueler_id = 11; Query OK, 0 rows affected (0.02 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> NOTEE

Direkt vor dem „Logging to file…“ wurde der Befehl TEE d:/temp/test.txt aufgerufen (übrigens wieder ohne Semikolon). Ruft man nach einem NOTEE den Befehl TEE erneut auf, ohne aber einen Dateinamen angegeben zu haben, wird in die letzte Datei protokolliert, die verwendet wurde. STATUS Mit diesem Befehl kann man sich Statusinformationen über den Server, den Client und die Verbindung anzeigen lassen. Auch er wird ohne Semikolon aufgerufen:

STATUS

mysql> STATUS -------------D:\mysql\bin\mysql.exe Ver 11.18 Distrib 3.23.51, for Win95/Win98 (i32) Connection id: Current database: Current user: Server version: Protocol version: Connection: Client characterset: Server characterset: TCP port: Uptime:

1 musikschule ODBC@localhost 3.23.51-nt 10 localhost via TCP/IP german1 german1 3306 12 hours 37 min 35 sec

Threads: 1 Questions: 66 Slow queries: 0 Opens: 24 Flush tables: 1 Open tables: 2 Queries per second avg: 0.001 --------------

 



Sandini Bib

Zunächst folgt eine Angabe des aktuellen Programms mit seinem Stand und der Distribution. Die Connection id gibt dann die Kennung an, über die das Client-Programm aktuell mit dem Server verbunden ist. Sie kann genutzt werden, um unter anderem mit mysqladmin Clients „abzuschießen“, die sich aufgehängt haben oder anderen Ärger bereiten. Die Current database gibt natürlich an, welche Datenbank gerade ausgewählt ist, und der Current user zeigt, wer man gerade ist. Server version und Protocol version geben Informationen über den Stand des Servers und die Protokollversion. Connection sagt, wie man mit dem Server verbunden ist, Client characterset und Server characterset informieren über den genutzten Zeichensatz, der TCP port steht für den Port, über den der MySQL-Server kommuniziert und die Uptime steht für die Laufzeit, die der Server schon ohne Unterbrechung läuft. Am Ende finden sich noch ein paar Informationen zur Statistik. Umleiten der Ein- und Ausgabe Redirection

Hat man mehrere Befehle in einer festen Reihenfolge zu verarbeiten, kann man diese in einer Textdatei speichern (gerne mit der Endung .sql) und entweder in mysql mit dem Befehl SOURCE ausführen lassen (wie schon weiter oben behandelt), oder direkt beim Aufruf per Umleitung der Eingabe mitgeben. Speichern wir zum Beispiel die folgenden Befehle in einer Datei namens mycommand.sql: SHOW DATABASES; USE musikschule; SELECT * FROM raum; SELECT * FROM instrument;

Wenn man nun mysql diese Datei als Eingabe mitgibt, erhält man folgende Ausgabe: prompt>mysql < mycommands.sql Database musikschule mysql test raum_id raum_name etage 1 A5.33 5 4 2 B2.02 2 20 3 C4.16 4 11 4 A5.21 5 6 5 A5.23 5 6



=XJULIIVP|JOLFKNHLWHQ

personen

Sandini Bib

instrument_id instr_name instr_gruppe 1 Querflöte Holzbläser 2 Klarinette Holzbläser 3 Violine Streicher 4 Viola Streicher 5 Posaune Blechbläser 6 Trompete Blechbläser 7 Klavier Tasten 8 Keyboard Tasten

Wie man sieht, werden keine weiteren Informationen neben den Daten selber und den Spaltenüberschriften mitgegeben. Getrennt werden die Daten mit dem Tabulatorzeichen. Dieses Ausgabeformat wird dann verwendet, wenn MySQL im sogenannten Batch-Modus läuft. Das Verhalten lässt sich auch erzwingen oder verhindern, dazu benötigt man die im nächsten Abschnitt beschriebenen Kommandozeilen-Parameter. Die Ausgabe lässt sich natürlich auch direkt in eine Datei umleiten. Das erreicht man mit folgendem Aufruf: prompt>mysql < mycommands.sql > myresult.txt

Kommandozeilen-Parameter Es gibt (neben -h, -u und -p) einige Parameter, die man beim Starten von mysql angeben kann und die in manchen Situationen helfen können. Fünf möchte ich hier aufführen, es gibt aber noch diverse mehr. • -? oder --help Dieser Parameter dürfte der wichtigste sein… Er gibt die möglichen Kommandozeilen-Parameter aus und beendet das Programm dann wieder. Das Ergebnis sehen Sie hier: prompt>mysql -? mysql Ver 11.18 Distrib 3.23.51, for Win95/Win98 (i32) Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPL license Usage: mysql [OPTIONS] [database] -?, --help Display this help and exit. -A, --no-auto-rehash No automatic rehashing. One has to use 'rehash' to get table and field completion. This gives a quicker start of mysql and disables rehashing on reconnect. -B, --batch Print results with a tab as separator, each row on a new line. Doesn't use history file.

 



Sandini Bib

--character-sets-dir=... Directory where character sets are located. -C, --compress Use compression in server/client protocol. -D, --database=.. Database to use. --default-character-set=... Set the default character set. -e, --execute=... Execute command and quit. (Output like with --batch) -E, --vertical Print the output of a query (rows) vertically. -f, --force Continue even if we get an sql error. -g, --no-named-commands Named commands are disabled. Use \* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. -G, --enable-named-commands Named commands are enabled. Opposite to -g. -i, --ignore-spaces Ignore spaces after function names. -h, --host=... Connect to host. -H, --html Produce HTML output. --local-infile=[1|0] Enable/disable LOAD DATA LOCAL INFILE -L, --skip-line-numbers Don't write line number for errors. --no-tee Disable outfile. See interactive help (\h) also. -n, --unbuffered Flush buffer after each query. -N, --skip-column-names Don't write column names in results. -O, --set-variable var=option Give a variable an value. --help lists variables. -o, --one-database Only update the default database. This is useful for skipping updates to other database in the update log. -p[password], --password[=...] Password to use when connecting to server If password is not given it's asked from the tty. -W, --pipe Use named pipes to connect to server -P, --port=... -q, --quick

-r, --raw



Port number to use for connection. Don't cache result, print it row by row. This may slow down the server if the output is suspended. Doesn't use history file. Write fields without conversion. Used with --batch

=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

-s, --silent -S --socket=... -t, --table -T, --debug-info --tee=... -u, -U, -v, -V, -w,

Be more silent. Socket file to use for connection. Output in table format. Print some debug info at exit. Append everything into outfile. See interactive help (\h) also. Does not work in batch mode. --user=# User for login if not current user. --safe-updates[=#], --i-am-a-dummy[=#] Only allow UPDATE and DELETE that uses keys. --verbose Write more. (-v -v -v gives the table output format) --version Output version information and exit. --wait Wait and retry if connection is down.

Default options are read from the following files in the given order: D:\WINNT\my.ini C:\my.cnf The following groups are read: mysql client The following options may be given as the first argument: --print-defaults Print the program argument list and exit --no-defaults Don't read default options from any options file --defaults-file=# Only read default options from the given file # --defaults-extra-file=# Read this file after the global files are read Possible variables for option connect_timeout current max_allowed_packet current net_buffer_length current select_limit current max_join_size current

--set-variable (-O) are: value: 0 value: 16777216 value: 16384 value: 1000 value: 1000000

• -e oder --execute Mit diesem Parameter kann man einzelne Befehle ausführen lassen. Das Programm wird dann direkt danach beendet. Der Befehl muss in Anführungszeichen stehen. Damit kann man sich einzelne Tabelleninhalte oder andere Informationen ausgeben lassen, ohne erst aufwändiger eine Befehlsdatei zu erstellen. Ein Beispiel dafür ist: prompt>mysql -e "SELECT * FROM raum;" musikschule +---------+-----------+-------+----------+ | raum_id | raum_name | etage | personen | +---------+-----------+-------+----------+ | 1 | A5.33 | 5 | 4 | | 2 | B2.02 | 2 | 20 | | 3 | C4.16 | 4 | 11 | | 4 | A5.21 | 5 | 6 | | 5 | A5.23 | 5 | 6 | +---------+-----------+-------+----------+

 



Sandini Bib

Nutzt man die lange Version des Parameters, sieht das Ganze so aus: prompt>mysql --execute="SHOW TABLES;" musikschule +-----------------------+ | Tables_in_musikschule | +-----------------------+ | ausleihe | | fulltexttest | | instrument | | lehrer | | leihinstrument | | orchester | | orchester_tn | | raum | | schueler | | unterricht | | unterricht_tn | | warteliste | +-----------------------+

• -B oder --batch Mit dieser Option kann man dafür sorgen, dass die Ausgabe wie bei umgeleiteter Eingabe geschieht: nur mit Tabulatoren getrennte Werte und keine Rahmen um die Tabellen herum. Das kann nützlich sein, wenn man mysql mit dem Parameter -e aufruft (was ansonsten zu einer „normalen“ Anzeige führt): prompt>mysql -B -e "SELECT * FROM instrument;" musikschule instrument_id instr_name instr_gruppe 1 Querflöte Holzbläser 2 Klarinette Holzbläser 3 Violine Streicher 4 Viola Streicher 5 Posaune Blechbläser 6 Trompete Blechbläser 7 Klavier Tasten 8 Keyboard Tasten

Natürlich kann man mysql mit diesem Parameter auch im interaktiven Modus starten, nur hat man dann keinerlei Anzeige und Strukturierung neben den eigentlichen Daten. Im folgenden Beispiel sind zur Verdeutlichung die eingegebenen Zeichen kursiv gedruckt:



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

prompt>mysql -B musikschule SELECT * FROM raum; raum_id raum_name etage personen 1 A5.33 5 4 2 B2.02 2 20 3 C4.16 4 11 4 A5.21 5 6 5 A5.23 5 6 SHOW FIELDS FROM raum; Field Type Null Key Default Extra raum_id smallint(5) unsigned PRI raum_name varchar(10) etage char(3) YES MUL NULL personen mediumint(8) unsigned YES EXIT

NULL

auto_increment

1

prompt>

Wie man sieht, ist dies in den meisten Fällen nicht wirklich sinnvoll. Das Gegenstück dazu ist der Parameter -t. • -H oder --html Diese Option sorgt dafür, dass die Daten anstatt mit |, + und - als HTML-Tabellen formatiert ausgegeben werden. Es wird eine Tabelle mit einer Rahmenstärke von 1 erzeugt, die Spaltennamen sind als ... formatiert, die Werte selber mit ... umschlossen. Diese Option kann man dann verwenden, wenn man die Tabelleninhalte schnell und einfach in einer HTML-Datei ausgeben möchte. Allerdings ist zu beachten, dass Umlaute und andere Sonderzeichen nicht gesondert behandelt werden. Möchte man also auf Nummer sicher gehen, sollte man in der HTML-Datei, die drumherum noch entstehen muss, den entsprechenden Zeichensatz angeben. Ein Beispiel für die Ausgabe zusammen mit -e wäre das folgende: D:\TEMP>mysql -H -e "SELECT * FROM instrument;" musikschule

instrument_idinstr_nameinstr_gruppe
1QuerflöteHolzbläser
2KlarinetteHolzbläser
3ViolineStreicher
4ViolaStreicher
5PosauneBlechbläser
6TrompeteBlechbläser
7KlavierTasten
8KeyboardTasten


Sehr gut lesbar, oder? Aber für eine Web-Seite genau das Richtige (vielleicht abgesehen von den fehlenden Zeilenumbrüchen, die aber auch nur den Quelltext lesbarer machen). Ein etwas komplexeres Bei-

 



Sandini Bib

spiel wäre das folgende, wobei wir die folgende SQL-Anfrage in einer Datei namens unterricht.sql speichern: SELECT CONCAT(LEFT(vorname, 1), '. ', nachname) AS Lehrer, instr_name AS "Instr.", raum_name AS Raum, CASE wochentag WHEN 0 THEN 'Mo' WHEN 1 THEN 'Di' WHEN 2 THEN 'Mi' WHEN 3 THEN 'Do' WHEN 4 THEN 'Fr' WHEN 5 THEN 'Sa' WHEN 6 THEN 'So' END AS Tag, CONCAT(TIME_FORMAT(uhrzeit_von, '%H:%i'), '-', TIME_FORMAT(uhrzeit_bis, '%H:%i')) AS Zeit, kosten AS Preis, IF(einzel, 'X', ' ') AS Einzel FROM unterricht AS u, lehrer AS l, instrument AS i, raum AS r WHERE u.lehrer_id = l.lehrer_id AND u.instrument_id = i.instrument_id AND u.raum_id = r.raum_id;

Sie finden die Datei unterricht.sql auch auf der beiliegenden CD-ROM im Verzeichnis Beispiel. Mit diesem Aufruf erzeugt man nun eine HTML-Tabelle in unterricht_ tab.html: prompt>mysql -H musikschule < unterricht.sql > unterricht_tab.html

Diese Datei kann man nun als Grundlage für eine vollständige HTML-Datei nutzen. Klemmt man noch die notwendigsten Codes drumherum ( am Anfang und am Ende), erhält man eine Ausgabe wie in Abbildung 5.1.

Abbildung 5.1: Ausgabe einer Tabelle im HTML-Format

Die ergänzte Ergebnisdatei unterricht_tab.html ist ebenfalls auf der CDROM im Verzeichnis Beispiel zu finden.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Diese Ausgabe kann man also als Grundlage für die Anzeige von Daten auf Web-Seiten nutzen, allerdings sind andere Wege (zum Beispiel über PHP, siehe weiter unten) deutlich flexibler. Man kann übrigens auch diesen Parameter im interaktiven Modus nutzen, allerdings sind die Ausgaben dann nicht sehr leserlich (wie man oben gesehen hat)… • -N oder --skip-column-names Mit -N kann man die Ausgabe der Spaltennamen unterdrücken. Dies kann dann nützlich sein, wenn man die Daten in Dateien speichern möchte und die Spaltennamen dabei eher hinderlich sind (man sollte dann ja sowieso wissen, wie die Spaltenanordnung ist). Ein Beispiel dafür ist: prompt>mysql -N 1 A5.33 2 B2.02 3 C4.16 4 A5.21 5 A5.23

-B -e "SELECT * FROM raum;" musikschule 5 4 2 20 4 11 5 6 5 6

• -t oder --table Mit dieser Option kann man sich auch dann die Daten in der üblichen Tabellenform (mit Rahmen) ausgeben lassen, wenn man sich eigentlich im Batch-Modus befindet (zum Beispiel bei Verwendung der Ein- und Ausgabeumleitung): prompt>mysql musikschule < unterricht.sql Lehrer Instr. Raum Tag Zeit Preis Einzel G. Mosler Posaune A5.23 Mi 19:15-20:00 25.00 H. Knarer Querflöte C4.16 Fr 17:00-17:45 K. Hansen Violine B2.02 Do 15:35-16:20 20.00 K. Hansen Viola B2.02 Do 16:30-17:15 20.00 P. Ottbein Klavier A5.33 Sa 10:00-11:00 35.00

30.00

X

X

prompt>mysql -t musikschule < unterricht.sql +------------+-----------+-------+------+-------------+-------+--------+ | Lehrer | Instr. | Raum | Tag | Zeit | Preis | Einzel | +------------+-----------+-------+------+-------------+-------+--------+ | G. Mosler | Posaune | A5.23 | Mi | 19:15-20:00 | 25.00 | | | H. Knarer | Querflöte | C4.16 | Fr | 17:00-17:45 | 30.00 | X | | K. Hansen | Violine | B2.02 | Do | 15:35-16:20 | 20.00 | | | K. Hansen | Viola | B2.02 | Do | 16:30-17:15 | 20.00 | | | P. Ottbein | Klavier | A5.33 | Sa | 10:00-11:00 | 35.00 | X | +------------+-----------+-------+------+-------------+-------+--------+

 



Sandini Bib

5.2.2 mysqladmin mysqladmin

Mit mysqladmin kann man viele Verwaltungsaufgaben einfacher erledigen als mit mysql. Dabei nutzt man die folgende Syntax: mysqladmin [options] command command ...

Die möglichen Optionen ähneln denen von mysql, in Tabelle 5.1 gibt es zu jeder eine kurze Beschreibung. Kurze Version

Lange Version

Beschreibung

-#

--debug=...

Ausgabe von Debug-Informationen

-f

--force

Beim Löschen von Datenbanken nicht nachfragen. Werden mehrere Befehle angegeben, werden die folgenden auch bei einem Fehler des aktuellen Befehls abgearbeitet.

-?

--help

Ausgabe der Hilfe

--charactersets-dir=...

Angabe des Verzeichnisses mit den Zeichensatzinformationen

-C

--compress

Komprimierung bei der Kommunikation zwischen Client und Server nutzen

-h

--host=...

Angabe des Hosts

-p

--password[=...]

Angabe des Kennworts oder Nachfragen, falls nicht angegeben

-W

--pipe

Verwendung von Named Pipes bei der Verbindung zum Server

-P

--port=...

Angabe des Ports für die Verbindung

-i

--sleep=...

wiederholtes Ausführen der Befehle mit einer Pause der angegebenen Zeit (in Sekunden)

-r

--relative

zeigt bei Nutzung von -i nur den Unterschied zur vorigen Ausführung (funktioniert nur mit extended-status, siehe unten).

-E

--vertical

Ausgabe wie bei -r, allerdings in vertikaler Richtung

-s

--silent

Keine Fehlermeldung, wenn es Verbindungsprobleme zum Server gibt.

-S

--socket=...

Angabe der Socket-Datei für die Verbindung

-u

--user=...

Angabe des Benutzers, mit dem sich das Programm anmelden soll

-v

--verbose

Ausgabe von mehr Informationen

-V

--version

Nur Ausgabe der Versionsnummer und Beenden des Programms.

-w

--wait=...

Falls die Verbindung nicht möglich ist, die angegebene Anzahl an Versuchen wiederholen, anstatt direkt abzubrechen.

Tabelle 5.1: Optionen für mysqladmin



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Bei den Befehlen gibt es insgesamt 21 Möglichkeiten. Allen ist gemein, dass man sie soweit abkürzen kann, wie sie noch eindeutig sind. create db_name zum Beispiel kann man mit c db_name abkürzen, status hingegen muss mindestens stat heißen. • create db_name Dieser Befehl legt eine Datenbank mit dem Namen db_name an. Er entspricht dem Befehl CREATE DATABASE db_name in mysql. • drop db_name Mit diesem Befehl wird eine Datenbank wieder gelöscht. Dabei gibt es eine Kontrollabfrage (sofern man nicht die Option -f nutzt), da danach unwiderruflich auch alle Tabellen und Daten dieser Datenbank gelöscht sind: prompt>mysqladmin create mytest prompt>mysqladmin drop mytest Dropping the database is potentially a very bad thing to do. Any data stored in the database will be destroyed. Do you really want to drop the 'mytest' database [y/N] y Database "mytest" dropped

• extended-status Dieser Befehl gibt eine Statusübersicht aus, die man unter mysql mit SHOW STATUS erhalten würde. Nutzt man den Parameter -r zusammen mit -i werden beim ersten Mal die „normalen“ Werte ausgegeben, danach aber nur noch die Differenzen zur Vorversion. Dies kann man nutzen, wenn man nur die Veränderungen der Werte beobachten möchte. Der folgende Befehl gibt die Statusliste alle 60 Sekunden aus, zeigt dabei aber (nach dem ersten Mal) nur die Differenzen an: prompt>mysqladmin -i 60 -r extended-status

• flush-hosts leert den Host-Cache. Dies kann nützlich sein, wenn sich die IPAdresse eines Hosts geändert hat oder wenn ein Host geblockt ist. Unter mysql kann man dafür den Befehl FLUSH HOSTS nutzen. • flush-logs schließt alle Log-Dateien und öffnet sie neu. Damit ist sichergestellt, dass alle Änderungen auch auf Festplatte abgelegt sind. Dies entspricht dem Befehl FLUSH LOGS unter mysql.

 



Sandini Bib

• flush-tables schließt alle offenen Tabellen und sorgt dafür, dass alle Tabellen, die gerade in Benutzung sind, möglichst bald geschlossen werden. Dies entspricht dem Befehl FLUSH TABLES unter mysql. • flush-privileges lädt die Berechtigungen erneut aus den entsprechenden Tabellen. Dies entspricht dem Befehl FLUSH PRIVILEGES unter mysql. • kill id, id, ... Mit diesem Befehl kann man Threads im MySQL-Server beenden, die einen Client bedienen. Die entsprechende ID wird beim Anmelden mit mysql ausgegeben, kann aber auch mit SHOW PROCESSLIST ermittelt werden. Ein Thread wird normalerweise nicht sofort beendet, sondern erhält nur eine Markierung. Diese wird erst in bestimmten Situationen berücksichtigt. • password new_pwd ändert das bestehende Kennwort ab und setzt es auf new_pwd. • ping prüft, ob der MySQL-Server erreichbar ist und reagiert: prompt>mysqladmin ping mysqld is alive

• processlist gibt die aktuelle Prozessliste aus. Dies kann auch mit SHOW PROCESSLIST in mysql erreicht werden. Wie in Abbildung 5.2 zu sehen, erhält man neben den angemeldeten Benutzern auch die gerade verwendete Datenbank und eine Information über die aktuelle Tätigkeit. Auch der Aufruf von mysqladmin ist hier (indirekt) vermerkt, er wird auf ein SHOW PROCESSLIST abgebildet. Prompt>mysqladmin processlist +----+------+-----------+-------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+-------------+---------+------+-------+------------------+ | 8 | ODBC | localhost | musikschule | Sleep | 1 | | | | 10 | ODBC | localhost | | Query | 0 | | show processlist | +----+------+-----------+-------------+---------+------+-------+------------------+

Abbildung 5.2: Ausgabe von mysqladmin processlist

• reload lädt die Berechtigungstabellen erneut. Dies ist identisch zu flushprivileges.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

• refresh schließt alle Tabellen und Log-Dateien und öffnet diese erneut. • shutdown Dieser Befehl fährt den Server herunter. Somit lässt sich der Server auch „aus der Ferne“ beenden, ohne, dass man sich direkt auf dem Rechner anmelden muss. • status Mit diesem Befehl kann man sich eine kurze Status-Information ausgeben lassen: prompt>mysqladmin status Uptime: 35750 Threads: 2 Questions: 19 Slow queries: 0 Opens: 7 Flush tables: 1 Open tables: 0 Queries per second avg: 0.001

• start-slave startet einen Slave in einer verteilten Umgebung • stop-slave beendet einen Slave in einer verteilten Umgebung • variables Dieser Befehl gibt die aktuellen Variablen des MySQL-Servers aus und entspricht einem SHOW VARIABLES unter mysql. • version Mit diesem Befehl kann man sich – anders als bei der Option -V – nicht nur die Version von mysqladmin ausgeben lassen, sondern auch die Versionsinfo des MySQL-Servers auslesen. Zudem werden auch die Informationen angezeigt, die man mit status erhält: prompt>mysqladmin version mysqladmin Ver 8.23 Distrib 3.23.51, for Win95/Win98 on i32 Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPL license Server version Protocol version Connection TCP port Uptime:

3.23.51-nt 10 localhost via TCP/IP 3306 9 hours 59 min 36 sec

Threads: 2 Questions: 26 Slow queries: 0 Opens: 7 Flush tables: 1 Open tables: 0 Queries per second avg: 0.001

 



Sandini Bib

5.2.3 mysqlshow mysqlshow

Das Programm mysqlshow dient als einfaches Interface zum Anzeigen von Strukturdaten. Es lassen sich Datenbanken, Tabellen und Spalten ausgeben. Alle Informationen erhält man auch über den SHOW-Befehl in mysql. Der Aufruf sieht wie folgt aus: mysqlshow [OPTIONS] [database [table [column]]]

Als Optionen stehen die (zum Teil bekannten) Varianten aus Tabelle 5.2 zur Verfügung. Kurze Version

Lange Version

Beschreibung

-#

--debug=...

Ausgabe von Debug-Informationen

-?

--help

Ausgabe der Hilfe

-c

--charactersets-dir=...

Angabe des Verzeichnisses mit den Zeichensatzinformationen

-C

--compress

Komprimierung bei der Kommunikation zwischen Client und Server nutzen

-h

--host=...

Angabe des Hosts

-i

–status

Ausgabe von zusätzlichen Tabelleninformationen

-k

--keys

Angabe der Schlüssel für die Tabelle

-p

--password[=...]

Angabe des Kennworts oder Nachfragen, falls nicht angegeben

-W

--pipe

Verwendung von Named Pipes bei der Verbindung zum Server

-P

--port=...

Angabe des Ports für die Verbindung

-S

--socket=...

Angabe der Socket-Datei für die Verbindung

-u

--user=...

Angabe des Benutzers, mit dem sich das Programm anmelden soll

-v

--verbose

Ausgabe von mehr Informationen

-V

--version

nur Ausgabe der Versionsnummer und Beenden des Programms

Tabelle 5.2: Optionen für mysqlshow

Wird keine Datenbank angegeben, werden alle verfügbaren Datenbanken angezeigt: prompt>mysqlshow +-------------+ | Databases | +-------------+ | musikschule | | mysql | | test | +-------------+



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Mit Angabe einer Datenbank werden die dort enthaltenen Tabellen angegeben: prompt>mysqlshow musikschule Database: musikschule +----------------+ | Tables | +----------------+ | ausleihe | | fulltexttest | | instrument | | lehrer | | leihinstrument | | orchester | | orchester_tn | | raum | | schueler | | unterricht | | unterricht_tn | | warteliste | +----------------+

Mit dem zusätzlichen Parameter -i erhält man eine sehr umfangreiche Ausgabe wie bei SHOW TABLE STATUS, siehe auch Abbildung 5.3. prompt>mysqlshow -i musikschule Database: musikschule +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+| Name | Type | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+| 0 | 0 | 0 | ausleihe | 81604378623 | MyISAM | Fixed | 1024 | 0 | | 16 | 227 | 3632 | fulltexttest | 4294967295 | MyISAM | Dynamic | 11264 | 0 | | 8 | 29 | 236 | instrument | 4294967295 | MyISAM | Dynamic | 2048 | 0 | | 4 | 124 | 496 | lehrer | 4294967295 | MyISAM | Dynamic | 2048 | 0 | | 4 | 34 | 136 | leihinstrument | MyISAM | Dynamic | 4294967295 | 2048 | 0 | | orchester | MyISAM | Dynamic | 0 | 0 | 0 | 4294967295 | 1024 | 0 | | 0 | 0 | 0 | orchester_tn | 73014444031 | MyISAM | Fixed | 1024 | 0 | | raum | MyISAM | Dynamic | 5 | 20 | 100 | 4294967295 | 3072 | 0 | | 4 | 117 | 468 | schueler | 4294967295 | MyISAM | Dynamic | 4096 | 0 | | 5 | 31 | 155 | unterricht | 133143986175 | MyISAM | Fixed | 2048 | 0 | | 4 | 13 | 52 | unterricht_tn | MyISAM | Fixed | 55834574847 | 2048 | 0 | | 0 | 0 | 0 | warteliste | 68719476735 | MyISAM | Fixed | 1024 | 0 | +----------------+--------+------------+------+----------------+-------------+-----------------+--------------+-----------+-

Fortsetzung +----------------+---------------------+---------------------+---------------------+----------------+---------+ | Update_time | Check_time | Auto_increment | Create_time | Create_options | Comment | +----------------+---------------------+---------------------+---------------------+----------------+---------+ | 1 | | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | | | | | | 2002-10-03 14:53:01 | 2002-10-03 15:04:05 | | | | 9 | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | | | | | 5 | 2002-09-19 21:49:54 | 2002-09-21 17:00:18 | | | | | 5 | | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | | | | 1 | | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | | | | 1 | | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | | | | | | 6 | 2002-09-24 21:38:22 | 2002-09-24 21:38:22 | 2002-09-24 21:38:22 | | | | 5 | 2002-09-28 21:50:55 | 2002-09-28 21:50:55 | 2002-10-03 13:57:10 | | 6 | | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | | | | 5 | | 2002-09-19 21:49:54 | 2002-09-19 22:13:37 | | | | 1 | | 2002-09-19 21:49:54 | 2002-09-19 21:49:54 | | | +----------------+---------------------+---------------------+---------------------+----------------+---------+

Abbildung 5.3: Ausgabe von mysqlshow -i musikschule

Gibt man Datenbank und Tabelle an, werden Informationen über die Spalten der Tabelle ausgegeben (siehe Abbildung 5.4). Diese Ausgabe erreicht man auch mit SHOW FULL COLUMNS FROM ...

 



Sandini Bib

prompt>mysqlshow musikschule raum Database: musikschule Table: raum Rows: 5 +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+ | Field | Type | Null | Key | Default | Extra | Privileges | +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+ | raum_id | smallint(5) unsigned | | PRI | | auto_increment | select,insert,update,references | | raum_name | varchar(10) | | | | | select,insert,update,references | | etage | char(3) | YES | MUL | | | select,insert,update,references | | personen | mediumint(8) unsigned | YES | | 1 | | select,insert,update,references | +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+

Abbildung 5.4: Ausgabe von mysqlshow musikschule raum

Nutzt man zusätzlich den Parameter -k, werden auch noch Informationen über die Schlüssel der Tabelle ausgegeben, wie in Abbildung 5.5 zu sehen. Dies ist eine Kombination von SHOW FULL COLUMNS FROM ... mit SHOW INDEX FROM ... prompt>mysqlshow -k musikschule raum Database: musikschule Table: raum Rows: 5 +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+ | Field | Type | Null | Key | Default | Extra | Privileges | +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+ | raum_id | smallint(5) unsigned | | PRI | | auto_increment | select,insert,update,references | | raum_name | varchar(10) | | | | | select,insert,update,references | | etage | char(3) | YES | MUL | | | select,insert,update,references | | personen | mediumint(8) unsigned | YES | | 1 | | select,insert,update,references | +-----------+-----------------------+------+-----+---------+----------------+---------------------------------+ +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+---------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Comment | +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+---------+ | raum | 0 | PRIMARY | 1 | raum_id | A | 5 | | | | | raum | 1 | idx_etage | 1 | etage | A | 2 | | | | +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+---------+

Abbildung 5.5: Ausgabe von mysqlshow -k musikschule raum

Gibt man auf einer der beschriebenen Ebenen einen Platzhalter an (wobei hier auch die Betriebssystem-Platzhalter * und ? zugelassen sind), werden diese wie ein LIKE ... umgesetzt: prompt>mysqlshow musikschule u* Database: musikschule Wildcard: u% +---------------+ | Tables | +---------------+ | unterricht | | unterricht_tn | +---------------+

Dies kann allerdings zu Problemen führen, wenn man zum Beispiel die Spalten der Tabelle unterricht_tn angezeigt bekommen möchte: prompt>mysqlshow musikschule unterricht_tn Database: musikschule Wildcard: unterricht_tn +---------------+ | Tables | +---------------+ | unterricht_tn | +---------------+



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Leider interpretiert mysqlshow die letzte Angabe als Angabe eines Filters mit Platzhaltern (wegen des Unterstrichs) und gibt nur alle Tabellen aus, die diesem Filter entsprechen. Umgehen kann man das Problem, indem man in solch einem Fall als zusätzlichen Wert noch ein einzelnes Prozentzeichen anfügt: prompt>mysqlshow musikschule unterricht_tn % Database: musikschule Table: unterricht_tn Rows: 4 Wildcard: % +------------------+------------------+------+-----+---------+-------... | Field | Type | Null | Key | Default | Extra ... +------------------+------------------+------+-----+---------+-------... | unterricht_tn_id | int(10) unsigned | | PRI | | | unterricht_id | int(10) unsigned | | | 0 | | schueler_id | int(10) unsigned | | | 0 | +------------------+------------------+------+-----+---------+-------...

Damit wird die Angabe der Tabelle nicht mehr als Filter interpretiert, sondern nur die Angabe für die Spalten. Bei denen bewirkt das % aber natürlich, dass alle Spalten ausgegeben werden – also unser gewünschtes Ergebnis.

5.3

phpMyAdmin

phpMyAdmin ist kein direktes Programm. Es handelt sich vielmehr um eine Sammlung von PHP-Seiten für einen Web-Server, mit denen sich MySQL-Server (auch aus der Ferne) administrieren und die Daten anzeigen lassen. Er bietet unter einer übersichtlichen Oberfläche viele Funktionen, die bei der Verwaltung von MySQL nützlich sind.

phpMyAdmin

Um phpMyAdmin nutzen zu können, benötigt man einen laufenden Web-Server mit PHP-Unterstützung. Dabei kann es sich um den Apache oder auch andere Server handeln. Der Server muss nicht mit dem MySQL-Server identisch sein, solange er Zugriff auf ihn bietet. Zum Einrichten eines Apache-Servers mit PHP-Unterstützung sei auf das spätere Kapitel über LAMP/WAMP verwiesen. Zur Installation lädt man sich am besten von http://www.phpmyadmin.net die entsprechenden Dateien herunter. Die Installationsdateien sind auch auf der beiliegenden CD-ROM enthalten. Im Unterverzeichnis phpMyAdmin finden Sie die Dateien phpMyAdmin-2.3.3pl1-php.zip für Windows und phpMyAdmin-2.3.3pl1php.tar.gz.

SKS0\$GPLQ



Sandini Bib

Es reicht, die Daten auszupacken (wobei auf die Verzeichnisstruktur Rücksicht genommen werden sollte) und an eine Stelle zu kopieren, auf die der Web-Server zugreifen kann. Unter Apache kann man die Daten zum Beispiel in das entsprechende Verzeichnis htdocs in ein Unterverzeichnis phpMyAdmin kopieren. In der dort vorhandenen Datei config.inc.php müssen nun noch einige wenige Eintragungen zum MySQLServer und den Anmeldeparametern vorgenommen werden. • $cfg['Servers'][$i]['host'] = 'hostname'; Hier wird der Name oder die IP-Adresse des MySQL-Servers eingetragen. • $cfg['Servers'][$i]['port'] = 'portnumber'; Falls sich der Port, unter dem der MySQL-Server läuft, von der Standardvorgabe 3306 unterscheidet, kann man hier die korrekte Portnummer angeben. Ansonsten kann man den Eintrag leer lassen. • $cfg['Servers'][$i]['user'] = 'username'; Angabe des Benutzernamens, mit dem sich phpMyAdmin am MySQL-Server anmelden soll. • $cfg['Servers'][$i]['password'] = 'password'; Angabe des Kennworts für den angegebenen Benutzer. • $cfg['PmaAbsoluteUri'] = 'url_string'; Dies ist die absolute Angabe der URL für das phpMyAdmin-Verzeichnis, zum Beispiel http://localhost/phpMyAdmin/. Dies ist nicht unbedingt erforderlich, da phpMyAdmin diesen Wert in den meisten Fällen auch automatisch ermitteln kann. Lässt man diesen Eintrag leer, gibt es auf der Startseite eine Warnung, die ausgeschaltet werden kann, wenn es auch automatisch funktioniert. Hat man die notwendigen Einstellungen vorgenommen, kann man die Startseite aufrufen. Befindet man sich direkt auf dem Web-Server und hat phpMyAdmin unter htdocs im Verzeichnis phpMyAdmin installiert, kann man es mit der URL http://localhost/phpMyAdmin/index.php aufrufen und erhält eine Webseite wie in Abbildung 5.6 zu sehen.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Abbildung 5.6: Startseite von phpMyAdmin

Auf der Startseite kann man verschiedene Dinge erreichen: Im linken Rahmen lässt sich eine Datenbank auswählen. In der linken Spalte des rechten Rahmens gibt es einige Links auf Verwaltungstasks, die unabhängig von einer ausgewählten Datenbank sind (Erstellen einer neuen Datenbank, Informationen über den MySQL-Server oder die Benutzer anzeigen, MySQL-Server neu starten). Rechts lässt sich die Sprache von phpMyAdmin auswählen und man erreicht die Dokumentation. Der in eckigen Klammern angegebene Link auf Dokumentation führt direkt auf die entsprechenden Webseiten von http://www.mysql.com. Klickt man zum Beispiel auf Statistiken über alle Datenbanken, erreicht man die in Abbildung 5.7 dargestellte Seite. Hier lassen sich (wie an fast allen anderen Stellen auch, wo eine tabellarische Übersicht gezeigt wird) die Spaltentitel anklicken. Dadurch wird die Anzeige nach der entsprechenden Spalte sortiert. Klickt man die Überschrift ein zweites Mal an, wird die Tabelle nach dieser Spalte absteigend sortiert.

SKS0\$GPLQ



Sandini Bib

Abbildung 5.7: Statistiken über alle Datenbanken in phpMyAdmin

Über die Auswahlbox im linken Rahmen gelangt man zu den einzelnen Datenbanken des Servers. Wählt man dort musikschule aus, gelangt man in eine Übersicht über diese Datenbank (siehe Abbildung 5.8). Mit einem Klick auf eine der angezeigten Tabellennamen erhält man eine Übersicht über die Struktur der Tabelle und einige statistische Daten (Abbildung 5.9). Von hier aus kann man eigentlich alles machen, was mit einer Tabelle sinnvoll ist: Ändern der Struktur, Eintragen von Daten, Analysieren der Inhalte, Exportieren, Importieren, Umbenennen und vieles mehr. Klickt man auf den Reiter Anzeigen, erhält man den Inhalt der Tabelle (siehe Abbildung 5.10). Dabei sind ein paar Dinge, die auch an vielen anderen Stellen vorkommen, erwähnenswert: • Es wird der zugrunde liegende SQL-Befehl mit Syntaxhighlighting angezeigt. Dieser Befehl lässt sich über Ändern anpassen. So kann man nebenbei etwas SQL lernen … • Man kann angeben, wie viele Datensätze angezeigt werden sollen. Zudem lässt sich festlegen, ob die Datensätze untereinander oder nebeneinander zu sehen sein sollen. • Für die einzelnen Spalten lassen sich Aktionen durchführen (hier: Ändern, Löschen). • Es gibt eine Druckansicht, die alle „Befehlslinks“ entfernt und eine sinnvoll druckbare Übersicht darstellt (siehe Abbildung 5.11).



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Abbildung 5.8: Übersicht über die Datenbank musikschule in phpMyAdmin

Abbildung 5.9: Übersicht über die Tabelle raum in phpMyAdmin

SKS0\$GPLQ



Sandini Bib

Abbildung 5.10: Inhalt der Tabelle raum in phpMyAdmin

Abbildung 5.11: Druckansicht der Tabelle raum in phpMyAdmin

All diese Optionen sind auch an vielen anderen Stellen vorhanden, so dass man sich schnell zurechtfindet.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

phpMyAdmin ist ein sehr bequemes Hilfsmittel, um MySQL-Server und -Datenbanken zu administrieren. Fast alles, was man mit einem MySQLServer machen kann, lässt sich auch über diese grafische Oberfläche erreichen, zudem läuft es in (fast) jedem Browser und unter jedem Betriebssystem und ist zudem wunderbar für die Fernwartung nutzbar.

5.4

Import und Export

Manchmal ist es notwendig, Daten aus der Datenbank auszulesen, um sie für andere Programme zu verwenden. Oder man möchte Daten aus einer anderen Quelle einlesen, um sie in seinen eigenen Applikationen zu nutzen. Natürlich kann man dafür eigene Programme schreiben, die Satz für Satz aus- bzw. einlesen und wegschreiben bzw. in die Datenbank eintragen. Es gibt allerdings einfachere und schnellere Methoden dafür. Um Daten im Text-Format zu speichern, kann man SELECT ... INTO ... nutzen. Umgekehrt lassen sich mit LOAD DATA INFILE Daten mit hoher Geschwindigkeit aus einer Text-Datei in die Datenbank einlesen. Das Programm mysqldump erzeugt SQL-Befehle, mit denen eine Datenbank oder Tabelle in einem anderen Datenbank-Server wieder angelegt werden kann. In die andere Richtung ist mysqlimport ein Tool, welches das Importieren von Text-Dateien wie mit LOAD DATA INFILE unterstützt.

5.4.1 SELECT ... INTO Um Daten in Textdateien zu exportieren, bietet sich der SELECT-Befehl an, dem als Erweiterung ein Ziel und verschiedene Format-Optionen mitgegeben werden. Die daraus erstellten Daten lassen sich auf ziemlich weitreichende Art anpassen, allerdings haben sie nie eine feste Breite (was man im Notfall allerdings mit einigen Tricks erreichen kann), sondern sind immer durch Trennzeichen unterteilt.

SELECT INTO

Die Syntax wurde schon weiter oben im Kapitel über SELECT beschrieben, hier nochmals ein Überblick über die Optionen. Prinzipiell geht es um einen normalen SELECT-Befehl, der beliebig kompliziert sein kann. Zusätzlich folgt nach Angabe der Spalten der Abschnitt INTO ... mit seinen Optionen: SELECT ... INTO {OUTFILE | DUMPFILE} 'file_name' [FIELDS [TERMINATED BY 'fielddiv'] [[OPTIONALLY] ENCLOSED BY 'enc_char'] [ESCAPED BY 'esc_char']] [LINES TERMINATED BY 'linedev'] FROM ...

  



Sandini Bib

Normalerweise wird man SELECT ... INTO OUTFILE ... nutzen. Dies ist die „normale“ Version und sorgt für das Speichern aller selektierten Daten mit den entsprechenden Formatierungsoptionen in der gewünschten Datei. Verwendet man stattdessen SELECT ... INTO DUMPFILE ..., wird nur ein Datensatz in die Datei geschrieben, und zwar ohne Feldbegrenzer, Spaltenüberschriften oder Ähnliches. Man kann dies zum Beispiel dazu nutzen, den Inhalt eines Blob-Feldes direkt in einer Datei zu verewigen. Zu beachten ist, dass die Ergebnismenge auf jeden Fall nur einen Datensatz enthalten darf, ansonsten funktioniert das Ganze nicht. Sichergehen kann man, indem man die Option LIMIT 1 am Ende des SELECT-Befehls nutzt. Als Dateiname wird file_name verwendet. Diese Datei darf aus Sicherheitsgründen noch nicht existieren, sie wird auf jeden Fall neu angelegt. Ist sie schon vorhanden, schlägt der Aufruf fehl. Die Datei wird auf dem Server erstellt. Möchte man die Daten auf dem Client speichern, muss man einen anderen Weg gehen: entweder über mysql -e "SELECT ..." > file_name mit den entsprechend formatierten Spalten (siehe auch Kapitel 5.2.1) oder über das Programm mysqldump (siehe Kapitel 5.4.3). Gibt man nur den Dateinamen an, ohne weitere Formatierungsoptionen festzulegen, werden die Daten durch Tabulatorzeichen getrennt, ohne Anführungszeichen und mit dem Backslash (\) als Fluchtzeichen abgespeichert. Dabei wird pro Zeile ein Datensatz angelegt. Mit folgendem Befehl wird die Datei raum.txt auf dem Server erzeugt (sofern nicht schon vorhanden) und die Raum-Daten dort eingetragen: SELECT * INTO OUTFILE 'raum.txt' FROM raum;

Die Datei raum.txt hat dann folgenden Inhalt (wobei zwischen den einzelnen Werten ein Tabulatorzeichen steht): 1 2 3 4 5

A5.33 B2.02 C4.16 A5.21 A5.23

5 2 4 5 5

4 20 11 6 6

Mit den folgenden Formatierungsoptionen lässt sich der Inhalt der Datei in einigen Dingen anpassen. FIELDS ändert das Ausgabeformat bezüglich der Feldwerte, während LINES das Verhalten beim Wechsel von einem Datensatz auf den nächsten festlegt. FIELDS TERMINATED



• FIELDS TERMINATED BY 'fielddiv' Hiermit kann man festlegen, wie die Feldwerte untereinander getrennt werden sollen. Üblich sind das Tabulatorzeichen (Standardeinstellung, auch erreichbar durch '\t'), das Komma oder das

=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Semikolon. Man kann durchaus auch mehr als ein Zeichen angeben, es wird dann der gesamte String als Trenner genutzt. Dies ist zum Beispiel dann nützlich, wenn die Ausgabe „leserlicher“ sein soll: Man gibt nicht nur ein Komma an, sondern auch noch ein Leerzeichen danach: SELECT * INTO OUTFILE 'raum2.txt' FIELDS TERMINATED BY ', ' FROM raum;

Das Ergebnis sieht dann so aus: 1, 2, 3, 4, 5,

A5.33, B2.02, C4.16, A5.21, A5.23,

5, 2, 4, 5, 5,

4 20 11 6 6

• FIELDS [OPTIONALLY] ENCLOSED BY 'enc_char'

FIELDS ENCLOSED

Alle Feldwerte werden durch den enc_char umschlossen. Üblicherweise wird man hier das einfache oder doppelte Anführungszeichen nutzen, es sind aber auch andere denkbar, durchaus auch mehrere Zeichen (zum Beispiel '#*#'). Was für einen Vorteil hat es, Feldwerte zum Beispiel mit Anführungszeichen zu umschließen? Nun, ein Stringfeld kann alle möglichen Zeichen enthalten, unter anderem auch das Feldtrennzeichen. Ein Tabulatorzeichen ist vielleicht selten, aber wenn man das Komma als Feldtrenner nutzt, kann man schon bei Adressen Probleme bekommen. Das glauben Sie nicht? Dann kommen Sie mal nach Mannheim, dort gibt es in der Innenstadt keine Straßennamen (bis auf Ausnahmen), sondern nur Blöcke: A1, A2, ... A7, B1 usw. bis U7. Noch kein Problem, allerdings wird die Hausnummer meist durch ein Komma getrennt: P3, 6. Was macht das jetzt für Kummer? Geben wir doch einfach mal ein Beispiel aus. Zunächst fügen wir einen Satz mit entsprechender Adresse ein: INSERT INTO schueler (nachname, vorname, geburtsdatum, strasse, plz, ort, geschlecht) VALUES ('Herms', 'Georg', '1960-09-16', 'P3, 6', '68161', 'Mannheim', 0);

Nun lassen wir uns eine Liste mit Namen und Adressen der Schüler ausgeben: SELECT nachname, vorname, strasse, plz, ort INTO OUTFILE 'komma.txt' FIELDS TERMINATED BY ',' FROM schueler;

  



Sandini Bib

Das Ergebnis sieht so aus: Schmidt,Thomas,Hauptstr. 16,28219,Bremen Mayer,Anke,Leher Heerstr. 342,68219,Mannheim Meier,Frank,Waldweg 30,69190,Walldorf Schulze,Friederike,Relaisstr. 432,41564,Kaarst Herms,Georg,P3\, 6,68161,Mannheim

Wie man sieht, ist das Komma nun mit einem Fluchtzeichen versehen. Dies ist beim anschließenden Weiterverarbeiten meist recht umständlich, manche Programme können gar nichts damit anfangen. Es kann auch sein, dass man das Fluchtzeichen aus anderen Gründen bewusst weglässt (siehe dazu weiter unten). Und dann wird es schwierig. In diesem Fall wird das verarbeitende Programm nämlich 6 als Postleitzahl und 68161 als Ort ansehen. Dumm, oder? Die Lösung liegt eben in ENCLOSED BY. Das Beispiel von eben, jetzt aber mit Anführungszeichen: SELECT nachname, vorname, strasse, plz, ort INTO OUTFILE 'komma2.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '"' FROM schueler;

Als Ergebnis erhält man jetzt: "Schmidt","Thomas","Hauptstr. 16","28219","Bremen" "Mayer","Anke","Leher Heerstr. 342","68219","Mannheim" "Meier","Frank","Waldweg 30","69190","Walldorf" "Schulze","Friederike","Relaisstr. 432","41564","Kaarst" "Herms","Georg","P3, 6","68161","Mannheim"

Jeder Wert ist jetzt mit Anführungszeichen umschlossen, das Komma in P3, 6 nicht mehr mit einem Fluchtzeichen versehen. Damit können die meisten Programme etwas anfangen… Nun kann man einwenden, dass bei Zahlen nun wirklich kein Anführungszeichen notwendig ist. Es gibt keine besonders gefährlichen Zeichen, da kostet das nur Platz und macht auch etwas unübersichtlicher. Dazu kann man die Option OPTIONALLY nutzen: Sie sorgt dafür, dass nur String-Felder umschlossen werden: SELECT * INTO OUTFILE 'raum3.txt' FIELDS TERMINATED BY ', ' OPTIONALLY ENCLOSED BY '"' FROM raum;

führt zu: 1, 2, 3, 4, 5,



"A5.33", "B2.02", "C4.16", "A5.21", "A5.23",

"5", "2", "4", "5", "5",

4 20 11 6 6

=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Die Etage (als jeweils vorletzter Wert) ist hier mit Anführungszeichen umschlossen, da sie als String in der Datenbank abgespeichert ist. Wie man an den letzten Beispielen sieht, kann man bei FIELDS auch mehrere Optionen kombinieren. Es muss nur mindestens eine angegeben sein, sonst muss man auch FIELDS weglassen. • FIELDS ESCAPED BY 'esc_char'

FIELDS ESCAPED

Mit dieser Option kann man das Fluchtzeichen angeben. Standardmäßig handelt es sich um den Backslash (\), der dann vor allen besonders zu behandelnden Zeichen angegeben wird. Bei diesen Zeichen handelt es sich um den Backslash selber (daher muss er bei eigener Angabe auch als '\\' definiert werden), das Tabulatorzeichen, den Zeilenumbruch und der Feldtrenner innerhalb von Werten (sofern sie nicht durch Anführungszeichen umschlossen sind). Es lassen sich auch mehrere Zeichen angeben, von denen wird aber nur das erste verwendet. Will man das Fluchtzeichen unterdrücken, muss ein leerer String ('') angegeben werden. Dies funktioniert übrigens auch bei den anderen Optionen. • LINES TERMINATED BY 'linedev'

LINES

Durch diese Option ist es möglich, die einzelnen Datensätze durch besondere Zeichen zu trennen. Üblich ist der Zeilenumbruch ('\n'), man kann aber auch beliebige andere Werte angeben. So würde die Anweisung für einen Datensatztrenner #*# aussehen:

TERMINATED

SELECT raum_name INTO OUTFILE 'raum4.txt' LINES TERMINATED BY '#*#' FROM raum;

Das Ergebnis wäre dann: A5.33#*#B2.02#*#C4.16#*#A5.21#*#A5.23#*#

Es handelt sich hier nur um eine Zeile, da die Datensätze nur noch durch #*# getrennt sind, aber nicht mehr durch den Zeilenumbruch. Die Standardeinstellung lautet zusammengefasst wie folgt: FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n'

Zum Abschluss möchte ich noch ein Beispiel aus dem „richtigen“ Leben präsentieren, bei dem es um die geschickte Kombination von Funktionen, Formatoptionen und der Ausgabe geht: Es hat nichts mit der Musikschule zu tun, sondern basiert auf einer Frage in der Newsgroup de.comp.datenbanken.mysql von André Schleife. Er hatte in einer Tabelle Literaturverweise auf Artikel gespeichert, die er so in einer Datei spei-

  



Sandini Bib

chern wollte, dass sich aus dieser mittels BibTeX (einem Tool für das Satzsystem LaTeX) ein Literaturverzeichnis erstellen ließ. Gegeben war eine Tabelle article mit dem Schlüssel ikey sowie den Feldern author, title, journal, year, pages, volume, number, URL und keywords. Diese sollten dann als Datei pro Datensatz wie folgt aussehen: @Article{ikey, author = {author}, title = {title}, journal = {journal}, year = {year}, pages = {pages}, volume = {volume}, number = {number}, URL = {URL}, keywords = {keywords} } @Article{ikey, ...

Dabei entsprechen die kursiv geschriebenen Worte den Spaltennamen in der Tabelle. Erschwerend kam noch hinzu, dass Felder, die keinen Inhalt haben, nicht auftauchen sollen. Gelöst wurde das Ganze durch den folgenden Befehl: SELECT CONCAT('@Article{',ikey, IFNULL(CONCAT(',\n author = {',author),''), IFNULL(CONCAT('},\n title = {',title),''), IFNULL(CONCAT('},\n journal = {',journal),''), IFNULL(CONCAT('},\n year = {',year),''), IFNULL(CONCAT('},\n pages = {',page),''), IFNULL(CONCAT('},\n volume = {',volume),''), IFNULL(CONCAT('},\n number = {',number),''), IFNULL(CONCAT('},\n URL = {',URL),''), IFNULL(CONCAT('},\n keywords = {',keywords),''),'}\n}\n\n') INTO OUTFILE '/sqltest/literatur.td.bib' FIELDS ESCAPED BY '' FROM article ORDER BY ikey;

Mittels CONCAT werden die einzelnen Strings und Felder zusammengesetzt. IFNULL sorgt dafür, dass nur dann eine Zeile entsteht, wenn das Feld auch Inhalt besitzt. Dabei wurde die Eigenschaft ausgenutzt, dass CONCAT immer dann NULL zurückliefert, wenn auch nur ein einzelner Wert der Parameterliste NULL ist. So ist CONCAT(',\n author = {', author) dann komplett NULL, wenn author alleine schon NULL ist. In diesem Fall wird der zweite Parameter von IFNULL zurückgeliefert, der hier ein Leer-



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

string ist. Auf diesem Weg werden überflüssige Leerzeilen vermieden, zugleich wird aber für die einzelnen Elemente nicht NULL, sondern ein leerer String zurückgeliefert. Dies ist deshalb nötig, weil sonst das oberste CONCAT auch NULL zurückgeben würde. FIELDS ESCAPED BY '' sorgt wiederum dafür, dass die per Hand eingefügten Zeilenumbrüche (\n) nicht wieder mit einem Backslash maskiert werden.

Als Ergebnis erhält man jetzt folgenden Text, der wunderbar mit BibTeX weiterverarbeitet werden kann: @Article{eppler:92, author = {D. T. Eppler et al.}, title = {Passive Microwave Signatures of Sea Ice}, journal = {Geophysical Monograph}, year = {1992}, pages = {300--302}, volume = {68}, keywords = {microwave signatures} } @Article{fuhrhop:96, author = {R. Fuhrhop and C. Simmer}, title = {SSM/I Brightness Temperature Corrections for Incidence Angle Variantions}, journal = {Journal of Atmospheric and Oceanic Technology}, year = {1996}, pages = {230--235}, volume = {13}, number = {1}, keywords = {temperature corrections} } @Article{hartmann:96, author = {J. Hartmann et al.}, title = {Radiation and Eddy Flux Experiment 1995 (REFLEX III)}, journal = {Berichte zur Polarforschung}, year = {1996}, pages = {147--155}, volume = {218}, URL = {www.awi-bremerhaven.de}, keywords = {REFLEX III} }

Setzt man das Ergebnis schließlich in LaTeX um, erhält man die gewünschte Ausgabe in Abbildung 5.12:

  



Sandini Bib

So konnten Daten aus den verschiedenen jahreszeitlichen Situationen gewonnen werden [2]. Dabei ist zu bedenken, daß alleine dem SSM/I-Sensor schon ein Fehler von etwa 2 K zuzuordnen ist [3]. Aus [1] wurden dann fur ¨ diesen Eistyp die entsprechenden Emissivit¨aten ermittelt und in (fur ¨ MWMOD notwendige) Reflektivit¨aten umgerechnet.

Literatur [1] D. T. Eppler et al. Passive Microwave Signatures of Sea Ice. Geophysical Monograph, 68:300–302, 1992. [2] J. Hartmann et al. Radiation and Eddy Flux Experiment 1995 (REFLEX III). Berichte zur Polarforschung, 218:147–155, 1996. [3] R. Fuhrhop and C. Simmer. SSM/I Brightness Temperature Corrections for Incidence Angle Variantions. Journal of Atmospheric and Oceanic Technology, 13(1):230–235, 1996. Abbildung 5.12: Ausgabe der LaTeX-Datei mit dem Literaturverzeichnis

5.4.2 LOAD DATA INFILE LOAD DATA

Für den umgekehrten Weg, das Einlesen von Textdaten in eine MySQLTabelle, gibt es den Befehl LOAD DATA INFILE. Mit ihm kann man Dateien des Typs einlesen, die sich auch mit SELECT ... INTO erzeugen lassen, auch die Parameter sind zum Teil identisch. Seine Syntax lautet: LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name' [REPLACE | IGNORE] INTO TABLE tbl_name [FIELDS [TERMINATED BY 'fielddiv'] [[OPTIONALLY] ENCLOSED BY 'enc_char'] [ESCAPED BY 'esc_char' ] ] [LINES TERMINATED BY 'linediv'] [IGNORE number LINES] [(col_name,...)]

Die kürzeste gültige Variante ist hier LOAD DATA INFILE 'file_name' INTO TABLE tbl_name;

Dabei werden die Daten aus der Textdatei file_name in die Tabelle tbl_name eingelesen. Die Feldwerte müssen durch Tabulatoren getrennt



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

werden, dürfen nicht mit Anführungszeichen umschlossen sein und das Fluchtzeichen ist der Backslash (\). Die einzelnen Datensätze sind durch Zeilenumbrüche getrennt. Die solchermaßen eingelesenen Daten wandern dann in die Tabelle tbl_name, wobei alle Felder in der richtigen Reihenfolge schon in der Datei

vorhanden sein müssen und auch keine überflüssigen Zeilen am Anfang der Datei existieren dürfen. Die schon existierenden Schlüsselwerte in der Tabelle dürfen in der Datei nicht nochmals vorkommen, da es ansonsten eine Fehlermeldung gibt. Natürlich kann man sehr viele Änderungen am Standardverhalten vornehmen: • LOW_PRIORITY | CONCURRENT Gibt man als Option LOW_PRIORITY an, werden nur dann Daten in die Zieltabelle geschrieben, wenn sonst niemand mehr auf sie zugreift. Umgekehrt kann man mit CONCURRENT dafür sorgen, dass auch während des Einfügens von Daten mittels LOAD DATA INFILE andere Clients auf die Tabelle zugreifen können, was allerdings nur mit MyISAMTabellen möglich ist. • LOCAL Normalerweise sucht MySQL die Datei auf dem Server. Mit der Angabe von LOCAL wird der Client-Rechner als Grundlage genommen. Allerdings muss man sich darüber im Klaren sein, dass das Laden der Daten dann länger dauert, weil schließlich die Textdatei erst an den Server übertragen werden muss. • REPLACE | IGNORE Standardverhalten von LOAD DATA INFILE ist, dass es keine Konflikte zwischen den Schlüsselwerten der neu einzulesenden Daten und denen der schon vorhandenen Daten in der Tabelle geben darf. Passiert dies doch (d.h., ein neu einzutragender Datensatz aus der Datei besitzt einen Schlüssel, der schon in der Tabelle existiert), bricht die Verarbeitung mit einer Fehlermeldung ab. Dies entspricht dem Verhalten von INSERT INTO ... Mittels REPLACE kann man nun dafür sorgen, dass die Daten trotzdem eingetragen werden; bestehende Datensätze mit dem entsprechenden Schlüssel werden dadurch überschrieben. Durch diese Option wird also das Verhalten des Befehls REPLACE INTO ... nachgebildet. Nutzt man stattdessen IGNORE, werden vorhandene Daten nicht überschrieben, es wird aber auch keine Fehlermeldung ausgegeben. Damit bleiben alte Daten bestehen, noch nicht vorhandene werden aber trotzdem eingetragen – wie bei INSERT IGNORE INTO...

  



Sandini Bib

• FIELDS TERMINATED BY 'fielddiv' [OPTIONALLY] ENCLOSED BY 'enc_char' ESCAPED BY 'esc_char' LINES TERMINATED BY 'linediv'

Diese Optionen entsprechen exakt denen bei SELECT ... INTO, bis auf die Ausnahme, dass enc_char und esc_char nur aus jeweils einem einzelnen Zeichen bestehen dürfen (wenn man sie nicht leer lässt). Zudem ist es nicht zulässig, fielddiv und enc_char gleichzeitig leer zu lassen. Dies entspräche einer Textdatei mit festen Spaltenbreiten, was nicht unterstützt wird. • IGNORE number LINES Mit dieser Angabe werden die ersten number Zeilen am Anfang der Datei ignoriert. Damit kann man Zeilen überspringen, die Spaltenüberschriften oder andere Angaben zu den Daten enthalten. • (col_name, ...) Auf diesem Weg kann man die zu befüllenden Spalten der Tabelle in der entsprechenden Reihenfolge angeben, in der sie in der Datei auftauchen. Dies kann dann nützlich sein, wenn man nicht alle Spalten der Tabelle befüllen will oder die Daten in der Datei in der falschen Reihenfolge stehen. Felder, die nicht gefüllt werden – entweder, weil sie in der Spaltenliste nicht mit aufgeführt wurden, oder weil einfach nicht genug Werte pro Datensatz vorhanden sind – erhalten ihren Standardwert. Gibt es hingegen zu viele Werte pro Datensatz in der Datei, werden diese ignoriert und die Anzahl der Warnungen dementsprechend erhöht. Als Beispiel wollen wir unsere Musikschul-Datenbank mit den gültigen Bankleitzahlen ergänzen. Die Daten mögen zwar nicht unbedingt zwingend wichtig sein, sie sind aber ein schönes Beispiel für das Einlesen externer Daten nach MySQL. Zudem kann man die Daten kostenlos im Internet erhalten. Leider handelt es sich dabei um eine Datei mit fester Feldlänge und ohne Feldtrenner. Aber auch dies lässt sich mit Hilfe einer Zwischentabelle lösen. Auf den Web-Seiten der Bundesbank gibt es unter http://www.bundesbank.de/zv/zv_bankleitzahlen.htm einen Link „Download der Bankleitzahlen-Dateien“ zum Herunterladen der Bankleitzahlen. Es gibt verschiedene Varianten, uns interessiert hier aber die PC/Text-Version. Die hier genutzte Datei hat den Namen blz0209pc.exe. Dabei steht 0209 für die Gültigkeit ab September 2002, eventuell gibt es bereits neuere Versionen. Die Datei ist eine selbstentpackende Zip-Datei. Sie kann auch unter Linux mittels unzip ausgepackt werden. Als Ergebnis erhält man eine Text-



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

datei (hier blz0209pc.txt) und ein Word-Dokument mit der Beschreibung über den Aufbau der Daten (satzaufbaupc188.doc). Auch diese Datei lässt sich notfalls unter Linux öffnen, entweder, indem man OpenOffice oder StarOffice nutzt oder sie sich ganz rudimentär mit strings satzaufbaupc188.doc ausgeben lässt. Aus dieser Beschreibung kann man nun den Aufbau der Daten entnehmen. Da es sich allerdings um Felder fester Länge handelt, bleibt uns zunächst nichts anderes übrig, als die Daten „am Stück“ in eine Zwischentabelle zu importieren. Aus dieser Tabelle kann man dann mit den SQLFunktionen die „richtige“ BLZ-Tabelle mit den „richtigen“ Feldern befüllen. Der Dokumentation entnehmen wir, dass die Daten pro Satz insgesamt 188 Zeichen lang sind. Also erzeugen wir eine Tabelle mit einem Feld dieser Länge: CREATE TABLE blz_import (satz CHAR(188));

Nun können wir für den ersten Schritt die Daten importieren. mysql> LOAD DATA INFILE 'blz0209pc.txt' -> INTO TABLE blz_import; Query OK, 21104 rows affected (0.39 sec) Records: 21104 Deleted: 0 Skipped: 0 Warnings: 0

Wie man sieht, geht das Einlesen der Datei wirklich schnell – gerade mal 0,39 Sekunden haben die 21.104 Sätze benötigt. Allerdings sind wir noch nicht fertig, schließlich müssen die Daten noch in die richtige Tabelle kopiert werden. Diese legen wir erst einmal an, wobei als Vorlage für die Felder die Beschreibung der Datenstruktur dient. Dabei nutzen wir nur die Felder, die wir später auch nutzen wollen: die Bankleitzahl selber und der längere Name der Bank. CREATE TABLE bankleitzahl (blz CHAR(8), bank_name VARCHAR(58), INDEX (blz), INDEX (bank_name));

Es wurde kein Primärschlüssel angelegt, da es durchaus mehrfache Kombinationen von BLZ und Name gibt. Nun können wir die Daten aus der Import-Tabelle übertragen: mysql> INSERT INTO bankleitzahl -> (blz, bank_name) -> SELECT DISTINCT LEFT(satz, 8), MID(satz, 28, 58) -> FROM blz_import -> WHERE LEFT(satz, 8) > '00000000'; Query OK, 6401 rows affected (0.56 sec) Records: 6401 Duplicates: 0 Warnings: 0

  



Sandini Bib

Durch den Einsatz der Zwischentabelle und den Funktionen LEFT() und MID() haben wir trotz fester Feldlänge die Daten sinnvoll und ziemlich schnell importieren können. Wie man sieht, wurden nun nur noch 6.401 Sätze in die eigentliche BLZ-Tabelle übertragen. Das liegt zum einen daran, dass wir mit der WHERE-Bedingung alle zu löschenden Bankleitzahlen ausgeschlossen haben, zum anderen und vor allem aber daran, dass die diversen einzelnen Geschäftsstellen nicht mit aufgenommen wurden. Durch das DISTINCT wurde verhindert, dass eine Satzkombination mehrfach vorkommt. Man kann nun diese Tabelle dazu nutzen, Bankleitzahlen oder Banknamen automatisch heraussuchen und vorschlagen zu lassen, wenn man Kontodaten in einer Applikation für die Musikschule einträgt. Durch die Indizes auf blz und bank_name ist diese Suche auch ziemlich schnell.

5.4.3 mysqldump mysqldump

Um Daten aus einem MySQL-Server zu exportieren, kann man auch das Programm mysqldump nutzen. Es liest die Struktur und die Daten einer oder mehrerer Datenbanken oder Tabellen aus und schreibt sie in eine Datei. Mit dieser kann man die entsprechenden Strukturen danach wieder erneut aufbauen (sei es auf einem anderen Server oder auf dem gleichen Server zum Beispiel nach einer Neuinstallation) und die Daten eintragen lassen. Selbst das Erstellen der Datenbank selber wird auf Wunsch übernommen. Beim Aufruf des Programms kann man eine der drei folgenden SyntaxVarianten nutzen: mysqldump [OPTIONS] database [tables] mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] mysqldump [OPTIONS] --all-databases [OPTIONS]

In der ersten Variante gibt man eine Datenbank an, dazu die Tabellen, die exportiert werden sollen. Lässt man die Angabe der Tabellen weg, werden alle Tabellen der Datenbank exportiert. Die zweite Variante erlaubt die Auswahl mehrerer Datenbanken, für die alle Tabellen exportiert werden. Zusätzlich werden mit den erzeugten SQL-Befehlen die Datenbanken erstellt und vor dem Anlegen und Befüllen der Tabellen in diese gewechselt. In der dritten Variante schließlich werden alle Datenbanken exportiert. Die Optionen sind in Tabelle 5.3 aufgeführt.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Kurze Version

Lange Version

Beschreibung

-A

--all-databases

Exportiere alle Datenbanken des Servers.

-a

--all

Nutze alle CREATE-Optionen von MySQL.

-#

--debug=...

Ablauf des Programms protokollieren und verfolgen

--character-setsdir=...

Angabe des Verzeichnisses, in dem die Zeichensatzbeschreibungen abgelegt sind

-?

--help

Ausgabe der Hilfe-Informationen

-B

--databases

Ausgabe mehrerer Datenbanken, die nach den Optionen aufgeführt werden müssen (siehe zweite Syntaxvariante)

-c

--complete-insert

INSERT-Befehle werden komplett mit den Spaltennamen erzeugt.

-C

--compress

Nutzung der Datenkompression bei der Kommunikation zwischen Client und Server

--defaultcharacter-set=...

Angabe des Standard-Zeichensatzes

--extended-insert

Nutzung der Angabe mehrerer Datensätze mit einem INSERT-Befehl

--add-drop-table

Aufnahme des Befehls DROP TABLE vor dem Erstellen der Tabelle

--add-locks

Hinzufügen von LOCK TABLE vor den INSERTBefehlen und UNLOCK TABLE danach. Dadurch wird das Einfügen von Datensätzen beschleunigt.

--allow-keywords

Es können Schlüsselwörter als Spaltennamen genutzt werden, indem jedem Spaltenname der Tabellenname vorangestellt wird.

--delayed-insert

Einfügen von Daten mittels INSERT DELAYED.

--master-data

Hinzufügen von Ort und Dateiname des Masters bei verteilten Datenbanken

-F

--flush-logs

Wegschreiben der Log-Dateien, bevor mit dem Export begonnen wird

-f

--force

auch bei einem Fehler mit dem Exportieren fortfahren

-h

--host=...

Angabe des MySQL-Servers

-l

--lock-tables

Sperren aller Tabellen vor dem Exportieren

--no-autocommit

Hinzufügen von Befehlen zum Deaktivieren des Autocommits und dem Bestätigen am Ende der INSERT-Befehle.

--disable-keys

Deaktivieren der Schlüssel vor dem Eintragen der Daten und nachträgliches Aktivieren. Dies kann das Erstellen der Daten an ihrem neuen Ort beschleunigen.

-e

-K

Tabelle 5.3: Optionen von mysqldump

  



Sandini Bib

Kurze Version

Lange Version

Beschreibung

-n

--no-create-db

Die Befehle zum Anlegen der Datenbanken werden nicht mit ausgegeben.

-t

--no-create-info

Die Befehle zum Anlegen der Tabellen werden nicht mit ausgegeben.

-d

--no-data

Es werden nur die Strukturen exportiert.

-O

--set-variable var=option

Setzen von Variablen

--opt

Schnellstmögliche Variante zum Exportieren und Importieren von Daten. Diese Option entspricht der gemeinsamen Nutzung von --add-drop-table --add-locks --all --quick --extended-insert --lock-tables --disable-keys

-p

--password[=...]

Angabe des Kennworts oder Abfrage

-W

--pipe

Verwendung von Named Pipes für die Kommunikation mit dem Server

-P

--port=...

Angabe des Ports, über den sich der Client mit dem MySQL-Server verbinden soll

-q

--quick

direkte Ausgabe der Befehle ohne Pufferung

-Q

--quote-names

Umschließen von Tabellen- und Spaltennamen mit dem umgekehrten einfachen Anführungszeichen (`)

-r

--result-file=...

Ausgabe in die angegebene Datei statt auf die Standardausgabe

-S

--socket=...

Angabe der zu verwendenden Socket-Datei

--tables

Überschreiben der Option --databases

-T

--tab=...

Ausgabe der Daten als Textdatei. Für jede Tabelle werden zwei Dateien erstellt: eine SQL-Datei mit den Befehlen zum Anlegen der Struktur und eine Textdatei mit den Daten. Diese können später mit LOAD DATA INFILE eingelesen werden. Erstellt werden die Dateien im angegebenen Verzeichnis. Die Textdateien lassen sich wie bei SELECT INTO formatieren, dazu dienen die entsprechenden Optionen, die am Ende der Tabelle beschrieben sind.

-u

--user=...

Angabe des Benutzers, der sich anmelden soll

-v

--verbose

ausführlichere Ausgabe von Informationen während des Exportierens

-V

--version

Ausgabe der Version und Beenden des Programms

-w

--where=...

Angabe von WHERE-Bedingungen, die für die auszugebenden Datensätze erfüllt sein müssen. Anführungszeichen sind notwendig.

Tabelle 5.3: Optionen von mysqldump (Forts.)



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Kurze Version

Lange Version

Beschreibung

-X

--xml

Ausgabe der Daten als wohlgeformte XMLDatei

-x

--first-slave

Sperren aller Datensätze in allen Datenbanken (bei verteilten Systemen)

--fieldsterminated-by=...

Angabe der Feldtrenner bei --tab. Standard ist \t.

--fieldsenclosed-by=...

Angabe der Quoting-Zeichen bei --tab

--fields-optionally-enclosed-by=...

Angabe der optionalen Quoting-Zeichen bei --tab

--fields-escapedby=...

Angabe des Fluchtzeichens bei --tab

--linesterminated-by=...

Angabe der Datensatztrenner bei --tab. Standard ist \n.

Tabelle 5.3: Optionen von mysqldump (Forts.)

Standardmäßig werden die SQL-Befehle direkt in die Standardausgabe geschrieben. prompt>mysqldump musikschule -- MySQL dump 8.22 --- Host: localhost Database: musikschule ---------------------------------------------------------- Server version3.23.51-nt --- Table structure for table 'ausleihe' -CREATE TABLE ausleihe ( ausleihe_id int(10) unsigned NOT NULL auto_increment, leihinstrument_id int(10) unsigned NOT NULL default '0', schueler_id int(10) unsigned NOT NULL default '0', datum_von date NOT NULL default '0000-00-00', datum_bis date default NULL, PRIMARY KEY (ausleihe_id) ) TYPE=MyISAM; --- Dumping data for table 'ausleihe' ---- Table structure for table 'instrument' --

  



Sandini Bib

CREATE TABLE instrument ( instrument_id int(10) unsigned NOT NULL auto_increment, instr_name varchar(50) NOT NULL default '', instr_gruppe varchar(50) default NULL, PRIMARY KEY (instrument_id) ) TYPE=MyISAM; --- Dumping data for table 'instrument' --

INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT ...

INTO INTO INTO INTO INTO INTO INTO INTO

instrument instrument instrument instrument instrument instrument instrument instrument

VALUES VALUES VALUES VALUES VALUES VALUES VALUES VALUES

(1,'Querflöte','Holzbläser'); (2,'Klarinette','Holzbläser'); (3,'Violine','Streicher'); (4,'Viola','Streicher'); (5,'Posaune','Blechbläser'); (6,'Trompete','Blechbläser'); (7,'Klavier','Tasten'); (8,'Keyboard','Tasten');

Diese lässt sich natürlich in eine Datei umbiegen: prompt>mysqldump musikschule > musikschule.sql

Oder man nutzt die Option -r bzw. --result-file: prompt>mysqldump --result-file=musikschule.sql musikschule

Solch eine erzeugte Datei kann man mit mysql auf einem anderen Server wieder einlesen: prompt>mysql musikschule < musikschule.sql

Möchte man die Daten später mit mysqlimport wieder einlesen (siehe Kapitel 5.4.4), muss man mit der Option --tab arbeiten. Die Daten werden dann in Dateien geschrieben, die gleich den richtigen Namen besitzen: prompt>mysqldump --tab=musikschule musikschule

Damit werden in das Verzeichnis musikschule Dateien für alle Tabellen der Datenbank musikschule geschrieben. Um Daten von einem MySQL-Server auf einen anderen zu verschieben, kann man auch eine andere, elegantere Möglichkeit nutzen. Dazu sollte man sich auf dem Server mit dem Ausgangspunkt der Daten befinden. Dann kann man die Ausgabe des Programms gleich als Eingabe für mysql auf dem Zielserver nutzen:



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

prompt>mysqldump --opt musikschule | mysql --h remotehost -C musikschule

Durch --opt wird der schnellstmögliche Weg zum Exportieren gewählt, während -C dafür sorgt, dass die Kommunikation mit dem entfernten Rechner komprimiert vonstatten geht.

5.4.4 mysqlimport Mit dem Programm mysqlimport kann man – genau wie bei LOAD DATA INFILE – Daten aus Textdateien in die Datenbank laden. Im Grunde genommen stellt mysqlimport nur eine Kommandozeilenschnittstelle für den SQL-Befehl bereit. Viele Optionen sind identisch zu denen von LOAD DATA INFILE. Der Aufruf des Programms geschieht mit der folgenden Syntax:

mysqlimport

mysqlimport [OPTIONS] database textfile1 [textfile2 ...]

Von den angegebenen Textdateien werden alle vorhandenen Erweiterungen abgeschnitten und der eigentliche Dateiname als Name der Tabelle genommen, in die die Daten aus der entsprechenden Datei importiert werden sollen. So würden zum Beispiel die Daten der Datei unterricht.txt in die Tabelle unterricht importiert werden. Die verwendete Datenbank muss auch mit angegeben werden. Als mögliche Optionen stehen die in Tabelle 5.4 aufgeführten bereit. Viele sollten bereits bekannt sein. Kurze Version

Lange Version

Beschreibung

-#

--debug[=...]

Ablauf des Programms protokollieren und verfolgen

-?

--help

Ausgabe der Hilfe-Informationen

–-default-character-set=...

Angabe des Standard-Zeichensatzes

--character-setsdir=...

Angabe des Verzeichnisses mit den Zeichensätzen

-c

–-columns=...

Angabe der Spalten, die für den Import genutzt werden sollen. Diese Option entspricht dem Passus (col_name, ...) in LOAD DATA INFILE.

-C

–-compress

Nutzung der Datenkompression bei der Kommunikation zwischen Client und Server

-d

–-delete

Die zu befüllenden Tabellen werden vor dem Import geleert.

-f

–-force

Der Import soll weitergehen, auch wenn ein Fehler aufgetreten ist. Existiert zum Beispiel eine Tabelle nicht, wird das Programm ohne diese Option abgebrochen.

Tabelle 5.4: Optionen von mysqlimport

  



Sandini Bib

Kurze Version

Lange Version

Beschreibung

-h

–-host=...

Angabe des MySQL-Servers

-i

–-ignore

Zu importierende Daten mit Schlüsseln, die in der Tabelle schon vorhanden sind, werden ignoriert. Dies entspricht der Option IGNORE bei LOAD DATA INFILE.

-l

–-lock-tables

Sperren aller Tabellen des Servers bevor der Import beginnt. Damit ist sichergestellt, dass alle Tabellen synchron sind.

-L

–-local

Die Dateien werden auf dem Client eingelesen und zum Server übertragen. Ohne diese Angabe werden die Daten auf dem Server gesucht.

–-low-priority

Die Daten werden mit geringerer Priorität in die Tabellen geschrieben (entspricht LOW_PRIORITY bei LOAD DATA INFILE).

-p

–-password[=...]

Angabe des Kennwortes für den Benutzer oder Abfragen bei Nichtangabe

-W

–-pipe

Nutzung von Named Pipes für die Kommunikation zwischen Client und Server

-r

–-replace

Existieren schon Daten in der Tabelle mit Schlüsseln, die auch in der Datei vorkommen, werden die bestehenden Daten überschrieben. Dies entspricht der Option REPLACE in LOAD DATA INFILE.

-s

–-silent

„Stille“ Arbeitsweise. Es werden nur Fehlermeldungen ausgegeben.

-S

–-socket=...

Angabe der zu verwendenden Socket-Datei

-u

–-user=...

Angabe des Benutzers, der angemeldet werden soll

-v

–-verbose

ausführliche Statusausgaben während des Programmlaufs

-V

–-version

Ausgabe der Versionsangaben und Beenden des Programms

--fieldsterminated-by=...

Angabe der Feldtrenner. Standard ist \t.

--fieldsenclosed-by=...

Angabe der Quoting-Zeichen

--fields-optionally-enclosed-by=...

Angabe der optionalen Quoting-Zeichen

--fields-escapedby=...

Angabe des Fluchtzeichens

--lines-terminated-by=...

Angabe der Datensatztrenner. Standard ist \n.

Tabelle 5.4: Optionen von mysqlimport (Forts.)



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Möchte man also nun Daten importieren, kann man wie folgt vorgehen: prompt>cd musikschule prompt>mysqlimport --delete musikschule ausleihe.txt instrument.txt musikschule.ausleihe: Records: 0 Deleted: 0 Skipped: 0 Warnings: 0 musikschule.instrument: Records: 8 Deleted: 0 Skipped: 0 Warnings: 0

Durch --delete oder -d wird dafür gesorgt, dass keine alten Daten in den Tabellen verbleiben. Das möchte man natürlich ab und an gerne haben, in diesem Fall aber geht es eben um den direkten Import der alten Daten. Im Folgenden wird dann für jede Tabelle angegeben, wie viele Datensätze importiert wurden, ob es doppelte gab und ob Warnungen auftraten. Möchte man mehr über den aktuellen Status der Verarbeitung erfahren, kann man die Option --verbose oder -v nutzen: prompt>mysqlimport -d -v musikschule ausleihe.txt instrument.txt Connecting to localhost Selecting database musikschule Deleting the old data from table ausleihe Loading data from file: ausleihe.txt into ausleihe musikschule.ausleihe: Records: 0 Deleted: 0 Skipped: 0 Warnings: 0 Deleting the old data from table instrument Loading data from file: instrument.txt into instrument musikschule.instrument: Records: 8 Deleted: 0 Skipped: 0 Warnings: 0 Disconnecting from localhost

5.5

Schnittstellen

Es ist nun sicherlich sehr praktisch, die verschiedensten Programme zur Verfügung zu haben, um auf den MySQL-Server und dessen Daten zugreifen zu können. Allerdings sind dies alles Programme, die nicht unbedingt sinnvoll für einen Endbenutzer sind. Dieser erwartet eine Oberfläche, die die Daten sinnvoll angeordnet anzeigt, Eingabeprüfungen vornimmt und einfache Suchmöglichkeiten anbietet. Zudem sollte die Ablauf- oder Geschäftslogik unter der Oberfläche verborgen sein und nicht vom Benutzer selber beachtet werden müssen („Immer, wenn ein Kunde mehr als _ 10.000 Umsatz pro Jahr macht, erhält er automatisch 3% Rabatt.“). Das Programm kann MySQL einem natürlich nicht zur Verfügung stellen. Dafür ist der Applikationsentwickler selber verantwortlich. Aber um auf die Datenbank zugreifen zu können, benötigt er Schnittstellen. Es gibt sie für die verschiedensten Programmiersprachen. Ich möchte hier die am häufigsten genutzten vorstellen: Perl und C++ sowie allgemeiner und sprachunabhängiger MyODBC. PHP wird auch sehr häufig

6FKQLWWVWHOOHQ



Sandini Bib

für die Anbindung eines Web-Servers an eine MySQL-Datenbank genutzt. Da es dafür aber ein eigenes Kapitel gibt, werde ich hier nicht näher darauf eingehen.

5.5.1 Perl Perl ist eine ideale interpretierende Programmiersprache, um kleine (und auch größere) Skripten zu schreiben. Sie sind schnell erstellt, trotzdem aber auch flexibel und vor allem die Fähigkeiten zur Zeichenkettenverarbeitung sind sehr umfangreich. Die zentrale Stelle für Perl im Internet ist http://www.perl.com. Während unter Linux und anderen Unix-Derivaten Perl meist schon vorhanden ist, muss man es sich unter Windows erst installieren. Dafür findet man unter http://www.activestate.com/ActivePerl quasi die Perl-Version für Windows. Auf der beiliegenden CD-ROM finden Sie im Verzeichnis Perl die MSIInstallationsdatei für ActivePerl unter Windows. Der Dateiname lautet ActivePerl-5.6.1.633-MSWin32-x86.msi. Der Zugriff auf MySQL mit Perl geschieht über die Schnittstelle DBI. Dies ist eine universelle Datenbankschnittstelle für Perl und unterstützt unter anderem auch MySQL. Es gibt noch andere, ältere Zugriffsvarianten, aber mittlerweile sollte man auf jeden Fall DBI nutzen. Falls DBI und der MySQL-Zugriff unter Linux noch nicht vorhanden sind, muss man sich die entsprechenden Module von http:// www.mysql.com/Downloads/Contrib/ herunterladen. Mindestens notwendig sind Data-Dumper, DBI und msql-mysql-modules. Die entsprechenden Dateien Data-Dumper-2.101.tar.gz, DBI-1.18.tar.gz und MsqlMysql_modules-1.2216.tar.gz finden Sie auch auf der beiligenden CDROM im Verzeichnis Perl. Dabei handelt es sich um komprimierte Tar-Archive, die mittels prompt> gunzip < MODULE-VERSION.tar.gz | tar xvf -

ausgepackt werden können. MODULE-VERSION steht dabei für eines der aufgeführten Module einschließlich der Versionsbezeichnung. Danach wechselt man in das entsprechende Verzeichnis des Moduls: prompt> cd MODULE-VERSION



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Dort baut man die Distribution und kompiliert alles: prompt> prompt> prompt> prompt>

perl Makefile.PL make make test make install

Zu beachten ist noch, dass make test für msql-mysql-modules nur dann erfolgreich funktionieren wird, wenn der MySQL-Server läuft. Um DBI und die Unterstützung für MySQL unter Windows zu installieren, müssen Sie den PPM (Programmer's Package Manager) von ActivePerl aufrufen. Er benötigt eine Internetverbindung oder – falls vorhanden – Zugriff auf ein lokales Repository (wie zum Beispiel die CD von ActiveState). prompt>ppm

Nun kann DBI installiert werden: ppm> install DBI

Ist dies erfolgreich geschehen, kann man die MySQL-Unterstützung installieren (bitte den folgenden Befehl auf einer Zeile eingeben): ppm> install ftp //ftp.de.uu.net/pub/CPAN/authors/id/JWIED/DBD-mysql-1.2212.x86.ppd

Mit ppm> exit

wird der PPM beendet und es steht die DBI und der MySQL-Support zur Verfügung. Ein einfaches Perl-Programm, welches auf unsere Datenbank zugreift, sieht so aus: #! /usr/bin/perl # mysql01.pl - Einfaches Beispiel zum Verbinden mit MySQL use DBI; use strict; :my ($dsn) = "DBI:mysql:musikschule"; # Verbindungstyp und Datenbankname my ($user_name); # Benutzername (hier leer) my ($password); # Kennwort (hier leer) my ($dbh, $sth); # Verbindungshandle, Query-Handle my ($query); # SELECT-Befehl my (@ary); # Datensatz

6FKQLWWVWHOOHQ



Sandini Bib

# Verbindung aufbauen $dbh = DBI->connect($dsn, $user_name, $password, {RaiseError => 1}); #SELECT-Befehl definieren $query = "SELECT raum_id, raum_name, etage, personen " ."FROM raum ORDER BY raum_name"; # Anfrage vorbereiten und ausführen $sth = $dbh->prepare($query); $sth->execute(); # Spaltenüberschriften ausgeben print "raum_id\traum_name\tetage\tpersonen\n\n"; # Schleife über alle Datensätze while(@ary = $sth->fetchrow_array()) { # Ausgabe des Datensatzes print join("\t", @ary), "\n"; } # Freigeben des Query-Handles $sth->finish(); # Verbindung beenden $dbh->disconnect(); exit(0);

Sie finden das Skript mit dem Dateinamen mysql01.pl auf der beiliegenden CD-ROM. Ich möchte hier nicht die Programmierung mit Perl an sich erklären. Aber anhand dieses Beispielprogramms will ich auf die Elemente eingehen, die für DBI notwendig sind. #! /usr/bin/perl # mysql01.pl - Einfaches Beispiel zum Verbinden mit MySQL

Alle Zeichen hinter einem # werden als Kommentar betrachtet und ignoriert. Die erste Zeile dient unter Unix dazu, bei Aufruf von prompt> mysql01.pl

direkt den Perl-Interpreter zu starten und das Programm auszuführen.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

use DBI; use strict;

Mit diesen beiden Befehlen wird der Perl-Interpreter dazu gebracht, das Modul DBI zu laden, um es später nutzen zu können. Mit use strict wird dafür gesorgt, dass Variablennamen zunächst deklariert werden müssen, bevor man sie verwenden kann. Das ist zwar nicht unbedingt nötig, aber sehr sinnvoll, um später nicht ewig nach Fehlern zu suchen, die darauf beruhen, dass man eine Variable mit $myWonderfulLongVariableName anspricht, die in Wirklichkeit $myWonderfulLongVarName heißt (und dann natürlich keinen sinnvollen Inhalt hat). my my my my my my

($dsn) = "DBI:mysql:musikschule"; ($user_name); ($password); ($dbh, $sth); ($query); (@ary);

# # # # # #

Verbindungstyp und Datenbankname Benutzername (hier leer) Kennwort (hier leer) Verbindungshandle, Query-Handle SELECT-Befehl Datensatz

Mit my(...) werden Variablen deklariert. Normale Variablen haben ein $ am Anfang ihres Namens, Arrays ein @. Hier kommen jetzt als Variablennamen verschiedene Bezeichnungen ins Spiel, die häufig im Zusammenhang mit DBI verwendet werden: • $dsn: enthält meist die Verbindungsdaten (DSN, Data Source Name), hier zum Beispiel den Datenbanktyp und den Datenbanknamen. • $user_name, $password: Diese Variablennamen sprechen wohl für sich. • $dbh: Ein sogenannter Handle, der als Variable die Verbindung an sich repräsentiert. Ihn brauchen wir im Folgenden immer wieder, da er alle Daten enthält, die für die Verbindung notwendig sind. • $sth: Ein weiterer Handle, der für die Verarbeitung einer Anfrage notwendig ist. Ihm wird der SELECT-Befehl übergeben und von ihm erhält man die Ergebnisse. • $query: Der String, der die eigentliche SQL-Anfrage enthält. Es macht Sinn, den SQL-Befehl in einer eigenen Variable zusammenzubauen, um sie leichter ausgeben zu können, wenn man Probleme mit der Anfrage hat. • @ary: Ein Array, in dem der Inhalt eines Datensatzes gespeichert wird. Die Variable $dsn wird hier gleich mit den notwendigen Verbindungsdaten gefüllt, die durch den Doppelpunkt getrennt sind. Zunächst kommt die Angabe DSI, dann der Datenbanktyp mysql, schließlich der Name der Datenbank musikschule. Handelt es sich nicht um die Datenbank auf localhost, kann man zum Schluss (wieder durch einen Doppelpunkt abgeteilt) auch noch den Rechnernamen angeben.

6FKQLWWVWHOOHQ



Sandini Bib

$dbh = DBI->connect($dsn, $user_name, $password, {RaiseError => 1});

Mit diesem Befehl wird die Verbindung zur Datenbank aufgebaut und der Handle $dbh mit Leben gefüllt. {RaiseError => 1} sorgt dafür, dass bei einem Fehler im Verbindungsaufbau das Skript beendet wird und nicht mit einer Warnung weiterläuft. Normalerweise wird man sicherlich eine ausgefeiltere Fehlerbehandlung durchführen, aber wir haben es hier schließlich nur mit einem einfachen Skript zu tun. $query = "SELECT raum_id, raum_name, etage, personen " ."FROM raum ORDER BY raum_name"; $sth = $dbh->prepare($query); $sth->execute();

Nun wird die Abfrage definiert und der Query-Handle mit prepare(...) vorbereitet. Danach lässt er sich mit execute() ausführen. print "raum_id\traum_name\tetage\tpersonen\n\n";

Zunächst werden jetzt die Feldnamen ausgegeben. while(@ary = $sth->fetchrow_array()) { # Ausgabe des Datensatzes print join("\t", @ary), "\n"; }

Nun wird in einer Schleife so lange die Methode fetchrow_array() aufgerufen, bis sie keine Ergebnisse mehr zurückliefert. Diese Methode liest den aktuellen Datensatz aus, gibt die Werte der Spalten in einem Array zurück und springt dann zu nächsten Datensatz. Handelt es sich dabei um den letzten Satz, wird beim nächsten Aufruf ein leeres Array zurückgegeben. Es gibt noch weitere Befehle zum Auslesen von Datensätzen, zum Beispiel fetchrow_arrayref(), fetch() und fetchrow_hashref(), die in anderen Situationen einsetzbar sind. print join("\t", @ary) gibt die einzelnen Elemente des Arrays durch Tabulatorzeichen getrennt aus. $sth->finish(); $dbh->disconnect(); exit(0); finish() gibt die Ressourcen wieder frei, die der Query-Handle benötigt hat. disconnect() trennt die Verbindung zur Datenbank und exit(0)

schließlich beendet das Programm mit einem normalen Code.



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Ruft man dieses Programm jetzt auf (zum Beispiel mit perl mysql01.pl), erhält man folgende Ausgabe: prompt>perl mysql01.pl raum_id raum_name etage 4 5 1 2 3

A5.21 A5.23 A5.33 B2.02 C4.16

5 5 5 2 4

personen

6 6 4 20 11

Möchte man Befehle ausführen, die im Allgemeinen keine Ergebnismenge zurückliefern, kann man statt execute() die Methode do(...)des Verbindungshandles $dbh nutzen. Mit ihr lassen sich zum Beispiel UPDATE, DELETE und INSERT ausführen. Hier wieder ein kleines Beispielskript: #! /usr/bin/perl # mysql02.pl - Einfaches Beispiel zum Ausführen von DML-Befehlen use DBI; use strict; :my ($dsn) = "DBI:mysql:musikschule"; # Verbindungstyp und Datenbankname my ($user_name); # Benutzername (hier leer) my ($password); # Kennwort (hier leer) my ($dbh); # Verbindungshandle my ($query); # SELECT-Befehl my ($rows); # Anzahl der betroffenen Zeilen # Verbindung aufbauen $dbh = DBI->connect($dsn, $user_name, $password, {RaiseError => 1}); # UPDATE ausführen $rows = $dbh->do("UPDATE raum SET personen = 9 WHERE raum_id = 2"); # Verbindung beenden $dbh->disconnect(); exit(0);

Sie finden das Skript mit dem Dateinamen mysql02.pl auf der beiliegenden CD-ROM. do(...) liefert die Menge der betroffenen Zeilen zurück, wobei allerdings INSERT, UPDATE und DELETE immer 0E0 vermelden. Bei einem Fehler würde dagegen der Wert undef ausgegeben werden.

6FKQLWWVWHOOHQ



Sandini Bib

Richtig nett wird es natürlich, wenn man die SQL-Befehle selber zusammenbaut. Dann kann man zum Beispiel folgendes Skript schreiben, welches einen Tabellennamen als Parameter enthält und den Inhalt dieser Tabelle ausgibt: #! /usr/bin/perl # mysql03.pl table_name - Einfaches Beispiel zum Anzeigen von Tabellen use DBI; use strict; my my my my my my

($dsn) = "DBI:mysql:musikschule"; ($user_name); ($password); ($dbh, $sth); ($query); (@ary);

# # # # # #

Verbindungstyp und Datenbankname Benutzername (hier leer) Kennwort (hier leer) Verbindungshandle, Query-Handle SELECT-Befehl Datensatz

# Verbindung aufbauen $dbh = DBI->connect($dsn, $user_name, $password, {RaiseError => 1}); #SELECT-Befehl definieren $query = "SELECT * FROM ".$ARGV[0]; # Anfrage vorbereiten und ausführen $sth = $dbh->prepare($query); $sth->execute(); # Spaltenüberschriften ausgeben print join("\t", @{$sth->{NAME}}), "\n"; # Schleife über alle Datensätze while(@ary = $sth->fetchrow_array()) { # Ausgabe des Datensatzes print join("\t", @ary), "\n"; } # Freigeben des Query-Handles $sth->finish(); # Verbindung beenden $dbh->disconnect(); exit(0);



=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

Sie finden das Skript mit dem Dateinamen mysql03.pl auf der beiliegenden CD-ROM. Hier haben wir die $query zusammengesetzt, um mit $ARGV[0] den Namen der Tabelle einbauen zu können. Natürlich muss man eigentlich noch Fehlerbehandlungsroutinen einbauen… Interessant ist noch die folgende Zeile: print join("\t", @{$sth->{NAME}}), "\n"; $sth->{NAME} liefert eine Referenz auf ein Array zurück (die dann mit @{...} aufgelöst wird), in der die Namen der zurückgegebenen Spalten

stehen. Somit kann man hier unabhängig von der Tabelle die Spaltennamen ausgeben lassen: prompt>perl mysql03.pl instrument instrument_id instr_name instr_gruppe 1 Querflöte Holzbläser 2 Klarinette Holzbläser 3 Violine Streicher 4 Viola Streicher 5 Posaune Blechbläser 6 Trompete Blechbläser 7 Klavier Tasten 8 Keyboard Tasten

Um eine SQL-Abfrage universell verwendbar zu machen, gibt es (hauptsächlich) zwei verschiedene Varianten: der jeweilige Aufbau eines SQLBefehls oder die Verwendung von Platzhaltern: • Die erste Variante haben wir im Prinzip schon kennengelernt: innerhalb des Abfrage-Strings wird eine Variable genutzt: $query = "SELECT * FROM schueler WHERE nachname LIKE '$name'"; $sth = $dbh->prepare($query); $sth->execute(); $name wird von Perl innerhalb von doppelten Anführungszeichen

durch den Inhalt der Variablen ersetzt. Möchte man doppelte Anführungszeichen innerhalb des SQL-Befehls nutzen, muss man sie durch ein Fluchtzeichen (\) maskieren: $query = "SELECT * FROM lehrer WHERE plz = \"$plz\"";

Eleganter und weniger fehleranfällig ist die Verwendung von qq{}. Alles zwischen den geschweiften Klammern (für die man im Übrigen auch andere Zeichen wie // oder () nutzen kann, die allerdings häufiger selbst in Strings vorkommen) wird dann als Zeichenkette interpre-

6FKQLWWVWHOOHQ



Sandini Bib

tiert und so ausgewertet, als ob es von doppelten Anführungszeichen umschlossen wäre. Man muss sich dabei aber keine Gedanken um das Maskieren der Anführungszeichen machen: $query = qq{SELECT * FROM lehrer WHERE plz = "$plz"};

Die zweite Variante ist etwas aufwändiger. Dafür ermöglicht sie die mehrfache Verwendung von SQL-Befehlen mit verschiedenen Werten, ohne dass der Befehl vom Server neu analysiert werden müsste. Das spart Zeit, wenn man häufig gleiche Anfragen, nur mit unterschiedlichen Werten ausgestattet, schickt. Allerdings ist dieser Vorteil bei MySQL gerade nicht gegeben, da MySQL die SQL-Befehle nicht für eine spätere Verwendung zwischenspeichert. Es kann sich aber trotzdem anbieten, den Zugriff auf diese Weise zu realisieren, falls man später einmal auch auf andere Datenbanksysteme zugreifen möchte. Will man Platzhalter nutzen, verwendet man an Stelle der Werte das Fragezeichen im SQL-Befehl: $query = "SELECT * FROM schueler WHERE nachname = ? AND vorname = ?"; $sth = $dbh->prepare($query);

Bei der Ausführung des Befehls werden execute() nun die entsprechenden Parameter in der angegebenen Reihenfolge mitgegeben: $sth->execute("Schmidt", "Thomas");

Platzhalter lassen sich auch für die Ausgabe verwenden. So kann man die Ergebnisspalten direkt mit Variablen verknüpfen, die dann bei jedem fetch automatisch mit den aktuellen Werten gefüllt werden. Das folgende Skript gibt die Adressen aller Schüler aus, deren Nachname mit den als Parameter übergebenen Buchstaben beginnt: #! /usr/bin/perl # mysql04.pl lastname - Einfaches Beispiel zum Verknüpfen von # Platzhaltern use DBI; use strict; my my my my my my my



($dsn) = "DBI:mysql:musikschule"; # Verbindungstyp und Datenbankname ($user_name); # Benutzername (hier leer) ($password); # Kennwort (hier leer) ($dbh, $sth); # Verbindungshandle, Query-Handle ($query); # SELECT-Befehl ($queryname) = $ARGV[0]; # Filterkriterium ($lastname, $firstname, $street, $plz, $city); # Ausgabevariablen

=XJULIIVP|JOLFKNHLWHQ

Sandini Bib

# Anfügen eines Prozentzeichens, um immer auch nach Namensstücken suchen # zu können $queryname = $queryname."%"; # Verbindung aufbauen $dbh = DBI->connect($dsn, $user_name, $password, {RaiseError => 1}); #SELECT-Befehl definieren $query = "SELECT nachname, vorname, strasse, plz, ort " ."FROM schueler WHERE nachname LIKE ?"; # Anfrage vorbereiten und ausführen $sth = $dbh->prepare($query); $sth->execute($queryname); # Ausgabespalten mit Variablen verbinden $sth->bind_col(1, \$lastname); $sth->bind_col(2, \$firstname); $sth->bind_col(3, \$street); $sth->bind_col(4, \$plz); $sth->bind_col(5, \$city); # Schleife über alle Datensätze print "$firstname $lastname\n$street\n$plz $city\n\n" while $sth->fetch(); # Freigeben des Query-Handles $sth->finish(); # Verbindung beenden $dbh->disconnect(); exit(0);

Sie finden das Skript mit dem Dateinamen mysql04.pl auf der beiliegenden CD-ROM. Entscheidend für die Ausgabebindung ist hier der Befehl bind_col(...). Er sorgt dafür, dass die angegebenen Variablen mit der entsprechenden Ausgabespalte der Abfrage verbunden werden. Alternativ kann man auch alle Variablen auf einmal verbinden, indem man den Befehl bind_columns(...) nutzt: $sth->bind_columns(\$lastname, \$firstname, \$street, \$plz, \$city);

Wichtig ist noch, dass bind_col(...) bzw. bind_columns(...) erst nach execute(...) ausgeführt wird.

6FKQLWWVWHOOHQ



Sandini Bib

5.5.2 C++ Um mittels der Programmiersprache C++ auf MySQL zugreifen zu können, benötigt man MySQL++, die entsprechende API von MySQL. Sie bietet eine umfassende Klassenstruktur, die man ähnlich nutzen kann wie andere Klassen der STL (Standard Template Library) von C++. Man kann mit MySQL++ sehr einfache und gut lesbare Programme schreiben, aber sie bietet eigentlich auch alles an, was man in irgendeiner Form für den Zugriff und die Kommunikation mit MySQL benötigt. MySQL++ findet man auf den Web-Seiten von MySQL im ContribBereich (http://www.mysql.com/Downloads/Contrib/). Die benötigten Dateien für Red Hat Linux und Windows (Borland und Visual C++) finden Sie auch auf der beiliegenden CD-ROM im Verzeichnis MySQL++. Ein einfaches Beispiel für den Zugriff auf MySQL ist das folgende Programm, welches den Beispielen von MySQL++ entnommen und für die Musikschule leicht modifiziert wurde: // mysql01.cpp // Ausgabe von Daten #include #include #include #include



int main() { try { Connection con("musikschule"); // Erstellen eines Query-Objektes, welches mit con verbunden ist Query query = con.query(); // Definieren der Abfrage query libtool --finish

Nun ist die PHP-Unterstützung in der Konfigurationsdatei httpd.conf von Apache eingetragen, allerdings werden Dateien mit der Endung .php noch nicht ausgewertet, sondern nur an den Client übertragen (was nicht Sinn der Sache ist). Daher öffnet man die Datei /usr/local/ apache2/conf/httpd.conf mit einem Editor und fügt folgende Zeile an einer Stelle hinzu, wo es schon ähnliche Einträge gibt: AddType application/x-httpd-php .php



 

Sandini Bib

Gleichzeitig kann man kontrollieren, ob das PHP-Modul auch eingetragen wurde. Es muss in der Datei eine Zeile geben, die so aussieht: LoadModule php4_module modules/libphp4.so

Nach dem Speichern der Änderungen stoppt man den Apache und startet ihn danach neu: prompt> /usr/local/apache2/bin/apachectl stop prompt> /usr/local/apache2/bin/apachectl start

Erstellt man nun eine Datei info.php im Verzeichnis /usr/local/apache2/ htdocs mit folgendem Inhalt, kann man diese im Web-Browser aufrufen und sollte eine Anzeige wie in Abbildung 7.6 erhalten.

PHP Test



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter info.php auf der beiliegenden CD-ROM.

Abbildung 7.6: PHP-Info mit Apache unter Linux/Red Hat

/$03:$03



Sandini Bib

Installation unter Windows Das Vorgehen ist recht einfach. Zunächst lädt man sich bequemerweise die fertigen Binaries von http://www.php.net/downloads.php herunter. Dabei hat man zwei Möglichkeiten: Entweder nutzt man die InstallerVersion, die klein ist und sich einfach installieren lässt, dafür aber nur eingeschränkte Möglichkeiten in der Zusammenarbeit mit dem WebServer bietet, oder man holt sich empfehlenswerterweise gleich die per ZIP gepackte Version, die alles bereitstellt, was PHP so anbietet und vor allem in der Zusammenarbeit mit dem Apache-Web-Server eine deutlich bessere Leistung möglich macht. Diese Datei hat beim Schreiben dieses Buches den Namen php-4.2.3-Win32.zip gehabt, mittlerweile kann es schon neuere Versionen geben. Sie finden die Datei php-4.2.3-Win32.zip auch auf der beiliegenden CD-ROM im Verzeichnis PHP. Diese Datei packt man nun mit einem Entpacker dorthin aus (zum Beispiel WinZIP), wo man es später auch nutzen will, zum Beispiel c:\php. Dabei sollte man darauf achten, dass die Verzeichnisstruktur erhalten bleibt. Zudem wird empfohlen, einen Pfad ohne Leerzeichen zu nutzen, da manche Web-Server damit Probleme haben könnten. Möchte man das Ganze unter Windows 95 laufen lassen (was allerdings nicht wirklich empfehlenswert ist), muss man sich vorher noch das DCOM-Update http://download.microsoft.com/msdownload/dcom/95/x86/ en/dcom95.exe holen. Weiterhin wird empfohlen, bei älteren WindowsVersionen (9x/NT) die neueste MDAC-Version zu besorgen (unter http:// www.microsoft.com/data/) und zu installieren. Nun sorgt man dafür, dass die nötigen DLLs vom System und vom WebServer gefunden werden. Dabei handelt es sich um die Datei php4ts.dll sowie um alle DLLs im Unterverzeichnis dlls. Diese sollten am einfachsten in das Systemverzeichnis von Windows kopiert werden: meist c:\windows\system unter Windows 9x/ME/XP oder c:\winnt\system32 unter Windows NT/2000. Danach kopiert man die Datei php.ini-recommended ebenfalls in das Systemverzeichnis und benennt sie dort in php.ini um. In dieser Datei muss man jetzt nur noch (zumindest bei Nutzung mit dem Apache) das Verzeichnis setzen, in dem sich die möglichen Erweiterungen von PHP befinden. Irgendwo etwa in der Mitte der Datei findet sich eine Zeile, in der das extension_dir gesetzt wird. Die sollte in unserem Beispiel so aussehen: extension_dir = "c:\php\extensions"



  

Sandini Bib

Jetzt kommen wir zu dem Teil, bei dem Apache und PHP miteinander bekannt gemacht werden. Dazu hält man den Apache-Server an (zum Beispiel mit NET STOP APACHE an der Kommandozeile) und editiert die Datei httpd.conf im Verzeichnis conf des Web-Servers. Um PHP als Modul unter Apache laufen zu lassen (was der Nutzung per cgi-bin aus Sicherheits-, Stabilitäts- und Performancegründen deutlich vorzuziehen ist), müssen die folgenden beiden Zeilen in der Datei auftauchen: LoadModule php4_module c:/php/sapi/php4apache.dll AddType application/x-httpd-php .php .phtml

Nun kann man Apache wieder starten (zum Beispiel mit NET START APACHE). Um zu testen, ob PHP funktioniert, erzeugt man am besten eine Web-Seite mit folgendem Inhalt und speichert sie als info.php im Verzeichnis htdocs des Apache-Verzeichnisses ab:

PHP Test



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter info.php auf der beiliegenden CD-ROM. Ruft man nun diese Seite im Browser auf, sollten Informationen über die PHP-Installation ausgegeben werden (siehe Abbildung 7.7). Mit PHP hat man nun alle Komponenten für einen LAMP/WAMP-Server zusammen. Wie der Zugriff von einer Web-Seite mit PHP auf MySQL erfolgt, wird im Abschnitt 7.3 dieses Kapitels besprochen.

/$03:$03



Sandini Bib

Abbildung 7.7: PHP-Info mit Apache unter Windows

7.2.5 IIS-Nutzung IIS



Unter Windows lässt sich PHP auch zusammen mit dem Internet Information Server (IIS) von Microsoft betreiben. Falls der IIS noch nicht eingerichtet ist, muss man ihn zunächst installieren. Für Windows 2000 ist das Vorgehen im Folgenden beschrieben, andere Windows-Versionen sind aber ähnlich zu behandeln. Man ruft in der Systemsteuerung (Start/Einstellungen/Systemsteuerung) den Punkt Software auf. Hier klickt man dann auf Windows-Komponenten hinzufügen/entfernen. Es erscheint der Assistent für Windows-Komponenten (siehe Abbildung 7.8), in dem man das Kontrollkästchen der Komponente Internet-Informationsdienst (IIS) aktiviert. Nach dem Klick auf die Schaltfläche Weiter wird der IIS nun installiert, man benötigt dazu im Allgemeinen die CD mit dem Betriebssystem.

  

Sandini Bib

Abbildung 7.8: Assistent für Windows-Komponenten

Nach erfolgreicher Installation lässt sich der IIS über Start/Programme/ Verwaltung/Internetdienste-Manager administrieren (siehe Abbildung 7.9). Hier kann man die Dienste für das HTTP- und FTP-Protokoll starten und beenden sowie die Einstellungen anpassen. Die Dateien, die den Browsern übermittelt werden sollen, liegen meist unter C:\Inetpub\ wwwroot.

Abbildung 7.9: MMC-Konsole des Internet Information Servers

Um PHP mit dem IIS zu nutzen, muss man es zunächst – wie in Kapitel 7.2.4 im Abschnitt über die Installation unter Windows beschrieben – installieren. Dabei müssen alle Schritte bis zu der Stelle durchgeführt werden, an der es um den Apache geht. Dann macht man weiter wie im Folgenden beschrieben.

/$03:$03



Sandini Bib

In der Verwaltungskonsole für den IIS klickt man mit der rechten Maustaste auf die Standardwebsite und wählt im Kontextmenü den Punkt Eigenschaften. Man erhält den Einstellungsdialog in Abbildung 7.10. Auf der Registerkarte Basisverzeichnis klickt man auf die Schaltfläche Konfiguration. Es öffnet sich ein Fenster Anwendungskonfiguration (siehe Abbildung 7.11). Hier fügt man auf der Registerkarte Anwendungszuordungen mit Hilfe der Schaltfläche Hinzufügen eine weitere Zuordnung für PHP ein. Im sich öffnenden Fenster Hinzufügen/Bearbeiten der Zuordnung von Anwendungserweiterungen (siehe Abbildung 7.12) gibt man als ausführbare Datei den Pfad auf die DLL php4isapi.dll an. Diese befindet sich im Verzeichnis sapi der PHP-Installation. Als Erweiterung wird .php eingetragen, Alle Vererben gewählt und das Skriptmodul aktiviert. Nachdem man alle Dialoge nun mit OK abgeschlossen hat, muss man die Standardwebsite beenden und wieder starten. Dies kann man in der Konsole für die Internet-Informationsdienste entweder mit den Schaltflächen in der Toolbar oder über das Kontextmenü erreichen. Legt man nun im Basisverzeichnis C:\Inetpub\wwwroot eine Datei namens info.php mit dem folgenden Source Code an und ruft diese mit einem Browser auf, erhält man die (mittlerweile altbekannte) Info-Seite von PHP (siehe Abbildung 7.13).

Abbildung 7.10: Eigenschaften der Standardwebseite



 

Sandini Bib

Abbildung 7.11: Anwendungsoptionen für das Basisverzeichnis

Abbildung 7.12: Eintragen der Anwendungserweiterung für PHP

/$03:$03



Sandini Bib

Abbildung 7.13: PHP-Info mit dem IIS unter Windows

7.3

MySQL und PHP

Um in einer Webseite Daten aus einer MySQL-Datenbank anzuzeigen, kann man die Skript-Sprache PHP nutzen. Sie wird dabei als serverseitige Skriptsprache verwendet. Das bedeutet, dass der Web-Server eine angeforderte PHP-Datei aus seinen Verzeichnissen holt und dem PHPModul übergibt. Dieses analysiert die Datei und führt den in den HTMLAnweisungen eingebetteten PHP-Code aus, welcher meist als Ergebnis wiederum HTML-Text in die Datei einfügt. Diese solchermaßen angepasste Datei wird dann an den Browser übertragen. Abgesehen von der Endung kann der Browser nun nicht mehr erkennen, dass es sich um eine PHP-Datei handelt, da nur noch HTML-Anweisungen und normaler Text vorhanden sind. (Natürlich können auch noch client-seitige Skriptsprachen wie zum Beispiel JavaScript enthalten sein, das hat aber nichts mit der vorherigen Verarbeitung durch das PHP-Modul zu tun.) Ich will hier nur einen ersten Überblick über PHP geben, damit die Verbindung zu MySQL und die Nutzung der API verständlicher sind.



  

Sandini Bib

7.3.1 PHP und HTML Wie sieht nun eine PHP-Datei aus? Wir haben weiter oben schon eine solche Datei gesehen, als wir die Einbindung von PHP in den Web-Server getestet haben. Beginnen wir hier mit einem klassischen und sehr einfachen Beispiel, dem berühmten „Hello World“. Erstellen Sie im Stammverzeichnis des Web-Servers (in unserem Beispiel /usr/local/ apache2/htdocs unter Linux und Apache) eine Datei test01.php mit folgendem Inhalt:

PHP-Test 01



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test01.php auf der beiliegenden CD-ROM. Ruft man nun mit einem Browser diese Datei auf, erhält man eine Ausgabe wie in Abbildung 7.14.

Abbildung 7.14: Ausgabe von test01.php

 



Sandini Bib

Schauen wir uns das Skript genauer an. Ein Großteil der Zeilen ist pures HTML. Nur innerhalb des Bodys gibt es etwas, was mit PHP zu tun hat:

Die Tags der ersten und letzten Zeilen geben an, dass alles dazwischen vom PHP-Modul interpretiert werden soll. Der eigentliche PHP-Code ist hier nur eine Zeile lang: print "Hello World\n";

Wie man sich vielleicht denken kann, wird damit der Text „Hello World“ ausgegeben, gefolgt von einem Zeilenumbruch. Aber wohin wird der Text ausgegeben? Er ersetzt den PHP-Code, der in der Datei an dieser Stelle vorhanden ist und wird in den HTTP-Stream geschrieben, der an den Client geschickt wird. Der Client erhält damit folgende Daten:

PHP-Test 01

Hello World

Damit erhält man dann die Ausgabe in Abbildung 7.14. Neben den Standard-Tags gibt es noch andere Möglichkeiten, PHP-Code auszuzeichnen: • Wer es ausführlich mag, schreibt

• Wer es spartanisch möchte, nutzt die Kurzversion ohne „php“:

• Und wer aus der ASP-Ecke kommt, kann auch die dort genutzten Tags verwenden:



  

Sandini Bib

Dabei ist allerdings zu beachten, dass man die letzten beiden Varianten nur dann nutzen kann, wenn man die entsprechenden Parameter in der php.ini setzt. Für die Kurzversion benötigt man short_open_tag = On;

Für die ASP-Variante ist folgender Eintrag nötig: asp_tags = On;

Da man allerdings nicht immer die Möglichkeit hat, die php.ini anpassen zu können (zum Beispiel bei einem Web-Hoster, der das PHP-Modul fertig bereitstellt), sollte man die Standardvariante nutzen, zumal sie auch keinen Ärger bereitet, wenn man mit XML-Dateien arbeitet.

7.3.2 PHP-Überblick PHP ist eine Skriptsprache. Die Anweisungen werden bei jeder Ausführung neu geparst und erst dann ausgeführt. Variablennamen beginnen mit einem $ und können dann Ziffern, Buchstaben und den Unterstrich enthalten. Es gibt verschiedene Typen von Werten, die eine Variable enthalten kann: Integer, Double, String, Boolean, Object und Array. Dabei ist eine Variable nicht auf einen Typ festgelegt, man kann den Typ jederzeit ändern. Weiterhin kann man auf Variablen verweisen und dynamische Variablen nutzen (d.h. den Namen einer Variablen während des Programmablaufs festlegen). Es gibt die üblichen arithmetischen Operatoren (+, -, *, /, %) und Vergleichsoperatoren (==, , =, !=). Zuweisungsoperatoren liefern zugleich den zugewiesenen Wert als Ergebnis zurück. Die Kombination aus arithmetischem Operator und Zuweisungsoperator ist wie bei C vorhanden (+=, -=, ...), ebenso die inkrementelle Erhöhung/Reduzierung von Variablen ($x++, $x--, ..., ++$x, --$x, ...). Verzweigungen und Schleifenstrukturen lassen sich mit verschiedenen Konstrukten umsetzen: if (bedingung) { ... } else { ... } if (bedingung1) { ... } elseif (bedingung2) {

 



Sandini Bib

... } else { ... } switch (ausdruck) { case ergebnis1: ... break; case ergebnis2: ... break; default: ... } while (bedingung) { ... } do { ... } while (bedingung) for (zuweisung; test; inkrement) { ... }

Wie man sieht, ist auch die allgemeine Struktur der von C sehr ähnlich. Arrays (für die Arbeit mit MySQL sehr wichtig) können Werte unterschiedlichen Typs enthalten: $myarray = array("Thomas", "Schmidt", 31, true); print $myarray[2]; $anotherarray[0] = "Annika"; $anotherarray[1] = "Herms"; $anotherarray[2] = 30; $anotherarray[3] = false; print anotherarray[3];

Arrays lassen sich dynamisch erweitern: $myarray = array("Thomas", "Schmidt"); $myarray[] = 31; $myarray[] = true;

Sie können mehrdimensional sein:



  

Sandini Bib

$mymatrix = array( array("Thomas", "Schmidt", 31, true), array("Annika", "Herms", 30, false), array("Iwipanda", "von Tidenhub", 38, true) ); print $mymatrix[2][0];

Und auch assoziative Arrays sind möglich: $myarray = array ( 'vorname' => "Thomas", 'nachname' => "Schmidt", 'alter' => 31, 'interesse'=> true ); print $myarray('nachname'); $anotherarray['ort'] $anotherarray['strasse'] $anotherarray['plz'] $anotherarray['stadtteil']

= = = =

"Bremen"; "Ritter-Raschen-Str. 243"; "28219"; "Walle";

Die Größe eines Arrays erhält man über die Funktion count(): $myarray = array("Thomas", "Schmidt", 31, true); print count($myarray[2]);

Um alle Elemente eines Arrays zu durchlaufen, kann man (seit PHP 4) das Konstrukt foreach nutzen: $myarray = array("Thomas", "Schmidt", 31, true); foreach ($myarray as $element) { print $element; }

In assoziativen Arrays geht man etwas anders vor: $myarray = array ( 'vorname' => "Thomas", 'nachname' => "Schmidt", 'alter' => 31, 'interesse'=> true ); foreach ($myarray as $schluessel=>$element) { print $schluessel.": ".$element."\n"; }

In der print-Anweisung wird hier der Wert des Schlüssels und des Wertes ausgegeben. Dabei werden beide Informationen durch einen Doppel-

 



Sandini Bib

punkt getrennt und am Ende ein Zeilenumbruch eingefügt. Verbunden sind die einzelnen Ausgabeelemente durch den Verbindungsoperator für Zeichenketten, den Punkt. Die Nutzung von Arrays wollen wir in einem „echten“ Beispiel aufzeigen. Speichern Sie den folgenden Code in der Datei test02.php im Basisverzeichnis des Web-Servers ab:

PHP-Test 02: Arrays



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test02.php auf der beiliegenden CD-ROM. In diesem Skript wird ein Array mit drei Einträgen definiert. Diese Einträge sind assoziative Arrays, in denen Personendaten abgespeichert sind. Anschließend werden diese Daten mit zwei verschachtelten foreach-Konstrukten ausgegeben. Dabei wird eine Tabellenstruktur in HTML genutzt. Der HTML-Code, der an den Client übermittelt wird, sieht dann so aus:

PHP-Test 02: Arrays

VornameNachnameStraßePLZOrtAlter
ThomasSchmidtRitter-Raschen-Str. 234 28219Bremen31
AnnikaHermsSehndenstr. 43129223 Celle30
Iwipandavon TidenhubWilly-Brandt-Platz 54 68161Mannheim38


Die vorher liebevoll angelegten Einrückungen sind im Bereich der PHPAusgabe nicht vorhanden. Das liegt daran, dass wir keine Leerzeichen zum Einrücken mit ausgegeben haben. Da es sich hier aber nur um die Grundlage für den Browser handelt, ist dies nicht weiter tragisch. Wer es wünscht, kann natürlich auch die Ausgabe entsprechend anpassen, damit würde dann aber das PHP-Skript nicht mehr so elegant aussehen. Das Ergebnis der ganzen Bemühungen ist nun in Abbildung 7.15 zu sehen.

 



Sandini Bib

Abbildung 7.15: Ausgabe der Seite test02.php

7.3.3 Zugriff auf die Datenbank Damit der Web-Server seine HTML-Seiten mit Daten aus einer MySQLDatenbank bereichern kann, muss er sich mit dem MySQL-Server verbinden. Diesen Part übernimmt PHP für ihn. (Es gibt natürlich auch aus anderen Sprachen die Möglichkeit, sich mit der Datenbank zu verbinden.) Um mit einer Datenbank arbeiten zu können, muss man zunächst die Verbindung aufbauen, dann eine Datenbank auswählen, um dann schließlich SQL-Befehle an den MySQL-Server zu schicken. Verbindung zum MySQL-Server herstellen Zum Verbinden mit dem MySQL-Server nutzt man die Funktion mysql_connect(). Ohne Parameter wird versucht, sich mit dem Server auf localhost ohne Benutzer und Kennwort anzumelden. Da dies in einer normalen Umgebung nicht sehr sinnvoll ist, sollte man immer alle drei Werte mitgeben: mysql_connect("localhost", "demmig", "mypwd");

Falls die Verbindung fehlschlägt, sollte man das Skript an dieser Stelle beenden, daher bietet sich – gerade bei der Verbindung zur Datenbank – immer die folgende Variante an: mysql_connect("localhost", "demmig", "mypwd") or die("No Connection!");



 

Sandini Bib

Dieses Konstrukt wird einem häufiger begegnen. Es beruht darauf, dass in dieser Oder-Kombination der zweite Teil (or die(...)) nur dann ausgewertet wird, wenn der erste Teil nicht wahr zurückgibt. Das geschieht eben dann, wenn die Verbindung fehlschlägt. Das Ganze hat allerdings noch einen Schönheitsfehler. Wir wollen das an einem Beispiel zeigen. Speichern Sie den folgenden Code in der Datei test03.php ab und rufen Sie ihn in einem Browser auf:

PHP-Test 03: Datenbankzugriff



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test03.php auf der beiliegenden CD-ROM. Das Kennwort ist falsch und es wird der Text „No Connection!“ ausgegeben. Allerdings meldet sich auch die Funktion mysql_connect() selber zu Wort, wie in Abbildung 7.16 zu sehen ist.

Abbildung 7.16: Fehlermeldung bei vergeblichem Verbindungsaufbau

 



Sandini Bib

Um einer PHP-Funktion diese Gesprächigkeit abzugewöhnen, muss man ihr ein @ davor setzen: @mysql_connect("localhost", "demmig", "mypwd") or die("No Connection!");

Ändert man das Skript test03.php dementsprechend ab, wird nur noch der Hinweis „No Connection“ ausgegeben. Die Verbindung wird automatisch am Ende des Skriptes geschlossen. Hat man viele PHP-Seiten, die sich alle mit einem MySQL-Server verbinden, wäre es effektiver, wenn man nur eine Verbindung aufbaut, die immer wieder verwendet wird. Nutzt man den Apache-Server, hilft einem die Funktion mysql_pconnect()dabei. Sie hat die gleichen Parameter wie mysql_connect(), bleibt aber bestehen, solange der entsprechende Thread läuft. Ruft man diese Funktion auf, obwohl die Verbindung noch nicht vorhanden ist, wird sie geöffnet. Ist die Verbindung beim Aufruf schon offen, wird sie wiederverwendet. Etwas wird sicherlich so manchem übel aufstoßen: Benutzername und Kennwort sind deutlich lesbar im PHP-Code abgelegt. Bei einem korrekt konfigurierten Web-Server macht das nicht viel aus, da der Client den PHP-Code nicht zu sehen bekommt. Ist der Server allerdings fehlerhaft eingestellt, kann man auch die Quelltexte der PHP-Seiten sehen und erhält damit eventuell Zugriff auf die Datenbank. Es gibt eine Möglichkeit, diese Sicherheitslücke zu schließen. PHP bietet die Möglichkeit, in einem Skript andere Dateien („Include“-Dateien) mit PHP-Code einzubinden. Somit können wir die Verbindungsparameter als Variablen in einer solchen Datei ablegen und vor dem Verbinden mit einlesen. Erstellen wir also eine Include-Datei db.inc:

Wo speichert man diese Datei? Vorteilhaft wäre es, wenn diese Datei an einer Stelle abgelegt wäre, auf die man „von außen“ nicht zugreifen kann. PHP ermöglicht es, in der php.ini mehrere Pfade anzugeben, in denen es nach Include-Dateien sucht. Trägt man zum Beispiel die folgende Zeile ein, wird im aktuellen Pfad und in c:\php\include gesucht. include_path = ".;c:\php\include"



  

Sandini Bib

Auf das Verzeichnis c:\php\include kann man nun (hoffentlich) nicht von außen zugreifen. Legen wir die Datei db.inc dort ab, sind die Verbindungsdaten ziemlich sicher. Dann muss man mit include(filename) auf diese Datei zugreifen und kann die dort definierten Variablen nutzen. Unser neues Skript test04.php sieht dann wie folgt aus:

PHP-Test 04: sicherer Datenbankzugriff



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test04.php auf der beiliegenden CD-ROM. Die Datenbank auswählen Hat man erst einmal eine Verbindung zum Datenbank-Server hergestellt, kann man im nächsten Schritt die Datenbank auswählen. Dazu dient die Funktion mysql_select_db(). Ihr übergibt man als Parameter den Namen der gewünschten Datenbank auf dem MySQL-Server: mysql_select_db("musikschule");

Auch bei dieser Funktion kann man den Fehlerfall mit ... or die(errormsg) abfangen. Es gibt aber eine Möglichkeit, ausführlichere Fehlerinformationen zu erhalten und auszugeben. Dazu dienen die Funktionen mysql_error() und mysql_errno(). mysql_error() gibt eine Fehlerbeschreibung aus, während mysql_errno() die entsprechende Fehlernummer angibt. Die meisten Funktionen der MySQL-API liefern im Fehlerfall ein FALSE zurück, so dass man dies abfangen und eine ausführlichere Fehlermeldung ausgeben kann. Diese beiden Funktionen arbeiten allerdings erst dann, wenn schon erfolgreich eine Verbindung zum MySQL-Server aufgebaut worden ist. Daher muss man den Verbindungsaufbau per mysql_connect() oder mysql_pconnect() auch immer per ... or die() abfangen. Ein Beispiel für die Nutzung der Fehlerfunktionen ist der folgende Code, den man als test05.php abspeichern und aufrufen kann:

 



Sandini Bib

PHP-Test 05: Datenbankzugriff mit Fehlerbehandlung



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test05.php auf der beiliegenden CD-ROM. Hat man in der Datei db.inc als Datenbank-Name fälschlicherweise $dbname = "muskelschule" angegeben, wird die Auswahl der Datenbank fehlschlagen. Das obige Skript sorgt dann für eine Ausgabe wie in Abbildung 7.17 zu sehen.

Abbildung 7.17: Ausgabe bei falscher Datenbank-Angabe

Solch eine Abfrage wird man immer wieder machen müssen, da an fast jeder Stelle etwas schief gehen kann. Da bietet es sich an, den Vorgang



  

Sandini Bib

in eine Funktion zu übernehmen. Ergänzen wir die Datei db.inc um eine Funktionsdefinition:

Jetzt kann man die Datei test05.php so umschreiben, dass die Funktion check_error() genutzt wird und das Skript – im Fall eines Fehlers – abbricht. Als Ergebnis erhält man test06.php, welche das gleiche Ergebnis liefert wie das vorige Skript test05.php.

PHP-Test 06: Datenbankzugriff mit Fehlerbehandlung in Funktion



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test06.php auf der beiliegenden CD-ROM. Daten abfragen und ausgeben Nachdem wir nun eine Verbindung zum MySQL-Server aufgebaut und die gewünschte Datenbank ausgewählt haben, können wir endlich Anfragen stellen und Daten ändern. Dazu nutzt man die Funktion

 



Sandini Bib

mysql_query(). Ihr wird als Parameter ein SQL-Befehl übergeben. Als Er-

gebnis erhält man einen Wert, mit dem man danach auf die Daten zugreifen kann (wenn denn überhaupt eine Ergebnismenge zurückgeliefert wurde). War die Abfrage fehlerhaft, ist dieser Wert FALSE. Eine Abfrage sieht dann zum Beispiel wie folgt aus: $sqlquery = "SELECT nachname, vorname, strasse, plz, ort ". "FROM schueler ORDER BY nachname, vorname;"; $result = mysql_query($sqlquery);

Die Variable $result kann man nun den Funktionen mysql_fetch_row() oder mysql_fetch_array() mitgeben. Beide Funktionen liefern den aktuellen Datensatz der Ergebnismenge zurück und springen anschließend zum nächsten. Während mysql_fetch_row() ein Array mit numerischen Indexwerten zurückgibt, kann man bei mysql_fetch_array() zusätzlich mit den Spaltennamen als Array-Indizes auf die Daten zugreifen. Im folgenden Skript (test07.php) nutzen wir die zweite Funktion. Zudem wird mysql_num_rows() verwendet, um die Anzahl der Datensätze zu bestimmen.

PHP-Test 07: Abfragen von Daten



Sie finden den Quelltext dieses PHP-Skripts im Verzeichnis Beispiel unter test07.php auf der beiliegenden CD-ROM. Die Ausgabe dieses Skripts ist in Abbildung 7.18 zu sehen.

Abbildung 7.18: Ausgabe der Seite test07.php

Will man Daten in einer Tabelle einfügen, löschen oder aktualisieren, nutzt man auch mysql_query(). Fügt man Daten ein und überlässt dabei MySQL die Aufgabe, den Wert eines AUTOINCREMENT-Feldes zu setzen, möchte man meist im Anschluss den Inhalt dieses Feldes erfahren. Dafür nutzt man die Funktion mysql_insert_id(). Diese liefert den letzten eingefügten Wert für ein solches Feld zurück. Im Skript test08.php nutzen wir dieses Vorgehen. Zunächst geben wir den Inhalt der Tabelle raum aus, fügen dann einen neuen Wert ein, holen uns die neue ID und geben schließlich genau diesen Satz erneut aus:

 



Sandini Bib

PHP-Test 08: Einfügen von Daten