Abfragen und Änderungen für MySQL zum Nachschlagen: Daten auswählen, filtern, sortieren, zählen und verbinden, Tabellen anlegen und füllen, und wie ein Programm Werte sicher in eine Abfrage gibt. Die Beispiele stammen aus den Datensätzen der Aktivitäten: die Länder der Welt und die Filmdatenbank (siehe Datensätze).
mycli ist ein MySQL-Client für das Terminal. Er meldet sich mit deinen Zugangsdaten aus dem Profil von selbst an und öffnet deine Datenbank:
mycli
;, erst dann führt mycli sie aus. Lange Abfragen dürfen über mehrere Zeilen gehen.mycli Befehle, Tabellen und Spalten vor; Tab übernimmt den Vorschlag.mycli seitenweise; Q führt zurück zur Eingabe.exit oder StrgD beendet mycli.Eine weitere Datenbank aus deinem Profil öffnest du mit ihrem Namen, eine Datei mit SQL führst du aus, indem du sie an mysql gibst:
mycli db_k7q4bcd9fghj mysql < film.sql
SHOW TABLES; DESCRIBE country;
SHOW TABLES listet die Tabellen der Datenbank, DESCRIBE die Spalten einer Tabelle mit ihren Datentypen, ob sie leer bleiben dürfen (NULL) und welche zum Schlüssel gehören (PRI, MUL).
SELECT name, population FROM country; SELECT * FROM country; SELECT name, population / area AS density FROM country;
Nach SELECT stehen die Spalten, die das Ergebnis haben soll, nach FROM die Tabelle. * heißt »alle Spalten«. Eine Spalte kann auch ein berechneter Wert sein; AS gibt ihr einen Namen.
SELECT * FROM country WHERE continent = 'Europa'; SELECT name FROM country WHERE population < 5000000 AND area > 1000000; SELECT name FROM country WHERE continent IN ('Europa', 'Asien'); SELECT name FROM country WHERE name LIKE '%land%';
| Schreibweise | Bedeutung |
|---|---|
= <> < <= > >=
|
gleich, ungleich, kleiner, kleiner oder gleich, … |
AND OR NOT
|
Bedingungen verknüpfen; Klammern legen fest, was zuerst gilt: (a OR b) AND c
|
IN ('Europa', 'Asien') |
einer der Werte |
BETWEEN 10 AND 20 |
von 10 bis 20, beide eingeschlossen |
LIKE '%land%' |
Textmuster: % steht für beliebig viele Zeichen, _ für genau eines |
IS NULL · IS NOT NULL
|
kein Wert eingetragen. = NULL trifft nie zu. |
Texte stehen in einfachen Anführungszeichen, Zahlen ohne.
SELECT name, population FROM country ORDER BY population DESC LIMIT 10; SELECT name, continent FROM country ORDER BY continent, name;
ORDER BY sortiert aufsteigend, mit DESC absteigend; nach mehreren Spalten, wenn die erste gleich ist. LIMIT gibt höchstens so viele Zeilen aus. Ohne ORDER BY ist die Reihenfolge nicht festgelegt.
SELECT COUNT(*) FROM country WHERE continent = 'Asien'; SELECT DISTINCT continent FROM country; SELECT continent, COUNT(*) AS countries, SUM(population) AS people FROM country GROUP BY continent HAVING COUNT(*) >= 10 ORDER BY countries DESC;
COUNT(*) zählt Zeilen, SUM, AVG, MIN und MAX rechnen über eine Spalte. DISTINCT gibt jeden Wert nur einmal aus. GROUP BY fasst Zeilen mit gleichem Wert zu einer Gruppe zusammen; dann rechnen die Funktionen je Gruppe. WHERE filtert Zeilen vor dem Gruppieren, HAVING danach die Gruppen.
SELECT movie.title, genre.name FROM movie JOIN movie_genre ON movie.id = movie_genre.movie_id JOIN genre ON movie_genre.genre_id = genre.id WHERE movie.title = 'Back to the Future';
JOIN … ON … setzt jede Zeile der einen Tabelle mit den Zeilen der anderen zusammen, für die die Bedingung gilt, meist Fremdschlüssel = Primärschlüssel. Spalten schreibt man dann mit ihrer Tabelle davor (movie.title). Braucht eine Abfrage dieselbe Tabelle zweimal, bekommt sie Namen: JOIN job AS ja ON …. LEFT JOIN behält auch Zeilen ohne Partner; deren Spalten aus der anderen Tabelle sind dann NULL.
SELECT b.name, ROUND(ST_Distance_Sphere(POINT(a.longitude, a.latitude), POINT(b.longitude, b.latitude)) / 1000) AS km FROM city AS a JOIN city AS b ON b.id <> a.id WHERE a.name = 'Berlin' ORDER BY km LIMIT 5;
POINT(x, y) macht aus zwei Zahlen einen Punkt, auf der Erde mit der Länge (longitude) zuerst und der Breite (latitude) danach. ST_Distance_Sphere gibt die Entfernung zweier Punkte auf einer Kugel vom Radius der Erde, in Metern, also Luftlinie. CROSS JOIN setzt jede Zeile der einen Tabelle mit jeder der anderen zusammen; JOIN … ON b.id <> a.id tut dasselbe ohne die Paare einer Stadt mit sich selbst. Für viele Millionen Punkte bekommt die Lage eine eigene Spalte vom Typ POINT mit einem SPATIAL INDEX, damit die Datenbank nicht jede Zeile ausrechnen muss.
CREATE TABLE entry ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(80) NOT NULL, message TEXT NOT NULL, created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) );
| Schreibweise | Bedeutung |
|---|---|
INT · DECIMAL(6,2)
|
ganze Zahl · Kommazahl mit 6 Stellen, davon 2 nach dem Komma |
VARCHAR(80) · TEXT
|
Text bis 80 Zeichen · langer Text |
DATE · DATETIME
|
Datum · Datum mit Uhrzeit |
NOT NULL |
Die Spalte braucht immer einen Wert. |
DEFAULT CURRENT_TIMESTAMP |
ohne Angabe der Zeitpunkt des Einfügens |
AUTO_INCREMENT |
MySQL vergibt die nächste Nummer selbst. |
PRIMARY KEY (id) |
bestimmt jede Zeile eindeutig |
UNIQUE (name) |
kein Wert doppelt |
FOREIGN KEY (movie_id) REFERENCES movie (id) |
Fremdschlüssel: nur Werte, die es in movie.id gibt |
DROP TABLE entry; löscht die Tabelle samt Inhalt. Wie ein ER-Modell zu Tabellen wird, steht unter ER-Modelle.
INSERT INTO entry (name, message) VALUES ('Ada', 'Schönes Gästebuch!'); INSERT INTO entry (name, message) VALUES ('Alan', 'Hallo'), ('Grace', 'Hi');
Nach dem Tabellennamen stehen die Spalten, nach VALUES die Werte in derselben Reihenfolge, je Zeile ein Paar Klammern. Spalten mit AUTO_INCREMENT oder DEFAULT darf man weglassen.
UPDATE entry SET message = 'Schönes Gästebuch :)' WHERE id = 1; DELETE FROM entry WHERE id = 2;
Beide wirken auf jede Zeile, auf die WHERE zutrifft. Ohne WHERE ändert oder löscht der Befehl alle Zeilen. Wer unsicher ist, schreibt zuerst ein SELECT mit derselben Bedingung und sieht nach, welche Zeilen getroffen werden.
Ein Programm, das eine Abfrage aus Text und Eingaben zusammenklebt, lässt die Eingabe mitbestimmen, was die Datenbank tut: Ein Anführungszeichen im Namen beendet den Text im SQL, und was danach kommt, liest MySQL als SQL. Das heißt SQL-Injection. Die Abhilfe ist immer dieselbe: Die Abfrage enthält Platzhalter, die Werte gehen getrennt an die Datenbank und werden nie als SQL gelesen.
// PHP mit PDO $stmt = $db->prepare('INSERT INTO entry (name, message) VALUES (?, ?)'); $stmt->execute([$name, $message]);
# Ruby mit mysql2 client.prepare('SELECT * FROM entry WHERE name = ?').execute(name)
# Python mit mysql.connector cursor.execute('SELECT * FROM entry WHERE name = %s', (name,))
Platzhalter stehen nur für Werte, nicht für Tabellen- oder Spaltennamen. Muss ein Programm einen Namen wählen, nimmt es ihn aus einer festen Liste erlaubter Namen.
| Meldung | Bedeutung |
|---|---|
1064 You have an error in your SQL syntax |
Die Schreibweise stimmt nicht. Die Meldung zitiert die Stelle, ab der MySQL nichts mehr versteht; oft fehlt kurz davor ein Komma, eine Klammer oder ein Anführungszeichen. |
1146 Table … doesn't exist |
Die Tabelle gibt es in dieser Datenbank nicht: Tippfehler, andere Datenbank, oder die Daten sind noch nicht geladen. |
1054 Unknown column … |
Die Spalte gibt es nicht. DESCRIBE zeigt die richtigen Namen. Auch ein Text ohne Anführungszeichen wird als Spaltenname gelesen. |
1062 Duplicate entry … |
Der Wert steht schon in einer Spalte mit PRIMARY KEY oder UNIQUE. |
1452 Cannot add or update a child row |
Ein Fremdschlüssel verweist auf eine Zeile, die es nicht gibt. |
1045 · 1044 Access denied |
Falscher Benutzername oder falsches Passwort, oder dieser Zugang darf die Datenbank nicht benutzen. |