Kapitel 4. Arbeiten mit Datenbankstrukturen
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
In diesem Kapitel erfährst du, wie du deine eigenen Datenbanken erstellst, Strukturen wie Tabellen und Indizes hinzufügst und entfernst und welche Spaltentypen du in deinen Tabellen verwenden kannst. Es konzentriert sich auf die Syntax und die Funktionen von SQL und nicht auf die Semantik des Entwerfens, Spezifizierens und Verfeinerns eines Datenbankentwurfs; eine einführende Beschreibung der Datenbankentwurfstechniken findest du in Kapitel 2. Um dieses Kapitel durchzuarbeiten, musst du wissen, wie du mit einer bestehenden Datenbank und ihren Tabellen arbeitest, wie in Kapitel 3 beschrieben.
In diesem Kapitel werden die Strukturen der Beispieldatenbank sakila
aufgelistet. Wenn du die Anweisungen zum Laden der Datenbank in "Beispiele für die Modellierung von Entity-Relationships" befolgt hast , hast du die Datenbank bereits zur Verfügung und weißt, wie du sie wiederherstellen kannst, nachdem du ihre Strukturen geändert hast.
Wenn du dieses Kapitel abgeschlossen hast, verfügst du über alle Grundlagen, um Datenbankstrukturen zu erstellen, zu ändern und zu löschen. Zusammen mit den Techniken, die du in Kapitel 3 gelernt hast, bist du in der Lage, eine Vielzahl von grundlegenden Operationen auszuführen. In den Kapiteln 5 und 7 geht es um die Fähigkeiten, mit denen du fortgeschrittenere Operationen mit MySQL durchführen kannst.
Datenbanken erstellen und verwenden
Wenn du den Entwurf einer Datenbank abgeschlossen hast, ist der erste praktische Schritt, den du mit MySQL machen musst, sie zu erstellen. Das tust du mit der Anweisung CREATE DATABASE
. Angenommen, du möchtest eine Datenbank mit dem Namen lucy
erstellen. Hier ist die Anweisung, die du eingeben würdest:
mysql
>
CREATE
DATABASE
lucy
;
Query OK, 1 row affected (0.10 sec)
Wir gehen hier davon aus, dass du weißt, wie du dich mit dem MySQL-Client verbinden kannst, wie in Kapitel 1 beschrieben. Außerdem gehen wir davon aus, dass du dich als Root-Benutzer oder als ein anderer Benutzer, der Strukturen erstellen, löschen und ändern kann, einloggen kannst (eine ausführliche Diskussion über Benutzerrechte findest du in Kapitel 8). Wenn du die Datenbank erstellst, sagt MySQL, dass eine Zeile betroffen ist. Dabei handelt es sich nicht um eine normale Zeile in einer bestimmten Datenbank, sondern um einen neuen Eintrag, der der Liste hinzugefügt wurde, die du unter mit dem Befehl SHOW DATABASES
einsehen kannst.
Wenn du die Datenbank erstellt hast, musst du sie im nächsten Schritt mit als Datenbank auswählen, mit der du arbeitest. Das machst du mit dem Befehl MySQL USE
:
mysql
>
USE
lucy
;
Database changed
Dieser Befehl muss in einer Zeile eingegeben werden und braucht nicht mit einem Semikolon abgeschlossen zu werden, obwohl wir das aus Gewohnheit normalerweise automatisch tun. Sobald du die Datenbank verwendet (ausgewählt) hast, kannst du mit den im nächsten Abschnitt beschriebenen Schritten Tabellen, Indizes und andere Strukturen erstellen.
Bevor wir mit dem Erstellen anderer Strukturen fortfahren, wollen wir einige Funktionen und Einschränkungen beim Erstellen von Datenbanken besprechen. Sehen wir uns zunächst an, was passiert, wenn du versuchst, eine Datenbank zu erstellen, die bereits existiert:
mysql
>
CREATE
DATABASE
lucy
;
ERROR 1007 (HY000): Can't create database 'lucy'; database exists
Du kannst diesen Fehler vermeiden, indem du die Schlüsselwortphrase IF NOT EXISTS
in dieAnweisung einfügst:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
lucy
;
Query OK, 0 rows affected (0.00 sec)
Du kannst sehen, dass MySQL sich nicht beschwert hat, aber auch nichts unternommen hat: Die Meldung 0 rows affected
zeigt an, dass keine Daten geändert wurden. Dieser Zusatz ist nützlich, wenn du SQL-Anweisungen zu einem Skript hinzufügst: Er verhindert, dass das Skript bei einem Fehler abbricht.
Schauen wir uns an, wie man Datenbanknamen wählt und Groß- und Kleinschreibung verwendet. Datenbanknamen definieren physische Verzeichnisnamen (oder Ordnernamen) auf der Festplatte. Auf einigen Betriebssystemen wird bei Verzeichnisnamen zwischen Groß- und Kleinschreibung unterschieden, auf anderen spielt die Großschreibung keine Rolle. Bei Unix-ähnlichen Systemen wie Linux und macOS wird in der Regel zwischen Groß- und Kleinschreibung unterschieden, bei Windows hingegen nicht. Das hat zur Folge, dass für Datenbanknamen die gleichen Einschränkungen gelten: Wenn die Groß- und Kleinschreibung für das Betriebssystem wichtig ist, ist sie es auch für MySQL. Auf einem Linux-Rechner sind zum Beispiel LUCY
, lucy
und Lucy
verschiedene Datenbanknamen; unter Windows beziehen sie sich auf eine einzige Datenbank. Wenn du unter Linux oder macOS eine falsche Großschreibung verwendest, wird sich MySQL beschweren:
mysql
>
SELECT
SaKilA
.
AcTor_id
FROM
ACTor
;
ERROR 1146 (42S02): Table 'sakila.ACTor' doesn't exist
Aber unter Windows funktioniert das normalerweise.
Tipp
Um dein SQL maschinenunabhängig zu machen, empfehlen wir , dass du für Datenbanken (und für Tabellen, Spalten, Aliase und Indizes) konsequent Kleinbuchstaben verwendest. Das ist jedoch keine Voraussetzung, und wie die Beispiele in diesem Buch gezeigt haben, kannst du jede Namenskonvention verwenden, mit der du dich wohl fühlst. Sei einfach konsequent und erinnere dich daran, wie sich MySQL auf verschiedenen Betriebssystemen verhält.
Dieses Verhalten wird durch den Parameter lower_case_table_names
gesteuert. Wenn er auf 0
gesetzt ist, werden die Tabellennamen wie angegeben gespeichert, und bei Vergleichen wird die Groß-/Kleinschreibung beachtet. Wenn er auf 1
gesetzt ist, werden die Tabellennamen in Kleinbuchstaben auf der Festplatte gespeichert, und bei Vergleichen wird nicht auf Groß- und Kleinschreibung geachtet. Wenn dieser Parameter auf 2
gesetzt ist, werden die Tabellennamen wie angegeben gespeichert, aber in Kleinbuchstaben verglichen. Unter Windows ist der Standardwert 1
. Unter macOS ist der Standardwert 2
. Unter Linux wird ein Wert von 2
nicht unterstützt; der Server erzwingt stattdessen den Wert 0
.
Es gibt weitere Einschränkungen für Datenbanknamen. Sie dürfen höchstens 64 Zeichen lang sein. Außerdem solltest du keine reservierten MySQL-Wörter wie SELECT
, FROM
und USE
als Namen für Strukturen verwenden; diese können den MySQL-Parser verwirren und es ihm unmöglich machen, die Bedeutung deiner Anweisungen zu interpretieren. Du kannst diese Einschränkung umgehen, indem du das reservierte Wort in Backticks einschließt (`
), aber es macht mehr Mühe, sich daran zu erinnern, als es wert ist. Außerdem darfst du bestimmte Zeichen in den Namen nicht verwenden - insbesondere den Schrägstrich, den umgekehrten Schrägstrich, das Semikolon und den Punkt - und ein Datenbankname darf nicht mit einem Leerzeichen enden. Auch hier verwirrt die Verwendung dieser Zeichen den MySQL-Parser und kann zu unvorhersehbarem Verhalten führen. Das passiert zum Beispiel, wenn du ein Semikolon in einen Datenbanknamen einfügst:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
lu
;
cy
;
Query OK, 1 row affected (0.00 sec) ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'cy' at line 1
Da mehr als eine SQL-Anweisung in einer einzigen Zeile stehen kann, führt das dazu, dass eine Datenbank lu
erstellt wird, und dann ein Fehler durch die sehr kurze, unerwartete SQL-Anweisung cy;
erzeugt wird. Wenn du wirklich eine Datenbank mit einem Semikolon im Namen erstellen willst, kannst du das mit Backticks tun:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
`lu;cy`
;
Query OK, 1 row affected (0.01 sec)
Du kannst sehen, dass du jetzt zwei neue Datenbanken hast:
mysql
>
SHOW
DATABASES
LIKE
`lu%`
;
+----------------+ | Database (lu%) | +----------------+ | lu | | lu;cy | +----------------+ 2 rows in set (0.01 sec)
Tabellen erstellen
Dieser Abschnitt behandelt Themen zu Tabellenstrukturen. Wir zeigen dir, wie man:
-
Erstellen von Tabellen, anhand von einführenden Beispielen.
-
Wähle Namen für Tabellen und tabellenbezogene Strukturen.
-
Verstehe und wähle Spaltentypen aus.
-
Verstehe und wähle Schlüssel und Indizes.
-
Verwende die MySQL-eigene Funktion
AUTO_INCREMENT
.
Wenn du diesen Abschnitt beendest, hast du alle Grundlagen zum Erstellen von Datenbankstrukturen kennengelernt. Der Rest dieses Kapitels behandelt die Beispieldatenbank sakila
und wie du bestehende Strukturen ändern und entfernen kannst.
Grundlagen
Für die Beispiele in diesem Abschnitt gehen wir davon aus, dass die Datenbank sakila
noch nicht erstellt wurde. Wenn du den Beispielen folgen willst und die Datenbank bereits geladen hast, kannst du sie für diesen Abschnitt löschen und später wieder laden. Wenn du sie löschst, werden die Datenbank, ihre Tabellen und alle Daten gelöscht, aber das Original lässt sich leicht wiederherstellen, indem du die Schritte in "Beispiele für die Modellierung von Entity Relationships" befolgst . So kannst du die Datenbank vorübergehend löschen:
mysql
>
DROP
DATABASE
sakila
;
Query OK, 23 rows affected (0.06 sec)
Die Anweisung DROP
wird am Ende dieses Kapitels unter "Löschen von Strukturen" näher erläutert .
Um zu beginnen, erstelle die Datenbank sakila
mit der Anweisung:
mysql
>
CREATE
DATABASE
sakila
;
Query OK, 1 row affected (0.00 sec)
Wähle dann die Datenbank mit aus:
mysql
>
USE
sakila
;
Database changed
Jetzt können wir damit beginnen, die Tabellen zu erstellen, die unsere Daten enthalten werden. Erstellen wir eine Tabelle für die Details der Schauspieler. Für den Anfang nehmen wir eine vereinfachte Struktur, die wir später noch komplexer gestalten werden. Hier ist die Anweisung, die wir verwenden:
mysql
>
CREATE
TABLE
actor
(
-
>
actor_id
SMALLINT
UNSIGNED
NOT
NULL
DEFAULT
0
,
-
>
first_name
VARCHAR
(
45
)
DEFAULT
NULL
,
-
>
last_name
VARCHAR
(
45
)
,
-
>
last_update
TIMESTAMP
,
-
>
PRIMARY
KEY
(
actor_id
)
-
>
)
;
Query OK, 0 rows affected (0.01 sec)
Keine Panik - auch wenn MySQL meldet, dass keine Zeilen betroffen sind, hat es die Tabelle erstellt:
mysql
>
SHOW
tables
;
+------------------+ | Tables_in_sakila | +------------------+ | actor | +------------------+ 1 row in set (0.01 sec)
Schauen wir uns das alles im Detail an. Der Befehl CREATE TABLE
hat drei große Abschnitte:
-
Die Anweisung
CREATE TABLE
, auf die der Name der zu erstellenden Tabelle folgt. In diesem Beispiel ist esactor
. -
Eine Liste mit einer oder mehreren Spalten, die der Tabelle hinzugefügt werden sollen. In diesem Beispiel haben wir eine ganze Reihe von Spalten hinzugefügt:
actor_id SMALLINT UNSIGNED NOT NULL DEFAULT 0
,first_name VARCHAR(45) DEFAULT NULL
,last_name VARCHAR(45)
undlast_update TIMESTAMP
. Wir werden sie gleich besprechen. -
Optionale Schlüsseldefinitionen. In diesem Beispiel haben wir einen einzigen Schlüssel definiert:
PRIMARY KEY (actor_id)
. Wir werden Schlüssel und Indizes später in diesem Kapitel ausführlich besprechen.
Beachte, dass auf die Komponente CREATE TABLE
eine öffnende Klammer folgt, die von einer schließenden Klammer am Ende der Anweisung ergänzt wird. Beachte auch, dass die anderen Komponenten durch Kommas getrennt sind. Es gibt noch weitere Elemente, die du zu einer CREATE TABLE
Anweisung hinzufügen kannst.
Kommen wir nun zu den Spaltenangaben. Die grundlegende Syntax lautet wie folgt: name type [NOT NULL | NULL] [DEFAULT value]
. Das name
ist der Spaltenname und unterliegt denselben Einschränkungen wie Datenbanknamen, die im vorherigen Abschnitt erläutert wurden. Er darf höchstens 64 Zeichen lang sein, Schrägstriche und Punkte sind nicht erlaubt, er darf nicht mit einem Leerzeichen enden und die Groß- und Kleinschreibung hängt vom zugrunde liegenden Betriebssystem ab. Das Feld type
Feld legt fest, wie und was in der Spalte gespeichert wird. Wir haben zum Beispiel gesehen, dass es für Zeichenketten auf VARCHAR
, für Zahlen auf SMALLINT
und für Datum und Uhrzeit auf TIMESTAMP
eingestellt werden kann.
Wenn du NOT NULL
angibst, ist eine Zeile ohne einen Wert für die Spalte nicht gültig; wenn du NULL
angibst oder diese Klausel auslässt, kann eine Zeile ohne einen Wert für die Spalte existieren. Wenn du eine value
mit der DEFAULT
Klausel angibst, wird die Spalte damit aufgefüllt, wenn du sonst keine Daten angibst; das ist besonders nützlich, wenn du häufig einen Standardwert wie z. B. einen Ländernamen wieder verwendest. Die value
muss eine Konstante sein (z. B. 0
, "cat"
oder 20060812045623
), außer wenn die Spalte vom Typ TIMESTAMP
ist. Die Typen werden im Abschnitt "Spaltentypen" ausführlich behandelt .
Die Funktionen NOT NULL
und DEFAULT
können zusammen verwendet werden. Wenn du NOT NULL
angibst und einen DEFAULT
Wert hinzufügst, wird der Standardwert verwendet, wenn du keinen Wert für die Spalte angibst. Manchmal funktioniert das gut:
mysql
>
INSERT
INTO
actor
(
first_name
)
VALUES
(
'John'
)
;
Query OK, 1 row affected (0.01 sec)
Und manchmal auch nicht:
mysql
>
INSERT
INTO
actor
(
first_name
)
VALUES
(
'Elisabeth'
)
;
ERROR 1062 (23000): Duplicate entry '0' for key 'actor.PRIMARY'
Ob es funktioniert oder nicht, hängt von den zugrunde liegenden Einschränkungen und Bedingungen der Datenbank ab: In diesem Beispiel hat actor_id
den Standardwert 0
, aber es ist auch der Primärschlüssel. Es ist nicht erlaubt, zwei Zeilen mit demselben Primärschlüsselwert zu haben. Daher schlägt der zweite Versuch, eine Zeile ohne Werte einzufügen (mit dem resultierenden Primärschlüsselwert 0
), fehl. Wir besprechen Primärschlüssel im Detail unter "Schlüssel und Indizes".
Spaltennamen haben weniger Einschränkungen als Datenbank- und Tabellennamen Namen. Außerdem sind die Namen unabhängig von der Groß- und Kleinschreibung und auf allen Plattformen anwendbar. In Spaltennamen sind alle Zeichen erlaubt, aber wenn du sie mit Leerzeichen abschließen oder Punkte oder andere Sonderzeichen wie Semikolon oder Bindestrich einfügen willst, musst du den Namen in Backticks (`
) einschließen. Auch hier empfehlen wir, dass du für entwicklungsbedingte Entscheidungen (z. B. Datenbank-, Alias- und Tabellennamen) konsequent Kleinbuchstaben wählst und Zeichen vermeidest, bei denen du dich an die Verwendung von Backticks erinnern musst.
Die Benennung von Spalten und anderen Datenbankobjekten ist eine persönliche Vorliebe, wenn du neu anfängst (du kannst dich von den Beispieldatenbanken inspirieren lassen) oder eine Frage der Einhaltung von Standards, wenn du an einer bestehenden Codebasis arbeitest. Generell solltest du versuchen, Wiederholungen zu vermeiden: Verwende in einer Tabelle mit dem Namen actor
den Spaltennamen first_name
und nicht actor_first_name
, der in einer komplexen Abfrage vor dem Tabellennamen überflüssig erscheinen würde (actor.actor_first_name
gegenüber actor.first_name
). Eine Ausnahme bildet der allgegenwärtige Spaltenname id
. Entweder vermeidest du diesen oder du stellst den Tabellennamen vor, um die Übersichtlichkeit zu erhöhen (z. B. actor_id
). Es ist gute Praxis, Wörter mit einem Unterstrich zu trennen. Du kannst auch ein anderes Zeichen verwenden, z. B. einen Bindestrich oder Schrägstrich, aber dann musst du dich daran erinnern, die Namen mit Backticks zu umschließen (z. B,
). Du kannst die Worttrennung auch ganz weglassen, aber die "CamelCase"-Formatierung ist wohl schwieriger zu lesen. Wie bei den Datenbank- und Tabellennamen darf auch der Spaltenname höchstens 64 Zeichen lang sein.actor-id
Sortierung und Zeichensätze
Wenn du Zeichenketten vergleichst oder sortierst, hängt es vom Zeichensatz und der verwendeten Sortierung ab, wie MySQL das Ergebnis auswertet. Zeichensätze legen fest, welche Zeichen gespeichert werden können; zum Beispiel musst du vielleicht nicht-englische Zeichen wie ю oder ü speichern. Eine Sortierreihenfolge legt fest, wie Zeichenketten geordnet werden, und für verschiedene Sprachen gibt es unterschiedliche Sortierreihenfolgen: Die Position des Zeichens ü im Alphabet ist zum Beispiel in zwei deutschen Sortierreihenfolgen unterschiedlich und in schwedischen und finnischen wiederum unterschiedlich. Da nicht jeder englische Zeichenketten speichern möchte, ist es wichtig, dass ein Datenbankserver in der Lage ist, nicht-englische Zeichen und verschiedene Arten der Sortierung vonZeichen zu verwalten.
Wir verstehen, dass dir die Diskussion über Sortierungen und Zeichensätze zu fortgeschritten vorkommen kann, wenn du gerade erst anfängst, MySQL zu lernen. Wir sind jedoch der Meinung, dass es sich lohnt, diese Themen zu behandeln, da nicht passende Zeichensätze und Sortierungen zu unerwarteten Situationen wie Datenverlust und falschen Abfrageergebnissen führen können. Wenn du es vorziehst, kannst du diesen Abschnitt und einen Teil der späteren Diskussion in diesem Kapitel überspringen und auf diese Themen zurückkommen, wenn du sie genauer kennenlernen möchtest. Das hat keinen Einfluss auf dein Verständnis der anderen Inhalte in diesem Buch.
In unseren vorherigen Beispielen für String-Vergleiche haben wir das Problem der Sortierung und des Zeichensatzes ignoriert und MySQL einfach seine Standardeinstellungen verwenden lassen. In den MySQL-Versionen vor 8.0 ist der Standard-Zeichensatz latin1
und die Standard-Sortierreihenfolge ist latin1_swedish_ci
. Mit MySQL 8.0 wurden die Standardeinstellungen geändert, so dass der Standardzeichensatz nun utf8mb4
und die Standardsortierreihenfolge utf8mb4_0900_ai_ci
ist. MySQL kann so konfiguriert werden, dass verschiedene Zeichensätze und Sortierreihenfolgen auf Verbindungs-, Datenbank-, Tabellen- und Spaltenebene verwendet werden. Die hier gezeigten Ausgaben stammen aus MySQL 8.0.
Du kannst die Zeichensätze, die auf deinem Server verfügbar sind, mit dem Befehl SHOW CHARACTER SET
auflisten. Dieser zeigt eine kurze Beschreibung jedes Zeichensatzes, seine Standardsortierreihenfolge und die maximale Anzahl von Bytes, die für jedes Zeichen in diesem Zeichensatz verwendet wird:
mysql
>
SHOW
CHARACTER
SET
;
+----------+---------------------------------+---------------------+--------+ | Charset | Description | Default collation | Maxlen | +----------+---------------------------------+---------------------+--------+ | armscii8 | ARMSCII-8 Armenian | armscii8_general_ci | 1 | | ascii | US ASCII | ascii_general_ci | 1 | | big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 | | binary | Binary pseudo charset | binary | 1 | | cp1250 | Windows Central European | cp1250_general_ci | 1 | | cp1251 | Windows Cyrillic | cp1251_general_ci | 1 | | ... | | ujis | EUC-JP Japanese | ujis_japanese_ci | 3 | | utf16 | UTF-16 Unicode | utf16_general_ci | 4 | | utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 | | utf32 | UTF-32 Unicode | utf32_general_ci | 4 | | utf8 | UTF-8 Unicode | utf8_general_ci | 3 | | utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 | +----------+---------------------------------+---------------------+--------+ 41 rows in set (0.00 sec)
Der Zeichensatz latin1
zum Beispiel ist eigentlich, der Zeichensatz der Windows Codepage 1252, der westeuropäische Sprachen unterstützt. Die Standard-Sortierreihenfolge für diesen Zeichensatz ist latin1_swedish_ci
, die den schwedischen Konventionen für die Sortierung von Zeichen mit Akzent folgt (Englisch wird wie erwartet behandelt). Bei dieser Sortierung wird die Groß- und Kleinschreibung nicht berücksichtigt, was durch die Buchstaben ci
angezeigt wird. Schließlich benötigt jedes Zeichen 1 Byte. Zum Vergleich: Wenn du den Standardzeichensatz utf8mb4
verwendest, benötigt jedes Zeichen bis zu 4 Byte an Speicherung. Manchmal ist es sinnvoll, den Standard zu ändern. Es gibt zum Beispiel keinen Grund, Base64-kodierte Daten (die per Definition ASCII sind) in utf8mb4
zu speichern.
Ebenso kannst du die Sortierreihenfolge und die Zeichensätze auflisten, für die sie gilt:
mysql
>
SHOW
COLLATION
;
+---------------------+----------+-----+---------+...+---------------+ | Collation | Charset | Id | Default |...| Pad_attribute | +---------------------+----------+-----+---------+...+---------------+ | armscii8_bin | armscii8 | 64 | |...| PAD SPACE | | armscii8_general_ci | armscii8 | 32 | Yes |...| PAD SPACE | | ascii_bin | ascii | 65 | |...| PAD SPACE | | ascii_general_ci | ascii | 11 | Yes |...| PAD SPACE | | ... |...| | | utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes |...| NO PAD | | utf8mb4_0900_as_ci | utf8mb4 | 305 | |...| NO PAD | | utf8mb4_0900_as_cs | utf8mb4 | 278 | |...| NO PAD | | utf8mb4_0900_bin | utf8mb4 | 309 | |...| NO PAD | | ... |...| | | utf8_unicode_ci | utf8 | 192 | |...| PAD SPACE | | utf8_vietnamese_ci | utf8 | 215 | |...| PAD SPACE | +---------------------+----------+-----+---------+...+---------------+ 272 rows in set (0.02 sec)
Hinweis
Die Anzahl der verfügbaren Zeichensätze und Sortierungen hängt davon ab, wie der MySQL-Server gebaut und verpackt wurde. Die Beispiele, die wir zeigen, stammen von einer Standardinstallation von MySQL 8.0, und die gleichen Zahlen sind auch unter Linux und Windows zu sehen. MariaDB 10.5 hingegen hat 322 Sortierreihen, aber nur 40 Zeichensätze.
Du kannst die aktuellen Standardeinstellungen auf deinem Server wie folgt sehen:
mysql
>
SHOW
VARIABLES
LIKE
'c%'
;
+--------------------------+--------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------+ | ... | | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql-8.0/charsets/ | | ... | | collation_connection | utf8mb4_0900_ai_ci | | collation_database | utf8mb4_0900_ai_ci | | collation_server | utf8mb4_0900_ai_ci | | ... | +--------------------------+--------------------------------+ 21 rows in set (0.00 sec)
Wenn du eine Datenbank erstellst, kannst du den Standardzeichensatz und die Sortierreihenfolge für die Datenbank und ihre Tabellen festlegen. Wenn du zum Beispiel den Zeichensatz utf8mb4
und die Sortierreihenfolge utf8mb4_ru_0900_as_cs
(Groß- und Kleinschreibung beachten) verwenden möchtest, schreibst du:
mysql
>
CREATE
DATABASE
rose
DEFAULT
CHARACTER
SET
utf8mb4
-
>
COLLATE
utf8mb4_ru_0900_as_cs
;
Query OK, 1 row affected (0.00 sec)
Normalerweise ist das nicht nötig, wenn du MySQL korrekt für deine Sprache und Region installiert hast und wenn du nicht vorhast, deine Anwendung zu internationalisieren. Da utf8mb4
seit MySQL 8.0 die Standardeinstellung ist, gibt es noch weniger Grund, den Zeichensatz zu ändern. Du kannst auch den Zeichensatz und die Sortierreihenfolge für einzelne Tabellen oder Spalten festlegen, aber wir werden hier nicht im Detail darauf eingehen, wie das geht. Wie sich die Sortierreihenfolge auf String-Typen auswirkt, wird im Abschnitt "String-Typen" behandelt .
Andere Merkmale
In diesem Abschnitt werden weitere Funktionen der Anweisung CREATE TABLE
kurz beschrieben. Er enthält ein Beispiel, in dem die Funktion IF NOT EXISTS
verwendet wird, sowie eine Liste der fortgeschrittenen Funktionen und der Stellen, an denen du in diesem Buch mehr über sie erfährst. Die gezeigte Anweisung ist die vollständige Darstellung der Tabelle aus der Datenbank sakila
, im Gegensatz zum vorherigen vereinfachten Beispiel.
Du kannst die Schlüsselwortphrase IF NOT EXISTS
verwenden, wenn du eine Tabelle erstellst, und es funktioniert genauso wie bei Datenbanken. Hier ist ein Beispiel, das keinen Fehler meldet, selbst wenn die Tabelle actor
existiert:
mysql
>
CREATE
TABLE
IF
NOT
EXISTS
actor
(
-
>
actor_id
SMALLINT
UNSIGNED
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
VARCHAR
(
45
)
NOT
NULL
,
-
>
last_name
VARCHAR
(
45
)
NOT
NULL
,
-
>
last_update
TIMESTAMP
NOT
NULL
DEFAULT
-
>
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
-
>
PRIMARY
KEY
(
actor_id
)
,
-
>
KEY
idx_actor_last_name
(
last_name
)
)
;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Du kannst sehen, dass null Zeilen betroffen sind und eine Warnung gemeldet wird. Schauen wir uns das mal an:
mysql
>
SHOW
WARNINGS
;
+-------+------+------------------------------+ | Level | Code | Message | +-------+------+------------------------------+ | Note | 1050 | Table 'actor' already exists | +-------+------+------------------------------+ 1 row in set (0.01 sec)
Es gibt eine ganze Reihe zusätzlicher Funktionen, die du zu einer CREATE TABLE
Anweisung hinzufügen kannst, von denen wir in diesem Beispiel nur einige wenige vorstellen. Viele davon sind fortgeschritten und werden in diesem Buch nicht behandelt, aber du findest weitere Informationen im MySQL Reference Manual im Abschnitt über die CREATE TABLE
Anweisung. Zu diesen zusätzlichen Funktionen gehören die folgenden:
- Die Funktion
AUTO_INCREMENT
für numerische Spalten -
Mit dieser Funktion kannst du automatisch eindeutige Bezeichner für eine Tabelle erstellen. Wir besprechen es ausführlich in "Das AUTO_INCREMENT Feature".
- Kommentare zur Kolumne
-
Du kannst einen Kommentar zu einer Spalte hinzufügen; dieser wird angezeigt, wenn du den Befehl
SHOW CREATE TABLE
verwendest, den wir später in diesem Abschnitt besprechen. - Fremdschlüssel-Beschränkungen
-
Du kannst MySQL anweisen, zu prüfen, ob die Daten in einer oder mehreren Spalten mit den Daten in einer anderen Tabelle übereinstimmen. Die Datenbank
sakila
hat zum Beispiel eine Fremdschlüssel-Beschränkung für die Spaltecity_id
der Tabelleaddress
, die sich auf die Spaltecity_id
der Tabellecity
bezieht. Das bedeutet, dass es unmöglich ist, eine Adresse in einer Stadt zu haben, die nicht in der Tabellecity
enthalten ist. In Kapitel 2 haben wir Fremdschlüssel-Beschränkungen vorgestellt, und in "Alternative Speicher-Engines" werden wir uns ansehen, welche Engines Fremdschlüssel-Beschränkungen unterstützen . Nicht jede Speicher-Engine in MySQL unterstützt Fremdschlüssel. - Temporäre Tabellen erstellen
-
Wenn du eine Tabelle mit der Schlüsselwortphrase
CREATE TEMPORARY TABLE
erstellst, wird sie entfernt (gelöscht), wenn die Verbindung geschlossen wird. Das ist beim Kopieren und Neuformatieren von Daten nützlich, weil du dich nicht daran erinnern musst, sie zu bereinigen. Manchmal werden temporäre Tabellen auch zur Optimierung verwendet, um Zwischendaten zu speichern. - Erweiterte Tabellenoptionen
-
Mit den Tabellenoptionen kannst du eine ganze Reihe von Funktionen der Tabelle steuern. Dazu gehören der Startwert von
AUTO_INCREMENT
, die Art und Weise, wie Indizes und Zeilen gespeichert werden, und Optionen, mit denen du die Informationen, die der MySQL-Abfrageoptimierer aus der Tabelle sammelt, außer Kraft setzen kannst. Es ist auch möglich, generierte Spalten anzugeben, die Daten wie die Summe zweier anderer Spalten enthalten, sowie Indizes für solche Spalten. - Kontrolle über Indexstrukturen
-
Bei einigen Speicher-Engines in MySQL kannst du angeben und steuern, welche Art von interner Struktur - z. B. einen B-Baum oder eine Hashtabelle - MySQL für seine Indizes verwendet. Du kannst MySQL auch mitteilen, dass du einen Volltext- oder einen räumlichen Datenindex für eine Spalte haben möchtest, was spezielle Sucharten ermöglicht.
- Aufteilung
-
MySQL unterstützt verschiedene Partitionierungsstrategien, die du bei der Tabellenerstellung oder später auswählen kannst. Wir werden die Partitionierung in diesem Buch nicht behandeln.
Du kannst die Anweisung zum Erstellen einer Tabelle mit der in Kapitel 3 vorgestellten Anweisung SHOW CREATE TABLE
sehen. Dies zeigt dir oft eine Ausgabe, die einige der erweiterten Funktionen enthält, die wir gerade besprochen haben; die Ausgabe stimmt nur selten mit dem überein, was du tatsächlich eingegeben hast, um die Tabelle zu erstellen. Hier ist ein Beispiel für die Tabelle actor
:
mysql
>
SHOW
CREATE
TABLE
actor
\
G
*************************** 1. row *************************** Table: actor Create Table: CREATE TABLE `actor` ( `actor_id` smallint unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(45) NOT NULL, `last_name` varchar(45) NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
Du wirst feststellen, dass die Ausgabe Inhalte enthält, die von MySQL hinzugefügt wurden und nicht in unserem ursprünglichen CREATE TABLE
Statement enthalten waren:
-
Die Namen der Tabelle und der Spalten werden in Backticks eingeschlossen. Das ist nicht notwendig, aber es vermeidet Parsing-Probleme, die durch die Verwendung von reservierten Wörtern und Sonderzeichen verursacht werden können, wie bereits erwähnt.
-
Eine zusätzliche Standardklausel
ENGINE
ist enthalten, die explizit den Tabellentyp angibt, der verwendet werden soll. Die Einstellung in einer Standardinstallation von MySQL lautetInnoDB
und hat daher in diesem Beispiel keine Auswirkungen. -
Es gibt eine zusätzliche
DEFAULT CHARSET
Klausel, die MySQL mitteilt, welcher Zeichensatz von den Spalten in der Tabelle verwendet wird. Auch dies hat bei einer Standardinstallation keine Auswirkung.
Säulen-Typen
Dieser Abschnitt beschreibt die Spaltentypen, die du in MySQL verwenden kannst. Es wird erklärt, wann jeder Typ verwendet werden sollte und welche Einschränkungen er hat. Die Typen sind nach ihrem Zweck gruppiert. Wir gehen auf die am häufigsten verwendeten Datentypen ein und erwähnen fortgeschrittenere oder weniger verwendete Typen nur am Rande. Das bedeutet nicht, dass sie keine Verwendung haben, aber betrachte das Kennenlernen dieser Typen als eine Übung. Höchstwahrscheinlich wirst du dich nicht an jeden einzelnen Datentyp und seine Besonderheiten erinnern, und das ist auch in Ordnung. Es lohnt sich, dieses Kapitel später noch einmal zu lesen und die MySQL-Dokumentation zu diesem Thema zu konsultieren, um dein Wissen auf dem neuesten Stand zu halten.
Integer-Typen
Wir beginnen mit den numerischen Datentypen, genauer gesagt mit den Ganzzahltypen, also den Typen, die bestimmte ganze Zahlen enthalten. Zunächst die beiden gängigsten Integer-Typen:
INT[(width)] [UNSIGNED] [ZEROFILL]
-
Dies ist der am häufigsten verwendete numerische Typ; er speichert ganzzahlige Werte im Bereich von -2.147.483.648 bis 2.147.483.647. Wenn das optionale Schlüsselwort
UNSIGNED
hinzugefügt wird, liegt der Bereich zwischen 0 und 4.294.967.295. Das SchlüsselwortINT
ist die Abkürzung fürINTEGER
und kann austauschbar verwendet werden. EineINT
Spalte benötigt 4 Byte Speicherplatz.INT
wie auch andere Integer-Typen, hat zwei Eigenschaften, die für MySQL spezifisch sind: optionalewidth
undZEROFILL
Argumente. Sie sind nicht Teil eines SQL-Standards und seit MySQL 8.0 veraltet. Dennoch wirst du sie sicherlich in vielen Codebasen finden, deshalb werden wir beide kurz behandeln.Der
width
Parameter gibt die Anzeigebreite an, die von Anwendungen als Teil der Spaltenmetadaten gelesen werden kann. Im Gegensatz zu Parametern an ähnlicher Stelle für andere Datentypen hat dieser Parameter keinen Einfluss auf die Speichereigenschaften eines bestimmten Integer-Typs und schränkt den nutzbaren Wertebereich nicht ein.INT(4)
undINT(32)
sind für die Zwecke der Speicherung von Daten gleich.ZEROFILL
ist ein zusätzliches Argument, das verwendet wird, um die Werte mit Nullen bis zu der durch den Parameter angegebenen Länge zu füllen.width
Parameter angegeben ist. Wenn duZEROFILL
verwendest, fügt MySQL automatischUNSIGNED
zur Deklaration hinzu (da das Auffüllen mit Nullen nur im Zusammenhang mit positiven Zahlen Sinn macht).In einigen wenigen Anwendungen, in denen
ZEROFILL
undwidth
nützlich sind, kann die FunktionLPAD()
verwendet werden, oder die Zahlen können inCHAR
Spalten formatiert gespeichert werden. BIGINT[(width)] [UNSIGNED] [ZEROFILL]
-
In der Welt der immer größer werdenden Datenmengen kommt es immer häufiger vor, dass Tabellen mit einer Anzahl von Zeilen in dieMilliarden gehen. Selbst einfache Spalten des Typs
id
benötigen möglicherweise einen größeren Bereich als den, den ein normalerINT
bietet.BIGINT
löst dieses Problem. Es ist ein großer Ganzzahlentyp mit einem vorzeichenbehafteten Bereich von -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807. EinBIGINT
ohne Vorzeichen kann Zahlen von 0 bis 18.446.744.073.709.551.615 speichern. Für Spalten dieses Typs werden 8 Byte Speicherung benötigt.Intern werden alle Berechnungen in MySQL mit vorzeichenbehafteten
BIGINT
oderDOUBLE
Werten durchgeführt. Die wichtigste Konsequenz daraus ist, dass du sehr vorsichtig sein solltest, wenn du mit extrem großen Zahlen arbeitest. Es gibt zwei Probleme, die du beachten solltest. Erstens sollten große Ganzzahlen ohne Vorzeichen, die größer als 9.223.372.036.854.775.807 sind, nur mit Bitfunktionen verwendet werden. Zweitens: Wenn das Ergebnis einer arithmetischen Operation größer als 9.223.372.036.854.775.807 ist, kann es zu unerwarteten Ergebnissen kommen.Zum Beispiel:
mysql
>
CREATE
TABLE
test_bigint
(
id
BIGINT
UNSIGNED
)
;
Query OK, 0 rows affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
18446744073709551615
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
18446744073709551615
-
1
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
184467440737095516
*
100
)
;
ERROR 1690 (22003): BIGINT value is out of range in '(184467440737095516 * 100)'
Auch wenn 18.446.744.073.709.551.600 kleiner ist als 18.446.744.073.709.551.615, wird der Fehler außerhalb des Bereichs beobachtet, da intern ein vorzeichenbehaftetes
BIGINT
für die Multiplikation verwendet wird.
Tipp
Der Datentyp SERIAL
kann als Alias für BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
verwendet werden. Wenn du nicht auf Datengröße und Leistung optimieren musst, solltest du SERIAL
für deine id
-ähnlichen Spalten verwenden. Auch UNSIGNED INT
kann viel schneller außer Reichweite geraten, als du denkst, und das oft zum denkbar ungünstigsten Zeitpunkt.
Bedenke, dass es zwar möglich ist, jede ganze Zahl als BIGINT
zu speichern, aber das ist eine Verschwendung von Speicherplatz. Außerdem schränkt, wie bereits erwähnt, der width
Parameter den Wertebereich nicht einschränken. Um Speicherplatz zu sparen und die gespeicherten Werte einzuschränken, solltest du verschiedene Integer-Typen verwenden:
SMALLINT[(width)] [UNSIGNED] [ZEROFILL]
-
Speichert kleine Ganzzahlen mit einem Bereich von -32.768 bis 32.767 mit Vorzeichen und von 0 bis 65.535 ohne Vorzeichen. Sie benötigt 2 Byte Speicherung.
TINYINT[(width)] [UNSIGNED] [ZEROFILL]
-
Der kleinste numerische Datentyp, der noch kleinere ganze Zahlen speichert. Der Bereich dieses Typs ist -128 bis 127 mit Vorzeichen und 0 bis 255 ohne Vorzeichen. Er benötigt nur 1 Byte an Speicherung.
BOOL[(width)]
-
Kurz für
BOOLEAN
und ein Synonym fürTINYINT(1)
. Normalerweise akzeptieren boolesche Typen nur zwei Werte: wahr oder falsch. DaBOOL
in MySQL jedoch ein Integer-Typ ist, kannst du Werte von -128 bis 127 in einerBOOL
speichern. Der Wert 0 wird als falsch und alle Werte ungleich Null als wahr behandelt. Es ist auch möglich, spezielletrue
undfalse
Aliase für 1 bzw. 0 zu verwenden. Hier sind einige Beispiele:mysql
>
CREATE
TABLE
test_bool
(
i
BOOL
)
;
Query OK, 0 rows affected (0.04 sec)
mysql
>
INSERT
INTO
test_bool
VALUES
(
true
)
,
(
false
)
;
Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
INSERT
INTO
test_bool
VALUES
(
1
)
,
(
0
)
,
(
-
128
)
,
(
127
)
;
Query OK, 4 rows affected (0.02 sec) Records: 4 Duplicates: 0 Warnings: 0
mysql
>
SELECT
i
,
IF
(
i
,
'true'
,
'false'
)
FROM
test_bool
;
+------+----------------------+ | i | IF(i,'true','false') | +------+----------------------+ | 1 | true | | 0 | false | | 1 | true | | 0 | false | | -128 | true | | 127 | true | +------+----------------------+ 6 rows in set (0.01 sec)
MEDIUMINT[(width)] [UNSIGNED] [ZEROFILL]
-
Speichert Werte im vorzeichenbehafteten Bereich von -8.388.608 bis 8.388.607 und im vorzeichenlosen Bereich von 0 bis 16.777.215. Sie benötigt 3 Byte Speicherung.
BIT[(M)]
-
Spezieller Typ, der zum Speichern von Bit-Werten verwendet wird.
M
gibt die Anzahl der Bits pro Wert an und ist standardmäßig auf 1 eingestellt, wenn sie weggelassen wird. MySQL verwendet eineb'value
Syntax für binäre Werte.
Festkomma-Typen
Die Datentypen DECIMAL
und NUMERIC
in MySQL sind gleich. Obwohl wir hier nur DECIMAL
beschreiben, gilt diese Beschreibung auch für NUMERIC
. Der Hauptunterschied zwischen Festkomma- und Fließkomma-Typen ist die Genauigkeit. Bei Festkomma-Typen ist der abgefragte Wert identisch mit dem gespeicherten Wert; das ist bei Typen, die Dezimalpunkte enthalten, wie den später beschriebenen Typen FLOAT
und DOUBLE
, nicht immer der Fall. Das ist die wichtigste Eigenschaft des DECIMAL
Datentyps, der in MySQL häufig als numerischer Typ verwendet wird:
DECIMAL[(width[,decimals])] [UNSIGNED] [ZEROFILL]
-
Speichert eine Festkommazahl, z. B. ein Gehalt oder eine Entfernung, mit einer Gesamtzahl von
width
Ziffern, von denen einige kleinere Zifferndecimals
die auf ein Dezimalkomma folgen. Eine Spalte, die alsprice DECIMAL(6,2)
deklariert ist, kann zum Beispiel dazu verwendet werden, Werte im Bereich von -9.999,99 bis 9.999,99 zu speichern.price DECIMAL(10,4)
würde Werte wie 123.456,1234 zulassen.Wenn du vor MySQL 5.7 versucht hast, einen Wert außerhalb dieses Bereichs zu speichern, wurde er als der nächstgelegene Wert im zulässigen Bereich gespeichert. Zum Beispiel wurde 100 als 99,99 gespeichert und -100 als -99,99. Ab Version 5.7.5 enthält der Standard-SQL-Modus jedoch den Modus
STRICT_TRANS_TABLES
, der dieses und andere unsichere Verhaltensweisen verbietet. Die Verwendung des alten Verhaltens ist möglich, kann aber zu Datenverlust führen.SQL-Modi sind spezielle Einstellungen, die das Verhalten von MySQL in Bezug auf Abfragen steuern. Sie können zum Beispiel "unsicheres" Verhalten einschränken oder beeinflussen, wie Abfragen interpretiert werden. Für das Erlernen von MySQL empfehlen wir dir, die Standardeinstellungen beizubehalten, da sie sicher sind. Das Ändern der SQL-Modi kann aus Gründen der Kompatibilität mit älteren Anwendungen in verschiedenen MySQL-Versionen erforderlich sein.
Der
width
ist optional, und wenn er weggelassen wird, wird der Wert 10 angenommen. Die Anzahl derdecimals
ist ebenfalls optional, und wenn sie weggelassen wird, wird der Wert 0 angenommen.decimals
darf nicht höher sein als der Wert vonwidth
. Der Höchstwert vonwidth
ist 65, und der maximale Wert vondecimals
ist 30.Wenn du nur positive Werte speichern willst, kannst du das Schlüsselwort
UNSIGNED
verwenden, wie fürINT
beschrieben. Wenn du Nullen einfügen willst, kannst du das SchlüsselwortZEROFILL
verwenden, um das gleiche Verhalten wie beiINT
zu erreichen. Das SchlüsselwortDECIMAL
hat drei identische, austauschbare Alternativen:DEC
,NUMERIC
, undFIXED
.Die Werte in den Spalten von
DECIMAL
werden in einem binären Format gespeichert. Dieses Format verwendet 4 Bytes für jeweils neun Ziffern.
Fließkomma-Typen
Neben dem Festkomma-Typ DECIMAL
, der im vorherigen Abschnitt beschrieben wurde, gibt es zwei weitere Typen, die Dezimalpunkte unterstützen: DOUBLE
(auch bekannt als REAL
) und FLOAT
. Sie wurden entwickelt, um ungefähre numerische Werte zu speichern und nicht die exakten Werte, die von DECIMAL
gespeichert werden.
Warum willst du Näherungswerte? Die Antwort ist, dass viele Zahlen mit einem Dezimalpunkt Näherungswerte für reale Größen sind. Nehmen wir zum Beispiel an, du verdienst 50.000 US-Dollar pro Jahr und möchtest dies als Monatslohn speichern. Wenn du dies in einen monatlichen Betrag umrechnest, sind es 4.166 $ plus 66 und 2/3 Cent. Wenn du diesen Betrag als 4.166,67 $ speicherst, ist er nicht genau genug, um ihn in einen Jahreslohn umzurechnen (denn 12 multipliziert mit 4.166,67 $ ergibt 50.000,04 $). Wenn du jedoch 2/3 mit genügend Nachkommastellen speicherst, ist es ein genauerer Näherungswert. Du wirst feststellen, dass es genau genug ist, um in einer hochpräzisen Umgebung wie MySQL korrekt zu multiplizieren und den ursprünglichen Wert zu erhalten, wobei nur ein wenig gerundet werden muss. Hier sind DOUBLE
und FLOAT
nützlich: Sie ermöglichen es dir, Werte wie 2/3 oder Pi mit einer großen Anzahl von Nachkommastellen zu speichern, so dass exakte Größen als Näherungswerte dargestellt werden können. Später kannst du die Funktion ROUND()
verwenden, um die Ergebnisse auf eine bestimmte Genauigkeit zu bringen.
Setzen wir das vorherige Beispiel mit DOUBLE
fort. Angenommen, du erstellst eine Tabelle wiefolgt:
mysql
>
CREATE
TABLE
wage
(
monthly
DOUBLE
)
;
Query OK, 0 rows affected (0.09 sec)
Jetzt kannst du den Monatslohn mit eingeben:
mysql
>
INSERT
INTO
wage
VALUES
(
50000
/
12
)
;
Query OK, 1 row affected (0.00 sec)
Und schau nach, was gespeichert ist:
mysql
>
SELECT
*
FROM
wage
;
+----------------+ | monthly | +----------------+ | 4166.666666666 | +----------------+ 1 row in set (0.00 sec)
Wenn du ihn jedoch multiplizierst, um einen Jahreswert zu erhalten, erhältst du einen hochpräzisen Näherungswert:
mysql
>
SELECT
monthly
*
12
FROM
wage
;
+--------------------+ | monthly*12 | +--------------------+ | 49999.999999992004 | +--------------------+ 1 row in set (0.00 sec)
Um den ursprünglichen Wert zurückzubekommen, musst du noch eine Rundung mit der gewünschten Genauigkeit vornehmen. Zum Beispiel könnte dein Unternehmen eine Genauigkeit von fünf Dezimalstellen benötigen. In diesem Fall könntest du den ursprünglichen Wert mit wiederherstellen:
mysql
>
SELECT
ROUND
(
monthly
*
12
,
5
)
FROM
wage
;
+---------------------+ | ROUND(monthly*12,5) | +---------------------+ | 50000.00000 | +---------------------+ 1 row in set (0.00 sec)
Aber die Genauigkeit auf acht Nachkommastellen würde nicht den ursprünglichen Wert ergeben:
mysql
>
SELECT
ROUND
(
monthly
*
12
,
8
)
FROM
wage
;
+---------------------+ | ROUND(monthly*12,8) | +---------------------+ | 49999.99999999 | +---------------------+ 1 row in set (0.00 sec)
Es ist wichtig zu verstehen, dass Fließkommadatentypen unpräzise und ungefähr sind.
Hier sind die Details zu den Typen FLOAT
und DOUBLE
:
FLOAT[(width, decimals)] [UNSIGNED] [ZEROFILL]
oderFLOAT[(precision)] [UNSIGNED] [ZEROFILL]
-
Speichert Fließkommazahlen. Sie hat zwei optionale Syntaxen: Die erste erlaubt eine optionale Anzahl von
decimals
und eine optionale Anzeigewidth
, und die zweite erlaubt eine optionaleprecision
die die Genauigkeit der Annäherung, gemessen in Bits, steuert. Ohne Parameter (die typische Verwendung) speichert der Typ kleine, 4-Byte große, einfach genaue Fließkommazahlen. Wennprecision
zwischen 0 und 24 liegt, tritt das Standardverhalten ein. Wennprecision
zwischen 25 und 53 liegt, verhält sich der Typ wieDOUBLE
. Derwidth
hat keinen Einfluss darauf, was gespeichert wird, sondern nur darauf, was angezeigt wird. Die OptionenUNSIGNED
undZEROFILL
verhalten sich wie beiINT
. DOUBLE[(width, decimals)] [UNSIGNED] [ZEROFILL]
-
Speichert Fließkommazahlen. Sie ermöglicht die Angabe einer optionalen Anzahl von
decimals
und einer optionalen Anzeigewidth
. Ohne Parameter (die typische Verwendung) speichert der Typ normale 8-Byte-Gleitkommazahlen mit doppelter Genauigkeit. Derwidth
Parameter hat keinen Einfluss darauf, was gespeichert wird, sondern nur darauf, was angezeigt wird. Die OptionenUNSIGNED
undZEROFILL
verhalten sich wie beiINT
. Der TypDOUBLE
hat zwei identische Synonyme:REAL
undDOUBLE PRECISION
.
String-Typen
String-Datentypen werden verwendet, um Text und, weniger offensichtlich, binäre Daten zu speichern. MySQL unterstützt die folgenden String-Typen:
[NATIONAL] VARCHAR(width) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Der wahrscheinlich am häufigsten verwendete Stringtyp,
VARCHAR
speichert Strings mit variabler Länge bis zu einer maximalenwidth
. Der maximale Wert vonwidth
beträgt 65.535 Zeichen. Die meisten Informationen, die für diesen Typ gelten, treffen auch auf andere Stringtypen zu.Die Typen
CHAR
undVARCHAR
sind sich sehr ähnlich, aber es gibt ein paar wichtige Unterschiede.VARCHAR
verursacht ein oder zwei zusätzliche Bytes an Overhead, um den Wert der Zeichenkette zu speichern, je nachdem, ob der Wert kleiner oder größer als 255 Bytes ist. Diese Größe unterscheidet sich von der Länge des Strings in Zeichen, da bestimmte Zeichen bis zu 4 Byte Platz benötigen können. Es mag also offensichtlich sein, dassVARCHAR
weniger effizient ist. Das ist jedoch nicht immer der Fall. DaVARCHAR
Zeichenketten beliebiger Länge speichern kann (bis zurwidth
definiert), benötigen kürzere Zeichenketten weniger Speicherplatz als eineCHAR
mit ähnlicher Länge.Ein weiterer Unterschied zwischen
CHAR
undVARCHAR
ist die Behandlung von Leerzeichen am Ende der Spalte.VARCHAR
behält Leerzeichen am Ende der Spalte bis zur angegebenen Spaltenbreite bei und schneidet den Rest ab, was zu einer Warnung führt. Wie später gezeigt wird, werden die Werte vonCHAR
rechts auf die Spaltenbreite aufgefüllt und die Leerzeichen am Ende der Spalte werden nicht beibehalten. BeiVARCHAR
sind die Leerzeichen am Ende der Spalte wichtig, es sei denn, sie werden abgeschnitten und zählen als eindeutige Werte. Lass es uns demonstrieren:mysql
>
CREATE
TABLE
test_varchar_trailing
(
d
VARCHAR
(
2
)
UNIQUE
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
test_varchar_trailing
VALUES
(
'a'
)
,
(
'a '
)
;
Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
SELECT
d
,
LENGTH
(
d
)
FROM
test_varchar_trailing
;
+------+-----------+ | d | LENGTH(d) | +------+-----------+ | a | 1 | | a | 2 | +------+-----------+ 2 rows in set (0.00 sec)
Die zweite Zeile, die wir eingefügt haben, hat ein Leerzeichen am Ende, und da die
width
für die Spalted
2 ist, zählt dieses Leerzeichen für die Eindeutigkeit einer Zeile. Wenn wir jedoch versuchen, eine Zeile mit zwei Leerzeichen am Ende einzufügen:mysql
>
INSERT
INTO
test_varchar_trailing
VALUES
(
'a '
)
;
ERROR 1062 (23000): Duplicate entry 'a ' for key 'test_varchar_trailing.d'
MySQL weigert sich, die neue Zeile zu akzeptieren.
VARCHAR(2)
schneidet implizit die Leerzeichen nach dem Satzwidth
ab, so dass sich der gespeicherte Wert von"a "
(mit einem doppelten Leerzeichen nach a) in"a "
(mit einem einfachen Leerzeichen nach a) ändert. Da es bereits eine Zeile mit einem solchen Wert gibt, wird ein Fehler für einen doppelten Eintrag gemeldet. Dieses Verhalten fürVARCHAR
undTEXT
kann durch Ändern der Spaltensortierung gesteuert werden. Einige Sortierungen, wielatin1_bin
, haben das AttributPAD SPACE
, was bedeutet, dass sie beim Abruf mit Leerzeichen aufgefüllt werden.width
mit Leerzeichen aufgefüllt. Das hat keinen Einfluss auf die Speicherung, aber auf die Eindeutigkeitsprüfung und die Funktionsweise der OperatorenGROUP BY
undDISTINCT
, die wir in Kapitel 5 besprechen werden. Du kannst überprüfen, ob eine SortierungPAD SPACE
oderNO PAD
ist, indem du den BefehlSHOW COLLATION
ausführst, wie wir in "Sortierung und Zeichensätze" gezeigt haben . Wir wollen die Wirkung in Aktion sehen, indem wir eine Tabelle mit der SortierreihenfolgePAD SPACE
erstellen:mysql
>
CREATE
TABLE
test_varchar_pad_collation
(
-
>
data
VARCHAR
(
5
)
CHARACTER
SET
latin1
-
>
COLLATE
latin1_bin
UNIQUE
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'a'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'a '
)
;
ERROR 1062 (23000): Duplicate entry 'a ' for key 'test_varchar_pad_collation.data'
Die Kollation
NO PAD
ist eine Neuerung von MySQL 8.0. In früheren Versionen von MySQL, die du vielleicht noch oft im Einsatz siehst, hat jede Sortierung implizit das AttributPAD SPACE
. Daher ist in MySQL 5.7 und früheren Versionen die einzige Möglichkeit, Leerzeichen am Ende zu erhalten, die Verwendung eines binären Typs:VARBINARY
oderBLOB
.Hinweis
Sowohl der
CHAR
als auch derVARCHAR
Datentyp erlauben die Speicherung von Werten, die länger alswidth
sind, es sei denn, der strenge SQL-Modus ist deaktiviert (d. h. wenn wederSTRICT_ALL_TABLES
nochSTRICT_TRANS_TABLES
aktiviert ist). Wenn der Schutz deaktiviert ist, werden Werte, die länger alswidth
sind, abgeschnitten und es wird eine Warnung angezeigt. Wir raten davon ab, das Legacy-Verhalten zu aktivieren, da dies zu Datenverlusten führen kann.Die Sortierung und der Vergleich der Typen
VARCHAR
,CHAR
undTEXT
erfolgt nach der Sortierreihenfolge des zugewiesenen Zeichensatzes. Wie du siehst, ist es möglich, den Zeichensatz und die Sortierreihenfolge für jede einzelne String-Typ-Spalte festzulegen. Es ist auch möglich, denbinary
Zeichensatz anzugeben, derVARCHAR
inVARBINARY
umwandelt. Verwechsle denbinary
Zeichensatz nicht mit demBINARY
Attribut für einen Zeichensatz; letzteres ist eine reine MySQL-Verknüpfung zur Angabe einer binären (_bin
) Sortierung.Darüber hinaus ist es möglich, eine Sortierreihenfolge direkt in der
ORDER BY
Klausel anzugeben. Die verfügbaren Sortierreihenfolgen hängen vom Zeichensatz der Spalte ab. In der Tabelletest_varchar_pad_collation
kannst du ein ä-Symbol speichern und dann sehen, wie sich die Sortierreihenfolge auf die Zeichenketten auswirkt:mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'ä'
)
,
(
'z'
)
;
Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
test_varchar_pad_collation
-
>
ORDER
BY
data
COLLATE
latin1_german1_ci
;
+------+ | data | +------+ | a | | ä | | z | +------+ 3 rows in set (0.00 sec)
mysql
>
SELECT
*
FROM
test_varchar_pad_collation
-
>
ORDER
BY
data
COLLATE
latin1_swedish_ci
;
+------+ | data | +------+ | a | | z | | ä | +------+ 3 rows in set (0.00 sec)
Das Attribut
NATIONAL
(oder die entsprechende KurzformNCHAR
) ist eine Standard-SQL-Methode, um anzugeben, dass eine Spalte vom Typ String einen vordefinierten Zeichensatz verwenden muss. MySQL verwendetutf8
als diesen Zeichensatz. Es ist jedoch wichtig zu wissen, dass MySQL 5.7 und 8.0 sich nicht einig sind, was genauutf8
ist: Ersterer verwendet es als Alias fürutf8mb3
, letzterer fürutf8mb4
. Daher ist es am besten, das AttributNATIONAL
sowie zweideutige Aliasnamen nicht zu verwenden. Die bewährte Methode für alle textbezogenen Spalten und Daten ist es, so eindeutig und spezifisch wie möglich zu sein. [NATIONAL] CHAR(width) [CHARACTER SET charset_name] [COLLATE collation_name]
-
CHAR
speichert eine Zeichenkette fester Länge (z. B. einen Namen, eine Adresse, oder eine Stadt) der Längewidth
. Wenn einewidth
nicht angegeben wird, wirdCHAR(1)
angenommen. Der maximale Wert vonwidth
ist 255. Wie beiVARCHAR
werden die Werte inCHAR
Spalten immer in der angegebenen Länge gespeichert. Ein einzelner Buchstabe, der in einerCHAR(255)
Spalte gespeichert wird, benötigt 255 Bytes (imlatin1
Zeichensatz) und wird mit Leerzeichen aufgefüllt. Das Auffüllen wird beim Lesen der Daten entfernt, es sei denn, derPAD_CHAR_TO_FULL_LENGTH
SQL-Modus ist aktiviert. Es ist wichtig zu erwähnen, dass dies bedeutet, dass alle Leerzeichen am Ende von Zeichenfolgen, die inCHAR
Spalten gespeichert werden, verloren gehen.In der Vergangenheit wurde die
width
einerCHAR
Spalte oft mit einer Größe in Bytes verbunden. Das ist jetzt nicht immer der Fall, und schon gar nicht standardmäßig. Multibyte-Zeichensätze, wie der Standardutf8mb4
in MySQL 8.0, können zu viel größeren Werten führen. InnoDB kodiert Spalten mit fester Länge als Spalten mit variabler Länge, wenn ihre maximale Größe 768 Byte überschreitet. Daher speichert InnoDB in MySQL 8.0 standardmäßig eineCHAR(255)
Spalte wie eineVARCHAR
Spalte. Hier ist ein Beispiel:mysql
>
CREATE
TABLE
test_char_length
(
-
>
utf8char
CHAR
(
10
)
CHARACTER
SET
utf8mb4
-
>
,
asciichar
CHAR
(
10
)
CHARACTER
SET
binary
-
>
)
;
Query OK, 0 rows affected (0.04 sec)
mysql
>
INSERT
INTO
test_char_length
VALUES
(
'Plain text'
,
'Plain text'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_char_length
VALUES
(
'的開源軟體'
,
'Plain text'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
LENGTH
(
utf8char
)
,
LENGTH
(
asciichar
)
FROM
test_char_length
;
+------------------+-------------------+ | LENGTH(utf8char) | LENGTH(asciichar) | +------------------+-------------------+ | 10 | 10 | | 15 | 10 | +------------------+-------------------+ 2 rows in set (0.00 sec)
Da die Werte linksbündig sind und rechts mit Leerzeichen aufgefüllt werden, und alle nachfolgenden Leerzeichen für
CHAR
überhaupt nicht berücksichtigt werden, ist es unmöglich, Zeichenketten zu vergleichen, die nur aus Leerzeichen bestehen. Wenn du dich in einer Situation befindest, in der das wichtig ist, istVARCHAR
der richtige Datentyp. BINARY[(width)]
undVARBINARY(width)
-
Diese Typen sind
CHAR
undVARCHAR
sehr ähnlich, aber speichern binäre Zeichenketten. Binäre Zeichenketten haben den speziellenbinary
Zeichensatz und die Sortierung, und die Sortierung ist abhängig von den numerischen Werten der Bytes in den gespeicherten Werten. Anstelle von Zeichenketten werden Byte-Zeichenketten gespeichert. In der früheren Diskussion überVARCHAR
haben wir denbinary
Zeichensatz und dasBINARY
Attribut beschrieben. Nur derbinary
Zeichensatz "wandelt" eineVARCHAR
oderCHAR
in die entsprechendeBINARY
Form um. Die Anwendung desBINARY
Attributs auf einen Zeichensatz ändert nichts an der Tatsache, dass Zeichenketten gespeichert werden. Anders als beiVARCHAR
undCHAR
,width
ist hier genau die Anzahl der Bytes. Wennwidth
fürBINARY
weggelassen wird, ist es standardmäßig 1.Wie bei
CHAR
werden die Daten in der SpalteBINARY
auf der rechten Seite aufgefüllt. Da es sich jedoch um binäre Daten handelt, werden sie mit Null-Bytes aufgefüllt, die normalerweise als0x00
oder\0
geschrieben werden.BINARY
behandelt ein Leerzeichen als signifikantes Zeichen, nicht als Auffüllung. Wenn du Daten speichern musst, die mit Null-Bytes enden könnten, die für dich von Bedeutung sind, verwende die TypenVARBINARY
oderBLOB
.Bei der Arbeit mit diesen beiden Datentypen ist es wichtig, das Konzept der binären Zeichenketten im Hinterkopf zu behalten. Auch wenn sie Zeichenketten akzeptieren, sind sie keine Synonyme für Datentypen mit Textstrings. Du kannst zum Beispiel die Groß- und Kleinschreibung der gespeicherten Buchstaben nicht ändern, da dieses Konzept auf binäre Daten nicht wirklich zutrifft. Das wird deutlich, wenn du dir die gespeicherten Daten ansiehst. Schauen wir uns ein Beispiel an:
mysql
>
CREATE
TABLE
test_binary_data
(
-
>
d1
BINARY
(
16
)
-
>
,
d2
VARBINARY
(
16
)
-
>
,
d3
CHAR
(
16
)
-
>
,
d4
VARCHAR
(
16
)
-
>
)
;
Query OK, 0 rows affected (0.03 sec)
mysql
>
INSERT
INTO
test_binary_data
VALUES
(
-
>
'something'
-
>
,
'something'
-
>
,
'something'
-
>
,
'something'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
d1
,
d2
,
d3
,
d4
FROM
test_binary_data
;
*************************** 1. row *************************** d1: 0x736F6D657468696E6700000000000000 d2: 0x736F6D657468696E67 d3: something d4: something 1 row in set (0.00 sec)
mysql
>
SELECT
UPPER
(
d2
)
,
UPPER
(
d4
)
FROM
test_binary_data
;
*************************** 1. row *************************** UPPER(d2): 0x736F6D657468696E67 UPPER(d4): SOMETHING 1 row in set (0.01 sec)
Beachte, dass der MySQL-Befehlszeilen-Client die Werte von Binärtypen im Hex-Format anzeigt. Wir glauben, dass dies viel besser ist als die stillen Konvertierungen, die vor MySQL 8.0 durchgeführt wurden und zu Missverständnissen führen konnten. Um die tatsächlichen Textdaten zurückzubekommen, musst du die Binärdaten explizit in Text umwandeln:
mysql
>
SELECT
CAST
(
d1
AS
CHAR
)
d1t
,
CAST
(
d2
AS
CHAR
)
d2t
-
>
FROM
test_binary_data
;
+------------------+-----------+ | d1t | d2t | +------------------+-----------+ | something | something | +------------------+-----------+ 1 row in set (0.00 sec)
Du kannst auch sehen, dass die Auffüllungen von
BINARY
beim Casting in Leerzeichen umgewandelt wurden. BLOB[(width)]
undTEXT[(width)] [CHARACTER SET charset_name] [COLLATE collation_name]
-
BLOB
undTEXT
sind häufig verwendete Datentypen, um große Daten zu speichern. Du kannst dirBLOB
alsVARBINARY
vorstellen, das so viele Daten speichert, wie du willst, und dasselbe gilt fürTEXT
undVARCHAR
. Die TypenBLOB
undTEXT
können bis zu 65.535 Bytes bzw. Zeichen speichern. Wie immer gilt: Es gibt auch Multibyte-Zeichensätze. Daswidth
Attribut ist optional. Wenn es angegeben wird, ändert MySQL den DatentypBLOB
oderTEXT
in den kleinsten Typ, der diese Datenmenge speichern kann. Die AngabeBLOB(128)
führt beispielsweise dazu, dassTINYBLOB
verwendet wird:mysql
>
CREATE
TABLE
test_blob
(
data
BLOB
(
128
)
)
;
Query OK, 0 rows affected (0.07 sec)
mysql
>
DESC
test_blob
;
+-------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+-------+ | data | tinyblob | YES | | NULL | | +-------+----------+------+-----+---------+-------+ 1 row in set (0.00 sec)
Für den Typ
BLOB
und verwandte Typen werden die Daten genauso behandelt wie im Fall vonVARBINARY
. Das heißt, es wird kein Zeichensatz angenommen, und Vergleich und Sortierung basieren auf den numerischen Werten der tatsächlich gespeicherten Bytes. BeiTEXT
kannst du den gewünschten Zeichensatz und die Sortierung genau angeben. Bei beiden Typen und ihren Varianten wird beiINSERT
kein Padding und beiSELECT
kein Trimming durchgeführt, so dass sie ideal sind, um Daten genau so zu speichern, wie sie sind. Außerdem ist eineDEFAULT
Klausel nicht erlaubt, und wenn ein Index für eineBLOB
oderTEXT
Spalte erstellt wird, muss ein Präfix definiert werden, das die Länge der indizierten Werte begrenzt. Mehr darüber erfahren Sie unter "Schlüssel und Indizes".Ein möglicher Unterschied zwischen
BLOB
undTEXT
ist die Behandlung von Leerzeichen am Ende der Zeichenkette. Wie wir bereits gezeigt haben, könnenVARCHAR
undTEXT
abhängig von der verwendeten Sortierreihenfolge Zeichenfolgen auffüllen.BLOB
undVARBINARY
verwenden beide den Zeichensatzbinary
mit einer einzigen Sortierreihenfolgebinary
ohne Auffüllen und sind unempfindlich gegenüber Sortierreihenfolgeverwechslungen und ähnlichen Problemen. Manchmal kann es eine gute Wahl sein, diese Typen für zusätzliche Sicherheit zu verwenden. Außerdem waren dies vor MySQL 8.0 die einzigen Typen, die Leerzeichen am Ende der Zeile erhalten haben. TINYBLOB
undTINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Diese sind identisch mit
BLOB
bzw.TEXT
, außer dass maximal 255 Bytes oder Zeichen gespeichert werden können. MEDIUMBLOB
undMEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Diese sind identisch mit
BLOB
bzw.TEXT
, mit dem Unterschied, dass maximal 16.777.215 Bytes oder Zeichen gespeichert werden können. Die TypenLONG
undLONG VARCHAR
werden aus Kompatibilitätsgründen auf den DatentypMEDIUMTEXT
abgebildet. LONGBLOB
undLONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Diese sind identisch mit
BLOB
bzw.TEXT
, mit dem Unterschied, dass maximal 4 GB an Daten gespeichert werden können. Beachte, dass dies auch im Fall vonLONGTEXT
eine harte Grenze ist und somit die Anzahl der Zeichen in Multibyte-Zeichensätzen weniger als 4.294.967.295 betragen kann. Die tatsächliche Maximalgröße der Daten, die von einem Client gespeichert werden können, wird durch den verfügbaren Speicher und den Wert dermax_packet_size
Variable begrenzt, der standardmäßig 64 MiB beträgt. ENUM(value1[,value2[, …]]) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Dieser Typ speichert eine Liste oder Aufzählung von String-Werten. Eine Spalte vom Typ
ENUM
kann auf einen Wert aus der Liste gesetzt werdenvalue1
,value2
gesetzt werden, und so weiter, bis zu einem Maximum von 65.535 verschiedenen Werten. Während die Werte als Strings gespeichert und abgerufen werden, wird in der Datenbank eine ganzzahlige Darstellung gespeichert. Die SpalteENUM
kann die WerteNULL
(gespeichert alsNULL
), die leere Zeichenkette''
(gespeichert als0
) oder eines der gültigen Elemente (gespeichert als1
,2
,3
, usw.) enthalten. Du kannst verhindern, dassNULL
Werte akzeptiert werden, indem du die Spalte bei der Erstellung der Tabelle alsNOT NULL
deklarierst.Dieser Typ bietet eine kompakte Möglichkeit, Werte aus einer Liste von vordefinierten Werten zu speichern, z. B. Staats- oder Ländernamen. Der Name kann einer der vordefinierten Werte
Apple
,Orange
oderPear
sein (zusätzlich zuNULL
und dem leeren String):mysql
>
CREATE
TABLE
fruits_enum
-
>
(
fruit_name
ENUM
(
'Apple'
,
'Orange'
,
'Pear'
)
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Apple'
)
;
Query OK, 1 row affected (0.00 sec)
Wenn du versuchst, einen Wert einzufügen, der nicht in der Liste steht, gibt MySQL eine Fehlermeldung aus, um dir mitzuteilen, dass es die gewünschten Daten nicht gespeichert hat:
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Banana'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
Eine Liste mit mehreren erlaubten Werten wird ebenfalls nicht akzeptiert:
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Apple,Orange'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
Wenn du dir den Inhalt der Tabelle anschaust, kannst du sehen, dass keine ungültigen Werte gespeichert wurden:
mysql
>
SELECT
*
FROM
fruits_enum
;
+------------+ | fruit_name | +------------+ | Apple | +------------+ 1 row in set (0.00 sec)
Frühere Versionen von MySQL erzeugten eine Warnung statt einer Fehlermeldung und speicherten einen leeren String anstelle eines ungültigen Wertes. Dieses Verhalten kann durch Deaktivieren des standardmäßigen Strict-SQL-Modus aktiviert werden. Es ist auch möglich, einen anderen Standardwert als einen leeren String anzugeben:
mysql
>
CREATE
TABLE
new_fruits_enum
-
>
(
fruit_name
ENUM
(
'Apple'
,
'Orange'
,
'Pear'
)
-
>
DEFAULT
'Pear'
)
;
Query OK, 0 rows affected (0.01 sec)
mysql
>
INSERT
INTO
new_fruits_enum
VALUES
(
)
;
Query OK, 1 row affected (0.02 sec)
mysql
>
SELECT
*
FROM
new_fruits_enum
;
+------------+ | fruit_name | +------------+ | Pear | +------------+ 1 row in set (0.00 sec)
Wenn du hier keinen Wert angibst, wird der Standardwert
Pear
gespeichert. SET( value1 [, value2 [, …]]) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Dieser Typ speichert eine Reihe von String-Werten. Eine Spalte des Typs
SET
kann auf null oder mehr Werte aus der folgenden Liste gesetzt werdenvalue1
,value2
und so weiter, bis zu einem Maximum von 64 verschiedenen Werten. Die Werte sind zwar Zeichenketten, werden aber in der Datenbank als Ganzzahl gespeichert.SET
unterscheidet sich vonENUM
dadurch, dass jede Zeile nur einenENUM
Wert in einer Spalte speichern kann, aber mehrereSET
Werte. Dieser Typ ist nützlich, um eine Auswahl aus einer Liste zu speichern, z. B. die Vorlieben der Benutzer. In diesem Beispiel werden Obstnamen verwendet; der Name kann eine beliebige Kombination der vordefinierten Werte sein:mysql
>
CREATE
TABLE
fruits_set
-
>
(
fruit_name
SET
(
'Apple'
,
'Orange'
,
'Pear'
)
)
;
Query OK, 0 rows affected (0.08 sec)
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Apple'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Banana'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Apple,Orange'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
*
FROM
fruits_set
;
+--------------+ | fruit_name | +--------------+ | Apple | | Apple,Orange | +--------------+ 2 rows in set (0.00 sec)
Beachte auch hier, dass wir mehrere Werte aus der Menge in einem einzigen Feld speichern können und dass bei ungültigen Eingaben ein leerer String gespeichert wird.
Wie bei den numerischen Typen empfehlen wir, dass du immer den kleinstmöglichen Typ wählst, um Werte zu speichern. Wenn du zum Beispiel den Namen einer Stadt speicherst, solltest du CHAR
oder VARCHAR
verwenden und nicht etwa den Typ TEXT
. Kürzere Spalten tragen dazu bei, die Größe deiner Tabelle gering zu halten, was sich wiederum positiv auf die Leistung auswirkt, wenn der Server eine Tabelle durchsuchen muss.
Die Verwendung einer festen Größe mit dem Typ CHAR
ist oft schneller als die Verwendung einer variablen Größe mit VARCHAR
, da der MySQL-Server weiß, wo jede Zeile beginnt und endet, und schnell über Zeilen springen kann, um die benötigte zu finden. Bei Feldern mit fester Länge wird jedoch jeder Platz, den du nicht nutzt, verschwendet. Wenn du z. B. bis zu 40 Zeichen für einen Städtenamen zulässt, verwendet CHAR(40)
immer 40 Zeichen, egal wie lang der Name der Stadt tatsächlich ist. Wenn du den Städtenamen als VARCHAR(40)
deklarierst, belegst du nur so viel Speicherplatz, wie du brauchst, plus 1 Byte für die Länge des Namens. Wenn der durchschnittliche Städtename 10 Zeichen lang ist, bedeutet das, dass bei Verwendung eines Feldes mit variabler Länge im Durchschnitt 29 Bytes weniger pro Eintrag benötigt werden. Das kann einen großen Unterschied machen, wenn du Millionen von Adressen speicherst.
Im Allgemeinen solltest du ein Feld mit variabler Länge verwenden, wenn der Speicherplatz knapp ist oder du große Schwankungen in der Länge der zu speichernden Zeichenketten erwartest; wenn die Leistung im Vordergrund steht, solltest du ein Feld mit fester Länge verwenden.
Datum- und Zeittypen
Diese Typen dienen dazu, bestimmte Zeitstempel, Daten oder Zeitbereiche zu speichern. Besondere Vorsicht ist geboten, wenn du mit Zeitzonen zu tun hast. Wir werden versuchen, die Details zu erklären, aber es lohnt sich, diesen Abschnitt und die Dokumentation später noch einmal zu lesen, wenn du tatsächlich mit Zeitzonen arbeiten musst. Die Datums- und Zeittypen in MySQL sind:
DATE
-
Speichert und zeigt ein Datum im Format
YYYY-MM-DD
für den Bereich 1000-01-01 bis 9999-12-31. Datumsangaben müssen immer als Dreiergruppe aus Jahr, Monat und Tag eingegeben werden, aber das Format der Eingabe kann variieren, wie in den folgenden Beispielen gezeigt wird:YYYY-MM-DD
oderYY-MM-DD
-
Es ist optional ob du zweistellige oder vierstellige Jahreszahlen angibst. Wir empfehlen dir dringend, die vierstellige Version zu verwenden, um Verwirrung über das Jahrhundert zu vermeiden. Wenn du die zweistellige Version verwendest, wirst du in der Praxis feststellen, dass 70 bis 99 als 1970 bis 1999 interpretiert werden und 00 bis 69 als 2000 bis 2069.
YYYY/MM/DD
,YYYY:MM:DD
,YY-MM-DD
, oder andere Formate mit Satzzeichen-
MySQL erlaubt beliebige Interpunktionszeichen, um die Komponenten eines Datums zu trennen. Wir empfehlen, Bindestriche zu verwenden und wiederum zweistellige Jahreszahlen zu vermeiden.
YYYY-M-D
,YYYY-MM-D
, oderYYYY-M-DD
-
Wenn Satzzeichen verwendet werden (auch hier ist jedes Satzzeichen erlaubt), können einstellige Tage und Monate als solche angegeben werden. Zum Beispiel kann der 2. Februar 2006 als
2006-2-2
angegeben werden. Die zweistelligen Jahresangaben sind verfügbar, werden aber nicht empfohlen. YYYYMMDD
oderYYMMDD
-
Satzzeichen können in beiden Datumsstilen weggelassen werden, aber die Ziffernfolgen müssen sechs oder acht Ziffern lang sein.
Du kannst auch ein Datum eingeben, indem du sowohl ein Datum als auch eine Uhrzeit in den Formaten angibst, die später für
DATETIME
undTIMESTAMP
beschrieben werden, aber nur die Datumskomponente wird in einer SpalteDATE
gespeichert. Unabhängig von der Eingabeart ist die Speicherung und Anzeige immerYYYY-MM-DD
. Das Null-Datum0000-00-00
ist in allen Versionen erlaubt und kann verwendet werden, um einen unbekannten oder Dummy-Wert darzustellen. Wenn ein Eingabedatum außerhalb des Bereichs liegt, wird das Null-Datum gespeichert. Allerdings ist dies nur in den MySQL-Versionen bis einschließlich 5.6 standardmäßig möglich. Sowohl 5.7 als auch 8.0 setzen standardmäßig SQL-Modi, die dieses Verhalten verbieten:STRICT_TRANS_TABLES
,NO_ZERO_DATE
, undNO_ZERO_IN_DATE
.Wenn du eine ältere Version von MySQL verwendest, empfehlen wir dir, diese Modi zu deiner aktuellen Sitzung hinzuzufügen:
mysql
>
SET
sql_mode
=
CONCAT
(
@
@
sql_mode
,
-
>
',STRICT_TRANS_TABLES'
,
-
>
',NO_ZERO_DATE'
,
',NO_ZERO_IN_DATE'
)
;
Tipp
Du kannst die Variable
sql_mode
auch auf globaler Serverebene und in der Konfigurationsdatei setzen. Diese Variable muss alle Modi auflisten, die du aktivieren möchtest.Hier sind einige Beispiele für das Einfügen von Daten auf einem MySQL 8.0 Server mit Standardeinstellungen:
mysql
>
CREATE
TABLE
testdate
(
mydate
DATE
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/0'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/0' for column 'mydate' at row 1
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/1'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/31'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/31' for column 'mydate' at row 1
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/100'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/100' for column 'mydate' at row 1
Sobald die
INSERT
Anweisungen ausgeführt wurden, enthält die Tabelle die folgenden Daten:mysql
>
SELECT
*
FROM
testdate
;
+------------+ | mydate | +------------+ | 2020-02-01 | +------------+ 1 row in set (0.00 sec)
MySQL hat dich davor geschützt, "schlechte" Daten in deiner Tabelle zu speichern. Manchmal musst du die eigentliche Eingabe aufbewahren und sie später manuell verarbeiten. Das kannst du tun, indem du die oben genannten SQL-Modi aus der Liste der Modi in der Variable
sql_mode
entfernst. In diesem Fall würdest du nach der Ausführung der vorherigenINSERT
Anweisungen die folgenden Daten erhalten:mysql
>
SELECT
*
FROM
testdate
;
+------------+ | mydate | +------------+ | 2020-02-00 | | 2020-02-01 | | 0000-00-00 | | 0000-00-00 | +------------+ 4 rows in set (0.01 sec)
Beachte auch hier, dass das Datum im
YYYY-MM-DD
Format angezeigt wird, unabhängig davon, wie es eingegeben wurde. TIME [fraction]
-
Speichert eine Zeit im Format
HHH:MM:SS
für den Bereich -838:59:59 bis 838:59:59. Dies ist nützlich, um die Dauer einer Aktivität zu speichern. Die Werte, die gespeichert werden können, liegen außerhalb des Bereichs der 24-Stunden-Uhr, damit große Unterschiede zwischen den Zeitwerten (bis zu 34 Tage, 22 Stunden, 59 Minuten und 59 Sekunden) berechnet und gespeichert werden können.fraction
inTIME
und anderen verwandten Datentypen gibt die Genauigkeit der Sekundenbruchteile im Bereich von 0 bis 6 an. Der Standardwert ist 0, was bedeutet, dass keine Sekundenbruchteile erhalten bleiben.Die Zeiten müssen immer in der Reihenfolge Tage, Stunden, Minuten, Sekunden eingegeben werden, wobei die folgenden Formate verwendet werden:
DD HH:MM:SS[.fraction]
,HH:MM:SS[.fraction]
,DD HH:MM
,HH:MM
,DD HH
, oderSS[.fraction]
-
DD
steht für einen ein- oder zweistelligen Wert von Tagen im Bereich von 0 bis 34. DerDD
Wert wird vom Stundenwert getrennt,HH
durch ein Leerzeichen getrennt, während die anderen Komponenten durch einen Doppelpunkt getrennt werden. Beachte, dassMM:SS
keine gültigeKombination ist, da sie nicht eindeutig vonHH:MM
. Wenn in der Definition vonTIME
nichts angegeben istfraction
nicht angegeben oder auf 0 gesetzt wird, führt das Einfügen von Sekundenbruchteilen dazu, dass die Werte auf die nächste Sekunde gerundet werden.Wenn du zum Beispiel
2 13:25:58.999999
in eine SpalteTIME
mit einemfraction
von 0 einfügst, wird der Wert61:25:59
gespeichert, da die Summe von 2 Tagen (48 Stunden) und 13 Stunden 61 Stunden beträgt. Ab MySQL 5.7 verbietet der standardmäßig eingestellte SQL-Modus das Einfügen von falschen Werten. Es ist jedoch möglich, das ältere Verhalten zu aktivieren. Wenn du dann versuchst, einen Wert einzufügen, der außerhalb des zulässigen Bereichs liegt, wird eine Warnung ausgegeben und der Wert wird auf die maximal verfügbare Zeit begrenzt. Wenn du versuchst, einen ungültigen Wert einzufügen, wird ebenfalls eine Warnung ausgegeben und der Wert auf Null gesetzt. Du kannst den BefehlSHOW WARNINGS
verwenden, um die Details der Warnung zu melden, die durch die vorherige SQL-Anweisung erzeugt wurde. Wir empfehlen, den standardmäßigen Strict-SQL-Modus beizubehalten. Anders als beim TypDATE
scheint es keinen Vorteil zu haben, falscheTIME
Einträge zuzulassen, abgesehen von einem einfacheren Fehlermanagement auf der Anwendungsseite und der Beibehaltung von Legacy-Verhaltensweisen.Lass uns das alles in der Praxis ausprobieren:
mysql
>
CREATE
TABLE
test_time
(
id
SMALLINT
,
mytime
TIME
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
test_time
VALUES
(
1
,
"2 13:25:59"
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
test_time
VALUES
(
2
,
"35 13:25:59"
)
;
ERROR 1292 (22007): Incorrect time value: '35 13:25:59' for column 'mytime' at row 1
mysql
>
INSERT
INTO
test_time
VALUES
(
3
,
"900.32"
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
*
FROM
test_time
;
+------+----------+ | id | mytime | +------+----------+ | 1 | 61:25:59 | | 3 | 00:09:00 | +------+----------+ 2 rows in set (0.00 sec)
H:M:S
sowie ein-, zwei- und dreistellige Kombinationen-
Du kannst verschiedene Kombinationen von Ziffern verwenden, wenn du Daten einfügst oder aktualisierst; MySQL wandelt sie in das interne Zeitformat um und zeigt sie einheitlich an. Zum Beispiel ist
1:1:3
gleichbedeutend mit01:01:03
. VerschiedeneZiffernzahlen können gemischt werden; zum Beispiel ist1:12:3
gleichbedeutend mit01:12:03
. Betrachte diese Beispiele:mysql
>
CREATE
TABLE
mytime
(
testtime
TIME
)
;
Query OK, 0 rows affected (0.12 sec)
mysql
>
INSERT
INTO
mytime
VALUES
-
>
(
'-1:1:1'
)
,
(
'1:1:1'
)
,
-
>
(
'1:23:45'
)
,
(
'123:4:5'
)
,
-
>
(
'123:45:6'
)
,
(
'-123:45:6'
)
;
Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
mytime
;
+------------+ | testtime | +------------+ | -01:01:01 | | 01:01:01 | | 01:23:45 | | 123:04:05 | | 123:45:06 | | -123:45:06 | +------------+ 5 rows in set (0.01 sec)
Beachte, dass die Stunden mit zwei Ziffern für Werte im Bereich von -99 bis 99 angezeigt werden.
HHMMSS
,MMSS
undSS
-
Satzzeichen können weggelassen werden, aber die Ziffernfolgen müssen zwei, vier oder sechs Ziffern lang sein. Beachte, dass das äußerste rechte Ziffernpaar immer als ein
SS
(Sekunden) interpretiert wird, das zweite Paar ganz rechts (falls vorhanden) alsMM
(Minuten) und das dritte Paar ganz rechts (falls vorhanden) alsHH
(Stunden). Das bedeutet, dass ein Wert wie1222
als 12 Minuten und 22 Sekunden und nicht als 12 Stunden und 22 Minuten interpretiert wird.Du kannst auch eine Uhrzeit eingeben, indem du sowohl ein Datum als auch eine Uhrzeit in den für
DATETIME
undTIMESTAMP
beschriebenen Formaten angibst, aber nur die Zeitkomponente wird in einer SpalteTIME
gespeichert. Unabhängig von der Eingabeart ist die Speicherung und Anzeige immerHH:MM:SS
. Die Nullzeit00:00:00
kann verwendet werden, um einen unbekannten Wert oder einen Dummy-Wert darzustellen.
TIMESTAMP[(fraction)]
-
Speichert und zeigt ein Datums- und Zeitpaar im Format
YYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
für den Bereich 1970-01-01 00:00:01.000000 bis2038-01-19 03:14:07.999999
. Dieser Typ ist dem TypDATETIME
sehr ähnlich, aber es gibt ein paar Unterschiede. Beide Typen akzeptieren einen Zeitzonenmodifikator für den Eingabewert MySQL 8.0, und beide Typen speichern und präsentieren die Daten auf dieselbe Weise für jeden Client in derselben Zeitzone. Allerdings werden die Werte in denTIMESTAMP
Spalten intern immer in der UTC-Zeitzone gespeichert, so dass es möglich ist, für Clients in verschiedenen Zeitzonen automatisch eine lokale Zeitzone zu erhalten. Das allein ist schon ein sehr wichtiger Unterschied, an den du dich erinnern solltest.TIMESTAMP
ist beim Umgang mit verschiedenen Zeitzonen bequemer zu verwenden.Vor MySQL 5.6 unterstützte nur der Typ
TIMESTAMP
die automatische Initialisierung und Aktualisierung. Außerdem konnte nur eine einzige solche Spalte in einer bestimmten Tabelle dies tun. Ab 5.6 unterstützen sowohlTIMESTAMP
als auchDATETIME
dieses Verhalten und eine beliebige Anzahl von Spalten kann dies tun.Die in einer
TIMESTAMP
Spalte gespeicherten Werte entsprechen immer der VorlageYYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
aber die Werte können in einer Vielzahl von Formaten angegeben werden:YYYY-MM-DD HH:MM:SS
oderYY-MM-DD HH:MM:SS
-
Für die Datums- und Zeitkomponenten gelten dieselben lockeren Beschränkungen wie für die Komponenten
DATE
undTIME
, die zuvor beschrieben wurden. Das bedeutet, dass alle Interpunktionszeichen erlaubt sind, einschließlich (anders als beiTIME
) der Flexibilität bei der Interpunktion in der Zeitkomponente. Zum Beispiel ist0
gültig. YYYYMMDDHHMMSS
oderYYMMDDHHMMSS
-
Satzzeichen können weggelassen werden, aber die Zeichenfolge sollte entweder 12 oder 14 Ziffern lang sein. Wir empfehlen, nur die eindeutige 14-stellige Version zu verwenden, und zwar aus denselben Gründen, die für den Typ
DATE
erläutert wurden. Du kannst auch Werte mit anderen Längen angeben, ohne Trennzeichen zu verwenden, aber wir empfehlen dies nicht.
Schauen wir uns die Funktion der automatischen Aktualisierung genauer an. Du steuerst dies, indem du die folgenden Attribute zur Spaltendefinition hinzufügst, wenn du eine Tabelle erstellst, oder später, wie wir in "Strukturen ändern" erklären werden :
-
Wenn du möchtest, dass der Zeitstempel nur gesetzt wird, wenn eine neue Zeile in die Tabelle eingefügt wird, füge
DEFAULT CURRENT_TIMESTAMP
am Ende der Spaltendeklaration hinzu. -
Wenn du keinen Standard-Zeitstempel möchtest, sondern die aktuelle Zeit von verwendet werden soll, wenn die Daten in einer Zeile aktualisiert werden, füge
ON UPDATE CURRENT_TIMESTAMP
am Ende der Spaltendeklaration hinzu. -
Wenn du beides willst, d.h. wenn du willst, dass der Zeitstempel in jeder neuen Zeile und bei jeder Änderung einer bestehenden Zeile auf die aktuelle Zeit gesetzt wird, füge
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
am Ende der Spaltendeklaration hinzu.
Wenn du für eine Spalte
TIMESTAMP
wederDEFAULT NULL
nochNULL
angibst, hat sie0
als Standardwert. YEAR[(4)]
-
Speichert eine vierstellige Jahreszahl im Bereich 1901 bis 2155 sowie die Jahreszahl Null, 0000. Ungültige Werte werden in die Jahreszahl Null umgewandelt. Du kannst Jahreszahlen entweder als Zeichenketten (wie
'2005'
) oder als ganze Zahlen (wie2005
) eingeben. Der TypYEAR
benötigt 1 Byte Speicherplatz.In früheren Versionen von MySQL war es möglich, den Parameter anzugeben.
digits
entweder2
oder4
zu übergeben. Die zweistellige Version speicherte Werte von 70 bis 69, was 1970 bis 2069 entspricht. MySQL 8.0 unterstützt den zweistelligenYEAR
Typ nicht, und die Angabe desdigits
Parameter für Anzeigezwecke ist veraltet. DATETIME[(fraction)]
-
Speichert und zeigt ein Datums- und Zeitpaar im Format
YYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
für den Bereich1000-01-01
00:00:00
bis9999-12-31 23:59:59
. Wie beiTIMESTAMP
entspricht der gespeicherte Wert immer der VorlageYYYY-MM-DD HH:MM:SS
, aber der Wert kann in denselben Formaten eingegeben werden, die in der Beschreibung vonTIMESTAMP
aufgeführt sind. Wenn du einerDATETIME
Spalte nur ein Datum zuweist, wird die Nullzeit00:00:00
angenommen. Wenn du einerDATETIME
Spalte nur eine Uhrzeit zuweist, wird das Datum0000-00-00
als Null angenommen. Dieser Typ hat die gleichen automatischen Aktualisierungsfunktionen wieTIMESTAMP
. Wenn das AttributNOT NULL
nicht für eineDATETIME
Spalte angegeben wird, ist einNULL
Wert der Standardwert; andernfalls ist der Standardwert0
. Anders als beiTIMESTAMP
werden die Werte vonDATETIME
für dieSpeicherung nicht in die UTC-Zeitzone umgewandelt.
Andere Typen
Seit MySQL 8.0 fallen die Datentypen "Spatial" und " JSON
" ( ) unter diese allgemeine Kategorie. Die Verwendung dieser Datentypen ist ein ziemlich fortgeschrittenes Thema, das wir hier nicht näher behandeln werden.
Räumliche Datentypen befassen sich mit der Speicherung geometrischer Objekte, und MySQL hat Typen, die den OpenGIS-Klassen entsprechen. Die Arbeit mit diesen Typen ist ein Thema, das ein eigenes Buch wert ist.
Der Datentyp JSON
ermöglicht die native Speicherung von gültigen JSON-Dokumenten. Vor MySQL 5.7 wurde JSON normalerweise in einer TEXT
oder einer ähnlichen Spalte gespeichert. Das hat jedoch viele Nachteile: Zum Beispiel werden die Dokumente nicht validiert und die Speicherung wird nicht optimiert (alle JSON-Dokumente werden einfach in ihrer Textform gespeichert). Mit dem nativen Typ JSON
wird es im Binärformat gespeichert. Um es in einem Satz zusammenzufassen: Verwende den Datentyp JSON
für JSON, lieber Leser.
Schlüssel und Indizes
Du wirst feststellen, dass fast alle Tabellen, die du verwendest, eine PRIMARY KEY
Klausel in ihrer CREATE TABLE
Anweisung haben, und manchmal auch mehrere KEY
Klauseln. Die Gründe, warum du einen Primärschlüssel und Sekundärschlüssel brauchst, wurden in Kapitel 2 erläutert. In diesem Abschnitt wird erläutert, wie Primärschlüssel deklariert werden, was dabei hinter den Kulissen passiert und warum du eventuell auch andere Schlüssel und Indizes für deine Daten erstellen solltest.
Ein Primärschlüssel identifiziert jede Zeile in einer Tabelle eindeutig. Noch wichtiger ist, dass bei der standardmäßigen InnoDB Speicherung ein Primärschlüssel auch als geclusterter Index verwendet wird. Das bedeutet, dass alle eigentlichen Tabellendaten in einer Indexstruktur gespeichert sind. Das ist ein Unterschied zu MyISAM, das Daten und Indizes getrennt speichert. Wenn eine Tabelle einen geclusterten Index verwendet, nennt man sie eine geclusterte Tabelle. Wie bereits erwähnt, wird in einer geclusterten Tabelle jede Zeile in einem Index gespeichert, im Gegensatz zu einem Heap, wie er normalerweise genannt wird. Das Clustern einer Tabelle führt dazu, dass die Zeilen nach der Reihenfolge des Cluster-Indexes sortiert werden und tatsächlich in den Blattseiten dieses Indexes gespeichert werden. Es kann nicht mehr als einen geclusterten Index pro Tabelle geben. Bei solchen Tabellen beziehen sich die sekundären Indizes auf die Datensätze im geclusterten Index und nicht auf die eigentlichen Tabellenzeilen. Das führt in der Regel zu einer verbesserten Abfrageleistung, kann sich aber nachteilig auf die Schreibvorgänge auswirken. InnoDB erlaubt es dir nicht, zwischen geclusterten und nicht geclusterten Tabellen zu wählen; dies ist eine Designentscheidung, die du nicht ändern kannst.
Primärschlüssel sind im Allgemeinen ein empfohlener Teil jedes Datenbankdesigns, aber für InnoDB sind sie notwendig. Wenn du bei der Erstellung einer InnoDB-Tabelle keine PRIMARY KEY
Klausel angibst, verwendet MySQL die erste UNIQUE NOT NULL
Spalte als Basis für den Clustered Index. Wenn keine solche Spalte vorhanden ist, wird ein versteckter Cluster-Index erstellt, der auf den ID-Werten basiert, die InnoDB jeder Zeile zuweist.
Da InnoDB die Standard-Speicher-Engine von MySQL ist und heutzutage ein De-facto-Standard ist, werden wir uns in diesem Kapitel auf sein Verhalten konzentrieren. Alternative Speicher-Engines wie MyISAM, MEMORY oder MyRocks werden im Abschnitt "Alternative Speicher-Engines" behandelt .
Wie bereits erwähnt, wird ein Primärschlüssel zu einem geclusterten Index, und alle Daten der Tabelle werden in den Blattblöcken dieses Indexes gespeichert. InnoDB verwendet B-Baum-Indizes (genauer gesagt, die B+Baum-Variante), mit Ausnahme von Indizes für räumliche Datentypen, die die R-Baum-Struktur verwenden. Andere Speicher-Engines können andere Indextypen implementieren, aber wenn die Speicher-Engine einer Tabelle nicht angegeben ist, kannst du davon ausgehen, dass alle Indizes B-Bäume sind.
Mit einem geclusterten Index, oder anders gesagt, mit indexorganisierten Tabellen, werden Abfragen und Sortierungen der Primärschlüsselspalten beschleunigt. Ein Nachteil ist jedoch, dass das Ändern von Spalten in einem Primärschlüssel teuer ist. Ein gutes Design erfordert daher einen Primärschlüssel, der auf Spalten basiert, die häufig zum Filtern in Abfragen verwendet, aber selten geändert werden. Denke daran, dass ein fehlender Primärschlüssel dazu führt, dass InnoDB einen impliziten Cluster-Index verwendet. Wenn du dir also nicht sicher bist, welche Spalten du für einen Primärschlüssel auswählen sollst, kannst du eine synthetische id
-ähnliche Spalte verwenden. In diesem Fall könnte zum Beispiel der Datentyp SERIAL
gut geeignet sein.
Von den internen Details von InnoDB einmal abgesehen: Wenn du einen Primärschlüssel für eine Tabelle in MySQL deklarierst, wird eine Struktur erstellt, die Informationen darüber speichert, wo die Daten jeder Zeile in der Tabelle gespeichert sind. Diese Informationen werden als Index bezeichnet und dienen dazu, die Suche nach dem Primärschlüssel zu beschleunigen. Wenn du zum Beispiel PRIMARY KEY (actor_id)
in der Tabelle actor
in der Datenbank sakila
deklarierst, erstellt MySQL eine Struktur, die es ihm ermöglicht, Zeilen, die mit einem bestimmten actor_id
(oder einer Reihe von Bezeichnern) übereinstimmen, extrem schnell zu finden.
Das ist nützlich, um z. B. Schauspieler mit Filmen oder Filme mit Kategorien zu verbinden. Du kannst die in einer Tabelle verfügbaren Indizes mit demBefehl SHOW INDEX
(oder SHOW INDEXES
) anzeigen:
mysql
>
SHOW
INDEX
FROM
category
\
G
*************************** 1. row *************************** Table: category Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: category_id Collation: A Cardinality: 16 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: Visible: YES Expression: NULL 1 row in set (0.00 sec)
Die Kardinalität ist die Anzahl der eindeutigen Werte im Index; für einen Index auf einem Primärschlüssel ist dies die gleiche wie die Anzahl der Zeilen in der Tabelle.
Beachte, dass alle Spalten, die Teil eines Primärschlüssels sind, als NOT NULL
deklariert sein müssen, da sie einen Wert haben müssen, damit die Zeile gültig ist. Ohne den Index besteht die einzige Möglichkeit, Zeilen in der Tabelle zu finden, darin, jede einzelne Zeile von der Festplatte zu lesen und zu prüfen, ob sie mit der category_id
übereinstimmt, nach der du suchst. Bei Tabellen mit vielen Zeilen ist diese erschöpfende, sequentielle Suche extrem langsam. Du kannst aber nicht einfach alles indizieren; darauf kommen wir am Ende dieses Abschnitts zurück.
Du kannst weitere Indizes für die Daten in einer Tabelle erstellen. Das tust du, damit andere Suchvorgänge (ob auf anderen Spalten oder Kombinationen von Spalten) schnell sind und um sequenzielle Scans zu vermeiden. Nehmen wir zum Beispiel wieder die Tabelle actor
. Sie hat nicht nur einenPrimärschlüssel für actor_id
, sondern auch einen Sekundärschlüssel für last_name
, um die Suche nach dem Nachnamen eines Schauspielers zu verbessern:
mysql
>
SHOW
CREATE
TABLE
actor
\
G
*************************** 1. row *************************** Table: actor Create Table: CREATE TABLE `actor` ( `actor_id` smallint unsigned NOT NULL AUTO_INCREMENT, ... `last_name` varchar(45) NOT NULL, ... PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ... 1 row in set (0.00 sec)
Du kannst sehen, dass das Schlüsselwort KEY
verwendet wird, um MySQL mitzuteilen, dass ein zusätzlicher Index benötigt wird. Alternativ kannst du auch das Wort INDEX
anstelle von KEY
verwenden. Nach diesem Schlüsselwort folgt ein Indexname, und dann wird die Spalte, die indiziert werden soll, in Klammern gesetzt. Du kannst auch Indizes hinzufügen, nachdem die Tabellen erstellt wurden - du kannst so ziemlich alles an einer Tabelle ändern, nachdem sie erstellt wurde. Das wird im Abschnitt "Strukturen ändern" behandelt .
Du kannst einen Index für mehr als eine Spalte erstellen. Betrachte zum Beispiel die folgende Tabelle, die eine modifizierte Tabelle von sakila
ist:
mysql
>
CREATE
TABLE
customer_mod
(
-
>
customer_id
smallint
unsigned
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
varchar
(
45
)
NOT
NULL
,
-
>
last_name
varchar
(
45
)
NOT
NULL
,
-
>
varchar
(
50
)
DEFAULT
NULL
,
-
>
PRIMARY
KEY
(
customer_id
)
,
-
>
KEY
idx_names_email
(
first_name
,
last_name
,
)
)
;
Query OK, 0 rows affected (0.06 sec)
Wie du siehst, haben wir einen Primärschlüsselindex für die Identifizierungsspalte customer_id
und einen weiteren Index - idx_names_email
- hinzugefügt, der die Spalten first_name
, last_name
und email
in dieser Reihenfolge umfasst. Überlegen wir nun, wie du diesen zusätzlichen Index verwenden kannst.
Du kannst den Index idx_names_email
für die schnelle Suche nach Kombinationen der drei Namensspalten verwenden. Er ist zum Beispiel in der folgenden Abfrage nützlich:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
;
Wir wissen, dass es bei der Suche hilft, weil alle im Index aufgeführten Spalten in der Abfrage verwendet werden. Mit der Anweisung EXPLAIN
kannst du überprüfen, ob das, was du glaubst, dass es passieren sollte, auch tatsächlich passiert:
mysql
>
EXPLAIN
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: customer_mod partitions: NULL type: ref possible_keys: idx_names_email key: idx_names_email key_len: 567 ref: const,const,const rows: 1 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
Wie du siehst, meldet MySQL, dass die possible_keys
idx_names_email
sind (was bedeutet, dass der Index für diese Abfrage verwendet werden könnte) und dass die key
, für die er sich entschieden hat, idx_names_email
ist. Was du erwartest und was passiert, ist also dasselbe, und das ist eine gute Nachricht! Mehr über die Anweisung EXPLAIN
erfährst du in Kapitel 7.
Der Index, den wir erstellt haben, ist auch nützlich für Abfragen, die nur die Spalte first_name
betreffen. Er kann zum Beispiel von der folgenden Abfrage verwendet werden:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
;
Du kannst EXPLAIN
erneut verwenden, um zu prüfen, ob der Index verwendet wird. Der Grund dafür, dass er verwendet werden kann, ist, dass die Spalte first_name
die erste im Index ist. In der Praxis bedeutet das, dass der Index die Informationen über die Zeilen aller Personen mit demselben Vornamen bündelt bzw. speichert und somit alle Personen mit demselben Vornamen finden kann.
Der Index kann auch für die Suche nach Kombinationen aus Vor- und Nachnamen verwendet werden, und zwar aus genau denselben Gründen, die wir gerade besprochen haben. Der Index fasst Personen mit demselben Vornamen zusammen und gruppiert Personen mit identischen Vornamen nach dem Nachnamen. Er kann also für diese Abfrage verwendet werden:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
;
Der Index kann jedoch nicht für diese Abfrage verwendet werden, weil die Spalte ganz links im Index, first_name
, nicht in der Abfrage vorkommt:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
;
Der Index soll helfen, die Menge der Zeilen auf eine kleinere Menge möglicher Antworten einzugrenzen. Damit MySQL einen Index verwenden kann, muss die Abfrage die folgenden beiden Bedingungen erfüllen:
-
Die Spalte ganz links in der
KEY
(oderPRIMARY KEY
) Klausel muss in der Abfrage enthalten sein. -
Die Abfrage darf keine
OR
Klauseln für Spalten enthalten, die nicht indiziert sind.
Auch hier kannst du die EXPLAIN
Anweisung verwenden, um zu prüfen, ob ein Index für eine bestimmte Abfrage verwendet werden kann.
Bevor wir diesen Abschnitt beenden, hier noch ein paar Ideen, wie du Indizes auswählen und gestalten kannst. Wenn du überlegst, einen Index einzufügen, solltest du Folgendes bedenken:
-
Indizes kosten Speicherplatz auf der Festplatte und müssen bei jeder Datenänderung aktualisiert werden. Wenn sich deine Daten häufig ändern oder sich viele Daten ändern, wenn du eine Änderung vornimmst, verlangsamen Indizes den Prozess. In der Praxis sind Indizes jedoch in der Regel von Vorteil, da
SELECT
Anweisungen (Datenlesen) viel häufiger vorkommen als andere Anweisungen (Datenänderungen). -
Füge nur einen Index hinzu, der häufig genutzt werden wird. Mach dir nicht die Mühe, Spalten zu indizieren, bevor du siehst, welche Abfragen deine Nutzer und deine Anwendungen benötigen. Du kannst nachträglich immer noch Indizes hinzufügen.
-
Wenn alle Spalten in einem Index in allen Abfragen verwendet werden, führe die Spalte mit der höchsten Anzahl an Duplikaten links in der
KEY
Klausel auf. Dadurch wird die Indexgröße minimiert. -
Je kleiner der Index ist, desto schneller wird er sein. Wenn du große Spalten indizierst, bekommst du einen größeren Index. Das ist ein guter Grund, um sicherzustellen, dass deine Spalten so klein wie möglich sind, wenn du deine Tabellen entwirfst.
-
Bei langen Spalten kannst du nur ein Präfix der Werte aus einer Spalte verwenden, um den Index zu erstellen. Dazu fügst du nach der Spaltendefinition einen Wert in Klammern hinzu, z. B.
KEY idx_names_email (first_name(3), last_name(2), email(10))
. Das bedeutet, dass nur die ersten 3 Zeichen vonfirst_name
indexiert werden, dann die ersten 2 Zeichen vonlast_name
und dann 10 Zeichen vonemail
. Das ist eine erhebliche Ersparnis gegenüber der Indexierung von 140 Zeichen aus den drei Spalten! Wenn du so vorgehst, ist dein Index weniger in der Lage, Zeilen eindeutig zu identifizieren, aber er ist viel kleiner und immer noch recht gut darin, passende Zeilen zu finden. Die Verwendung eines Präfixes ist für lange Typen wieTEXT
obligatorisch.
Zum Abschluss dieses Abschnitts müssen wir noch einige Besonderheiten in Bezug auf Sekundärschlüssel in InnoDB besprechen. Erinnere dich daran, dass alle Tabellendaten in den Blättern des geclusterten Index gespeichert sind. actor
Das bedeutet, dass wir auf die Daten des Primärschlüssels zugreifen müssen, wenn wir die Daten von first_name
benötigen, um nach last_name
zu filtern, auch wenn wir idx_actor_last_name
für eine schnelle Filterung verwenden können. Folglich werden bei jedem Sekundärschlüssel in InnoDB alle Primärschlüsselspalten implizit an seine Definition angehängt. Unnötig lange Primärschlüssel in InnoDB führen daher zu deutlich aufgeblähten Sekundärschlüsseln.
Das kann man auch in der Ausgabe von EXPLAIN
sehen (beachte die Extra: Using index
in der ersten Ausgabe des ersten Befehls):
mysql
>
EXPLAIN
SELECT
actor_id
,
last_name
FROM
actor
WHERE
last_name
=
'Smith'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 182 ref: const rows: 1 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
mysql
>
EXPLAIN
SELECT
first_name
FROM
actor
WHERE
last_name
=
'Smith'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 182 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
Tatsächlich ist idx_actor_last_name
ein abdeckender Index für die erste Abfrage, was bedeutet, dass InnoDB alle benötigten Daten allein aus diesem Index extrahieren kann. Für die zweite Abfrage muss InnoDB jedoch einen zusätzlichen Lookup eines geclusterten Indexes durchführen, um den Wert für die Spalte first_name
zu erhalten.
Die AUTO_INCREMENT-Funktion
Mit der MySQL-eigenen Funktion AUTO_INCREMENT
kannst du einen eindeutigen Bezeichner für eine Zeile erstellen, ohne eine SELECT
Abfrage zu starten. So funktioniert es. Nehmen wir wieder die vereinfachte Tabelle actor
:
mysql
>
CREATE
TABLE
actor
(
-
>
actor_id
smallint
unsigned
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
varchar
(
45
)
NOT
NULL
,
-
>
last_name
varchar
(
45
)
NOT
NULL
,
-
>
PRIMARY
KEY
(
actor_id
)
-
>
)
;
Query OK, 0 rows affected (0.03 sec)
Es ist möglich, Zeilen in diese Tabelle einzufügen, ohne die actor_id
anzugeben:
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Alexander'
,
'Kaidanovsky'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Anatoly'
,
'Solonitsyn'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Nikolai'
,
'Grinko'
)
;
Query OK, 1 row affected (0.00 sec)
Wenn du dir die Daten in dieser Tabelle ansiehst, kannst du sehen, dass jeder Zeile ein Wert für die Spalte actor_id
zugewiesen ist:
mysql
>
SELECT
*
FROM
actor
;
+----------+------------+-------------+ | actor_id | first_name | last_name | +----------+------------+-------------+ | 1 | Alexander | Kaidanovsky | | 2 | Anatoly | Solonitsyn | | 3 | Nikolai | Grinko | +----------+------------+-------------+ 3 rows in set (0.00 sec)
Jedes Mal, wenn eine neue Zeile eingefügt wird, wird ein eindeutiger Wert für die Spalte actor_id
für diese neue Zeile erstellt.
Schau dir an, wie diese Funktion funktioniert. Wie du siehst, wird die Spalte actor_id
mit den Klauseln NOT NULL AUTO_INCREMENT
als Ganzzahl deklariert. AUTO_INCREMENT
teilt MySQL mit, dass, wenn kein Wert für diese Spalte angegeben wird, der zugewiesene Wert um eins höher sein soll als der derzeit in der Tabelle gespeicherte Höchstwert. Die Folge AUTO_INCREMENT
beginnt bei einer leeren Tabelle mit 1.
Die NOT NULL
Klausel ist für AUTO_INCREMENT
Spalten erforderlich; wenn du NULL
einfügst (oder 0, obwohl das nicht empfohlen wird), findet der MySQL-Server automatisch den nächsten verfügbaren Bezeichner und weist ihn der neuen Zeile zu. Du kannst manuell negative Werte einfügen, wenn die Spalte nicht als UNSIGNED
definiert wurde; bei der nächsten automatischen Erhöhung verwendet MySQL jedoch einfach den größten (positiven) Wert in der Spalte oder beginnt bei 1, wenn es keine positiven Werte gibt.
Die Funktion AUTO_INCREMENT
hat die folgenden Voraussetzungen:
-
Die Spalte, für die sie verwendet wird, muss indiziert sein.
-
Die Spalte, für die sie verwendet wird, darf keinen
DEFAULT
Wert haben. -
Es kann nur eine
AUTO_INCREMENT
Spalte pro Tabelle geben.
MySQL unterstützt verschiedene Speicher-Engines, über die wir im Abschnitt "Alternative Speicher-Engines" mehr erfahren . Wenn du den nicht standardmäßigen MyISAM-Tabellentyp verwendest, kannst du die Funktion AUTO_INCREMENT
für Schlüssel verwenden, die aus mehreren Spalten bestehen. Du kannst also mehrere unabhängige Zähler in einer einzigen AUTO_INCREMENT
Spalte haben. Mit InnoDB ist dies jedoch nicht möglich.
Die Funktion AUTO_INCREMENT
ist zwar nützlich, aber nicht auf andere Datenbankumgebungen übertragbar, und sie verbirgt die logischen Schritte zur Erstellung neuer Bezeichner. Sie kann auch zu Unklarheiten führen: Wenn du zum Beispiel eine Tabelle löschst oder abschneidest, wird der Zähler zurückgesetzt, aber wenn du ausgewählte Zeilen löschst (mit einer WHERE
Klausel), wird der Zähler nicht zurückgesetzt. Wenn eine Zeile innerhalb einer Transaktion eingefügt wird und diese Transaktion dann zurückgenommen wird, wird der Bezeichner trotzdem verbraucht. Als Beispiel erstellen wir die Tabelle count
, die ein automatisch inkrementierendes Feld counter
enthält:
mysql
>
CREATE
TABLE
count
(
counter
INT
AUTO_INCREMENT
KEY
)
;
Query OK, 0 rows affected (0.13 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | +---------+ 6 rows in set (0.00 sec)
Das Einfügen mehrerer Werte funktioniert wie erwartet. Jetzt wollen wir ein paar Zeilen löschen und dann sechs neue Zeilen hinzufügen:
mysql
>
DELETE
FROM
count
WHERE
counter
>
4
;
Query OK, 2 rows affected (0.00 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.00 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 7 | | 8 | | 9 | | 10 | | 11 | | 12 | +---------+ 10 rows in set (0.00 sec)
Hier sehen wir, dass der Zähler nicht zurückgesetzt wird und bei 7 weiterläuft. Wenn wir jedoch die Tabelle abschneiden, also alle Daten entfernen, wird der Zähler auf 1 zurückgesetzt:
mysql
>
TRUNCATE
TABLE
count
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | +---------+ 6 rows in set (0.00 sec)
Zusammengefasst: AUTO_INCREMENT
garantiert eine Abfolge von transaktionalen und monoton ansteigenden Werten. Es garantiert jedoch in keiner Weise, dass jeder einzelne angegebene Bezeichner genau auf den vorherigen folgt. Normalerweise ist dieses Verhalten von AUTO_INCREMENT
klar genug und sollte kein Problem darstellen. Wenn dein spezieller Anwendungsfall jedoch einen Zähler erfordert, der garantiert keine Lücken aufweist, solltest du eine Umgehungslösung in Betracht ziehen. Leider wird diese wahrscheinlich auf der Anwendungsseite implementiert werden.
Strukturen verändern
Wir haben dir alle Grundlagen gezeigt, die du zum Erstellen von Datenbanken, Tabellen, Indizes und Spalten brauchst. In diesem Abschnitt lernst du, wie du Spalten, Datenbanken, Tabellen und Indizes in bereits bestehenden Strukturen hinzufügst, entfernst und änderst.
Hinzufügen, Entfernen und Ändern von Spalten
Mit der Anweisung ALTER TABLE
kannst du neue Spalten zu einer Tabelle hinzufügen, vorhandene Spalten entfernen und Spaltennamen, -typen und -längen ändern.
Beginnen wir mit der Frage, wie du bestehende Spalten änderst. Nehmen wir ein Beispiel, in dem wir eine Tabellenspalte umbenennen. Die Tabelle language
hat eine Spalte mit dem Namen last_update
, die den Zeitpunkt enthält, zu dem der Datensatz geändert wurde. Um den Namen dieser Spalte in last_updated_time
zu ändern, würdest du schreiben:
mysql
>
ALTER
TABLE
language
RENAME
COLUMN
last_update
TO
last_updated_time
;
Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
In diesem Beispiel wird die Online-DDL-Funktion von MySQL verwendet. Was tatsächlich hinter den Kulissen passiert, ist, dass MySQL nur die Metadaten ändert und die Tabelle nicht wirklich neu schreiben muss. Das kannst du daran erkennen, dass keine Zeilen betroffen sind. Nicht alle DDL-Anweisungen können online ausgeführt werden, daher wird dies bei vielen Änderungen, die du vornimmst, nicht der Fall sein.
Hinweis
DDL steht für Data Definition Language und ist im Kontext von SQL eine Untergruppe von Syntax und Anweisungen, die zum Erstellen, Ändern und Löschen von Schemaobjekten wie Datenbanken, Tabellen, Indizes und Spalten verwendet werden. CREATE TABLE
und ALTER TABLE
sind zum Beispiel beide DDL-Operationen.
Die Ausführung von DDL-Anweisungen erfordert spezielle interne Mechanismen, einschließlich spezieller Sperren - und das ist auch gut so, denn du möchtest wahrscheinlich nicht, dass sich Tabellen ändern, während deine Abfragen laufen! Diese speziellen Sperren werden in MySQL Metadaten-Sperren genannt. Wir geben einen detaillierten Überblick darüber, wie sie funktionieren, in "Metadaten-Sperren".
Beachte, dass alle DDL-Anweisungen, einschließlich derjenigen, die über Online-DDL ausgeführt werden, Metadaten-Sperren erfordern, um sie zu erhalten. In diesem Sinne sind Online-DDL-Anweisungen nicht so "online", aber sie sperren die Zieltabelle nicht vollständig, während sie ausgeführt werden.
Die Ausführung von DDL-Anweisungen auf einem laufenden System unter Last ist ein riskantes Unterfangen: Selbst eine Anweisung, die fast sofort ausgeführt werden sollte, kann zu einem Chaos führen. Wir empfehlen, dass du dich in Kapitel 6 und im Link zur MySQL-Dokumentation sorgfältig über das Sperren von Metadaten informierst und verschiedene DDL-Anweisungen mit und ohne gleichzeitige Last ausführst. Das ist vielleicht nicht so wichtig, während du MySQL lernst, aber wir denken, dass es sich lohnt, dich im Voraus zu warnen. Nachdem das geklärt ist, kommen wir zurück zu unserer ALTER
der Tabelle language
.
Du kannst das Ergebnis mit der Anweisung SHOW COLUMNS
überprüfen:
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | NO | | NULL |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 3 rows in set (0.01 sec)
Im vorherigen Beispiel haben wir die Anweisung ALTER TABLE
mit dem Schlüsselwort RENAME COLUMN
verwendet. Das ist eine Funktion von MySQL 8.0. Aus Kompatibilitätsgründen könnten wir alternativ ALTER TABLE
mit dem Schlüsselwort CHANGE
verwenden:
mysql
>
ALTER
TABLE
language
CHANGE
last_update
last_updated_time
TIMESTAMP
-
>
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
;
Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
In diesem Beispiel kannst du sehen, dass wir der Anweisung ALTER TABLE
mit dem Schlüsselwort CHANGE
vier Parameter übergeben haben:
-
Der Name der Tabelle,
language
-
Der ursprüngliche Spaltenname,
last_update
-
Der neue Spaltenname,
last_updated_time
-
Der Spaltentyp,
TIMESTAMP
, mit vielen zusätzlichen Attributen, die notwendig sind, um die ursprüngliche Definition nicht zu verändern
Du musst alle vier angeben, d.h. du musst den Typ und alle dazugehörigen Klauseln neu spezifizieren. Da wir in diesem Beispiel MySQL 8.0 mit den Standardeinstellungen verwenden, hat TIMESTAMP
keine expliziten Vorgaben mehr. Wie du siehst, ist die Verwendung von RENAME COLUMN
viel einfacher als CHANGE
.
Wenn du den Typ und die Klauseln einer Spalte ändern willst, aber nicht ihren Namen, kannst du das Schlüsselwort MODIFY
verwenden:
mysql
>
ALTER
TABLE
language
MODIFY
name
CHAR
(
20
)
DEFAULT
'n/a'
;
Query OK, 0 rows affected (0.14 sec) Records: 0 Duplicates: 0 Warnings: 0
Du kannst dies auch mit dem Schlüsselwort CHANGE
tun, aber indem du denselben Spaltennamen zweimal angibst:
mysql
>
ALTER
TABLE
language
CHANGE
name
name
CHAR
(
20
)
DEFAULT
'n/a'
;
Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
Sei vorsichtig, wenn du Typen änderst:
-
Verwende keine inkompatiblen Typen, denn du verlässt dich darauf, dass MySQL die Daten erfolgreich von einem Format in ein anderes umwandelt (wenn du zum Beispiel eine
INT
Spalte in eineDATETIME
Spalte umwandelst, wird sie wahrscheinlich nicht das tun, was du dir erhofft hast). -
Schneide die Daten nicht ab, es sei denn, das ist das, was du willst. Wenn du die Größe eines Typs reduzierst, werden die Werte an die neue Breite angepasst, und du kannst Daten verlieren.
Angenommen, du möchtest eine zusätzliche Spalte zu einer bestehenden Tabelle hinzufügen. So machst du es mit der Anweisung ALTER TABLE
:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
;
Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
Du musst das Schlüsselwort ADD
, den neuen Spaltennamen sowie den Spaltentyp und die Klauseln angeben. In diesem Beispiel wird die neue Spalte native_name
als letzte Spalte der Tabelle hinzugefügt, wie in der Anweisung SHOW COLUMNS
gezeigt:
mysql
>
SHOW
COLUMNS
FROM
artist
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... | native_name | char(20) | YES | | NULL |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.00 sec)
Wenn du möchtest, dass sie stattdessen die erste Spalte ist, verwende das Schlüsselwort FIRST
wie folgt:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
FIRST
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | native_name | char(20) | YES | | NULL |... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.01 sec)
Wenn du willst, dass er an einer bestimmten Stelle eingefügt wird, benutze das Schlüsselwort AFTER
:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
AFTER
name
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | native_name | char(20) | YES | | NULL |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.00 sec)
Um eine Spalte zu entfernen, verwendest du das Schlüsselwort DROP
, gefolgt von dem Spaltennamen. So wirst du die neu hinzugefügte Spalte native_name
wieder los:
mysql
>
ALTER
TABLE
language
DROP
native_name
;
Query OK, 0 rows affected (0.07 sec) Records: 0 Duplicates: 0 Warnings: 0
Dabei werden sowohl die Spaltenstruktur als auch die in der Spalte enthaltenen Daten gelöscht. Außerdem wird die Spalte aus allen Indizes entfernt, in denen sie enthalten war; wenn sie die einzige Spalte in einem Index ist, wird auch der Index gelöscht. Du kannst eine Spalte nicht entfernen, wenn sie die einzige in einer Tabelle ist; dazu musst du stattdessen die Tabelle löschen, wie in "Strukturen löschen" erklärt . Sei vorsichtig, wenn du Spalten löschst, denn wenn sich die Struktur einer Tabelle ändert, musst du in der Regel auch alle INSERT
Anweisungen ändern, mit denen du Werte in einer bestimmten Reihenfolge einfügst. Mehr dazu erfährst du unter "Die INSERT-Anweisung".
MySQL erlaubt es dir, mehrere Änderungen in einer einzigen ALTER TABLE
Anweisung anzugeben, indem du sie mit Kommas trennst. Hier ist ein Beispiel, das eine neue Spalte hinzufügt und eine andere anpasst:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
255
)
,
MODIFY
name
CHAR
(
255
)
;
Query OK, 6 rows affected (0.06 sec) Records: 6 Duplicates: 0 Warnings: 0
Beachte, dass du dieses Mal siehst, dass sechs Datensätze geändert wurden. Bei den vorherigen ALTER TABLE
Befehlen meldete MySQL, dass keine Zeilen betroffen waren. Der Unterschied besteht darin, dass wir dieses Mal keine Online-DDL-Operation durchführen, denn das Ändern des Typs einer Spalte führt immer dazu, dass eine Tabelle neu erstellt wird. Wir empfehlen, die Online-DDL-Operationen im Referenzhandbuch zu lesen, wenn du deine Änderungen planst. Wenn du Online- und Offline-Operationen kombinierst, wird eine Offline-Operation durchgeführt.
Wenn keine Online-DDL verwendet wird oder eine der Änderungen "offline" ist, ist es sehr effizient, mehrere Änderungen in einem einzigen Vorgang zusammenzuführen. Das spart potenziell die Kosten für die Erstellung einer neuen Tabelle, das Kopieren von Daten aus der alten Tabelle in die neue Tabelle, das Löschen der alten Tabelle und die Umbenennung der neuen Tabelle in den Namen der alten Tabelle für jede einzelne Änderung.
Hinzufügen, Entfernen und Ändern von Indizes
Wie wir bereits besprochen haben, ist es oft schwer zu wissen, welche Indizes nützlich sind, bevor die Anwendung, die du erstellst, genutzt wird. Vielleicht stellst du fest, dass eine bestimmte Funktion der Anwendung viel beliebter ist, als du erwartet hast, und du musst herausfinden, wie du die Leistung für die entsprechenden Abfragen verbessern kannst. Deshalb ist es nützlich, wenn du Indizes auch nach dem Einsatz deiner Anwendung hinzufügen, ändern und entfernen kannst. Dieser Abschnitt zeigt dir, wie das geht. Beachte, dass das Ändern von Indizes keine Auswirkungen auf die in einer Tabelle gespeicherten Daten hat.
Wir beginnen mit dem Hinzufügen eines neuen Indexes. Stell dir vor, dass die Tabelle language
häufig mit einer WHERE
Klausel abgefragt wird, die die name
angibt. Um diese Abfragen zu beschleunigen, hast du beschlossen, einen neuen Index hinzuzufügen, den du idx_name
nennst. So fügst du ihn hinzu, nachdem die Tabelle erstellt wurde:
mysql
>
ALTER
TABLE
language
ADD
INDEX
idx_name
(
name
)
;
Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0
Auch hier kannst du die Begriffe KEY
und INDEX
synonym verwenden. Du kannst die Ergebnisse mit der Anweisung SHOW CREATE TABLE
überprüfen:
mysql
>
SHOW
CREATE
TABLE
language
\
G
*************************** 1. row *************************** Table: language Create Table: CREATE TABLE `language` ( `language_id` tinyint unsigned NOT NULL AUTO_INCREMENT, `name` char(255) DEFAULT NULL, `last_updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`language_id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Wie erwartet, ist der neue Index Teil der Tabellenstruktur. Du kannst auch einen Primärschlüssel für eine Tabelle angeben, nachdem sie erstellt wurde:
mysql
>
CREATE
TABLE
no_pk
(
id
INT
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
no_pk
VALUES
(
1
)
,
(
2
)
,
(
3
)
;
Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0
mysql
>
ALTER
TABLE
no_pk
ADD
PRIMARY
KEY
(
id
)
;
Query OK, 0 rows affected (0.13 sec) Records: 0 Duplicates: 0 Warnings: 0
Nun wollen wir uns ansehen, wie man einen Index entfernt. Um einen Nicht-Primärschlüssel-Index zu entfernen, gehst du wie folgt vor:
mysql
>
ALTER
TABLE
language
DROP
INDEX
idx_name
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
Du kannst einen Primärschlüsselindex wie folgt löschen:
mysql
>
ALTER
TABLE
no_pk
DROP
PRIMARY
KEY
;
Query OK, 3 rows affected (0.07 sec) Records: 3 Duplicates: 0 Warnings: 0
MySQL lässt nicht zu, dass du mehrere Primärschlüssel in einer Tabelle hast. Wenn du den Primärschlüssel ändern willst, musst du den bestehenden Index entfernen, bevor du den neuen hinzufügst. Wir wissen jedoch, dass es möglich ist, DDL-Operationen zu gruppieren. Nimm dieses Beispiel:
mysql
>
ALTER
TABLE
language
DROP
PRIMARY
KEY
,
-
>
ADD
PRIMARY
KEY
(
language_id
,
name
)
;
Query OK, 0 rows affected (0.09 sec) Records: 0 Duplicates: 0 Warnings: 0
Du kannst einen einmal erstellten Index nicht mehr ändern. manchmal möchtest du ihn jedoch ändern, z. B. wenn du die Anzahl der indizierten Zeichen in einer Spalte verringern oder eine weitere Spalte zum Index hinzufügen möchtest. Die beste Methode dafür ist, den Index zu löschen und ihn dann mit den neuen Angaben neu zu erstellen. Angenommen, du möchtest, dass der Index idx_name
nur die ersten 10 Zeichen von artist_name
enthält. Gehe einfach wie folgt vor:
mysql
>
ALTER
TABLE
language
DROP
INDEX
idx_name
,
-
>
ADD
INDEX
idx_name
(
name
(
10
)
)
;
Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0
Umbenennen von Tabellen und Ändern anderer Strukturen
Wir haben gesehen, wie man Spalten und Indizes in einer Tabelle ändern kann; jetzt sehen wir uns an, wie man Tabellen selbst ändern kann. Es ist einfach, eine Tabelle umzubenennen. Angenommen, du möchtest language
in languages
umbenennen. Verwende dazu den folgenden Befehl:
mysql
>
ALTER
TABLE
language
RENAME
TO
languages
;
Query OK, 0 rows affected (0.04 sec)
Das Schlüsselwort TO
ist optional.
Es gibt noch einige andere Dinge, die du mit ALTER
Kontoauszügen machen kannst, z. B:
-
Ändere den Standardzeichensatz und die Sortierreihenfolge für eine Datenbank, eine Tabelle oder eine Spalte.
-
Verwalte und ändere Beschränkungen. Du kannst zum Beispiel Fremdschlüssel hinzufügen und entfernen.
-
Füge eine Partitionierung zu einer Tabelle hinzu oder ändere die aktuelle Partitionierungsdefinition.
-
Ändere die Speicher-Engine einer Tabelle.
Mehr über diese Operationen findest du im MySQL-Referenzhandbuch, in den Abschnitten über die ALTER DATABASE
und ALTER TABLE
Anweisungen. Eine alternative, kürzere Schreibweise für dieselbe Anweisung ist RENAME TABLE
:
mysql
>
RENAME
TABLE
languages
TO
language
;
Query OK, 0 rows affected (0.04 sec)
Eine Sache, die sich nicht ändern lässt, ist der Name einer bestimmten Datenbank. Wenn du jedoch die InnoDB-Engine verwendest, kannst du RENAME
verwenden, um Tabellen zwischenDatenbanken zu verschieben:
mysql
>
CREATE
DATABASE
sakila_new
;
Query OK, 1 row affected (0.05 sec)
mysql
>
RENAME
TABLE
sakila
.
language
TO
sakila_new
.
language
;
Query OK, 0 rows affected (0.05 sec)
mysql
>
USE
sakila
;
Database changed
mysql
>
SHOW
TABLES
LIKE
'lang%'
;
Empty set (0.00 sec)
mysql
>
USE
sakila_new
;
Database changed
mysql
>
SHOW
TABLES
LIKE
'lang%'
;
+------------------------------+ | Tables_in_sakila_new (lang%) | +------------------------------+ | language | +------------------------------+ 1 row in set (0.00 sec)
Löschen von Strukturen
Im vorherigen Abschnitt haben wir gezeigt, wie du Spalten und Zeilen aus einer Datenbank löschen kannst; jetzt beschreiben wir, wie du Datenbanken und Tabellen entfernen kannst.
Datenbanken fallen lassen
Eine Datenbank zu löschen, ist ganz einfach. So löschst du die Datenbank sakila
:
mysql
>
DROP
DATABASE
sakila
;
Query OK, 25 rows affected (0.16 sec)
Die Anzahl der Zeilen, die in der Antwort zurückgegeben wird, entspricht der Anzahl der entfernten Tabellen. Du solltest vorsichtig sein, wenn du eine Datenbank löschst, da alle Tabellen, Indizes und Spalten gelöscht werden, ebenso wie alle zugehörigen Dateien und Verzeichnisse auf der Festplatte, die MySQL zu ihrer Verwaltung verwendet.
Wenn eine Datenbank nicht existiert, führt der Versuch, sie zu löschen, dazu, dass MySQL einen Fehler meldet. Versuchen wir noch einmal, die Datenbank sakila
zu löschen:
mysql
>
DROP
DATABASE
sakila
;
ERROR 1008 (HY000): Can't drop database 'sakila'; database doesn't exist
Du kannst den Fehler vermeiden, was nützlich ist, wenn du die Anweisung in ein Skript einfügst, indem du die Phrase IF EXISTS
verwendest:
mysql
>
DROP
DATABASE
IF
EXISTS
sakila
;
Query OK, 0 rows affected, 1 warning (0.00 sec)
Du kannst sehen, dass eine Warnung gemeldet wird, da die Datenbank sakila
bereits gelöscht wurde.
Tische entfernen
Das Entfernen von Tabellen ist so einfach wie das Entfernen einer Datenbank. Erstellen und entfernen wir eine Tabelle aus der Datenbank sakila
:
mysql
>
CREATE
TABLE
temp
(
id
SERIAL
PRIMARY
KEY
)
;
Query OK, 0 rows affected (0.05 sec)
mysql
>
DROP
TABLE
temp
;
Query OK, 0 rows affected (0.03 sec)
Keine Sorge: Die Meldung auf 0 rows affected
ist irreführend. Du wirst feststellen, dass die Tabelle definitiv weg ist.
Du kannst die Phrase IF EXISTS
verwenden, um Fehler zu vermeiden. Lass uns noch einmal versuchen, die Tabelle temp
zu löschen:
mysql
>
DROP
TABLE
IF
EXISTS
temp
;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Wie üblich kannst du die Warnung mit der Anweisung SHOW WARNINGS
untersuchen:
mysql
>
SHOW
WARNINGS
;
+-------+------+-----------------------------+ | Level | Code | Message | +-------+------+-----------------------------+ | Note | 1051 | Unknown table 'sakila.temp' | +-------+------+-----------------------------+ 1 row in set (0.00 sec)
Du kannst mehr als eine Tabelle in einer einzigen Anweisung löschen, indem du die Tabellennamen mit Kommas trennst:
mysql
>
DROP
TABLE
IF
EXISTS
temp
,
temp1
,
temp2
;
Query OK, 0 rows affected, 3 warnings (0.00 sec)
In diesem Fall gibt es drei Warnungen, weil keine dieser Tabellen existiert.
Get MySQL lernen, 2. Auflage now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.