Kapitel 17. Partitionierung
Zurück |  | Weiter

## Kapitel 17. Partitionierung

In diesem Kapitel geht es um die in MySQL 5.1 implementierten Formen der Partitionierung. Eine Einführung in Partitionierung und Partitionierungskonzepte finden Sie in Abschnitt 17.1, „Überblick über die Partitionierung in MySQL“. MySQL 5.1 unterstützt mehrere Formen der Partitionierung, die in Abschnitt 17.2, „Partitionstypen“, beschrieben werden, sowie der Teilpartitionierung (auch als zusammengesetzte Partitionierung bezeichnet), die in Abschnitt 17.2.5, „Unterpartitionen“, beschrieben werden. Wie Partitionen in partitionierten Tabellen hinzugefügt, entfernt oder geändert werden, erfahren Sie in Abschnitt 17.3, „Partitionsverwaltung“. Tabellenwartungsbefehle für partitionierte Tabellen behandeln wir in Abschnitt 17.3.3, „Wartung von Partitionen“.

Wichtig: Partitionierte Tabellen, die mit den MySQL-Versionen vor 5.1.6 angelegt wurden, können von einem MySQL Server der Version 5.1.6 oder höher nicht gelesen werden. Außerdem kann die `INFORMATION_SCHEMA.TABLES`-Tabelle nicht benutzt werden, wenn solche Tabellen auf einem Server liegen, der mit der Version 5.1.6 oder höher betrieben wird. Wenn Sie partitionierte Tabellen haben, die mit MySQL 5.1.5 oder früher angelegt wurden, lesen Sie unbedingt die Zusatzinformationen und empfohlenen Workarounds in Abschnitt D.1.1, „Änderungen in Release 5.1.6 (Noch nicht veröffentlicht)“, nach, bevor Sie auf MySQL 5.1.6 oder höher aufrüsten.

Die Partitionierungsimplementierung in MySQL 5.1 befindet sich noch in der Entwicklung und ist noch nicht bereit für Produktionsumgebungen. Etwas Ähnliches gilt für den Inhalt dieses Kapitels: Manche der hier beschriebenen Features sind in Wirklichkeit noch gar nicht implementiert, und andere funktionieren noch nicht ganz so wie beschrieben (beispielsweise die Optionen `DATA DIRECTORY` und `INDEX DIRECTORY` für Partitionen, die noch unter dem Bug#13520 leiden). Wir haben versucht, diese Abweichungen im vorliegenden Kapitel kenntlich zu machen. Bitte schauen Sie in folgende Quellen hinein, bevor Sie uns Bugreports schicken:

- MySQL Partitioning Forum

  Dies ist das offizielle Diskussionsforum für alle, die sich für die Partitionierungstechnologie von MySQL interessieren oder damit arbeiten. Hier finden Sie Ankündigungen und Aktualisierungen von MySQL-Entwicklern und anderen. Das Forum wird von den Mitgliedern des Partitioning Development and Documentation-Teams geleitet.

- Partitioning Bug Reports

  Eine Liste aller Partitionierungs-Bugs, die an unser Bugs-System gemeldet wurden, unabhängig von Alter, Ernsthaftigkeit und aktuellem Status. Es ist möglich, diese Liste nach diversen Kritierien zu filtern. Eventuell können Sie auch auf die MySQL Bugs System Home Page gehen und nach Bugs fahnden, die für Sie von besonderem Interesse sind.

- Mikael Ronström's Blog

  Der MySQL Partitioning Architect und Lead Developer Mikael Ronström stellt hier häufig Artikel ein, die seine Arbeit mit MySQL Partitioning und MySQL Cluster betreffen.

- PlanetMySQL

  Eine News-Site über MySQL mit Blogs, die für jeden MySQL-Nutzer interessant sein dürften. Wir raten Ihnen, hier Links zu den Blogs der Nutzer herauszusuchen, die mit der Partitionierung in MySQL arbeiten, oder Ihr eigenes Blog eintragen zu lassen.

Die Alpha-Binaries von MySQL 5.1 stehen nun unter http://dev.mysql.com/downloads/mysql/5.1.html zum Herunterladen zur Verfügung. Die Quelle für die aktuellsten Bugfixes und neuen Features im Zusammenhang mit Partitionierung finden Sie allerdings in unserem BitKeeper-Repository. Um Partitionierung zu ermöglichen, müssen Sie den Server mit der Option `--with-partition` kompilieren. Weitere Informationen über den MySQL-Build finden Sie unter Abschnitt 2.8, „Installation der Quelldistribution“. Wenn Sie Probleme mit dem Kompilieren eines partitionierungsfähigen MySQL 5.1-Build haben, schauen Sie in das MySQL Partitioning Forum und fragen Sie dort um Hilfe, falls nicht bereits eine Lösung für Ihr Problem veröffentlicht wurde.

## 17.1. Überblick über die Partitionierung in MySQL

Dieser Abschnitt gibt einen Überblick über Partitionierungskonzepte in MySQL 5.1.

Beschränkungen der Partitionierung und die Grenzen des Features erfahren Sie unter Abschnitt 17.4, „Beschränkungen und Grenzen der Partitionierung“.

Der SQL-Standard enthält kaum Anleitungen in Bezug auf die physikalischen Aspekte der Datenspeicherung. Die Sprache SQL ist dafür ausgelegt, unabhängig von Datenstrukturen, Medien sowie den einem Schema zugrunde liegenden Tabellen, Zeilen oder Spalten zu funktionieren. Allerdings haben die meisten modernen Datenbankmanagementsysteme auch irgendwelche Möglichkeiten entwickelt, um festzustellen, an welchem Ort (Dateisystem, Hardware oder beides) bestimmte Daten physikalisch gespeichert werden. In MySQL unterstützte die Speicher-Engine `InnoDB` lange Zeit Tablespaces und der MySQL Server konnte schon vor der Einführung von Partitionierung so konfiguriert werden, dass er für die Speicherung unterschiedlicher Datenbanken verschiedene physikalische Verzeichnisse verwendete (unter Abschnitt 7.6.1, „Symbolische Verknüpfungen“, werden die Gründe dafür erklärt).

Mit der Partitionierung wird dieses Konzept noch einen Schritt weitergeführt: Hiermit können Sie verschiedene Teile einzelner Tabellen über ein Dateisystem verteilen, und zwar nach Regeln, die Sie im Großen und Ganzen nach Ihren Bedürfnissen festlegen. So werden verschiedene Teile einer Tabelle im Endeffekt als getrennte Tabellen an verschiedenen Stellen gespeichert. Die vom Benutzer gewählte Regel, nach welcher die Daten aufgeteilt werden, bezeichnet man als Partitionierungsfunktion. Diese kann in MySQL der Modulus sein, ein einfacher Vergleich mit einer Menge von Wertebereichen oder Wertelisten, oder auch eine interne oder lineare Hash-Funktion. Die Funktion wird je nach dem vom Benutzer angegebenen Partitionierungstyp ausgewählt und nimmt den Wert eines ebenfalls vom Benutzer gelieferten Ausdrucks als Parameter entgegen. Dieser Ausdruck kann der Wert einer Integer-Spalte sein oder auch eine Funktion, die auf einer oder mehreren Spalten arbeitet und einen Integer zurückgibt. Der Wert dieses Ausdrucks wird an die Partitionierungsfunktion übergeben, die ihrerseits die Nummer der Partition, in welcher dieser spezielle Datensatz gespeichert werden soll, als Integer zurückgibt. Diese Funktion muss nichtkonstant und nichtzufällig sein. Sie darf keine Anfragen enthalten, kann aber jedweden in MySQL zulässigen SQL-Ausdruck enthalten, wenn dieser nur einen positiven Integer kleiner `MAXVALUE` zurückgibt (dies ist der größtmögliche positive Integer). Beispiele für Partitionierungsfunktionen finden Sie in den Abschnitten über Partitionierungstypen weiter unten in diesem Kapitel (siehe Abschnitt 17.2, „Partitionstypen“) sowie in den Beschreibungen zur Partitionierungssyntax in Abschnitt 13.1.5, „`CREATE TABLE`“.

Dies bezeichnet man als horizontale Partitionierung: Verschiedene Zeilen einer Tabelle können unterschiedlichen physikalischen Partitionen zugewiesen werden. MySQL 5.1 kennt keine vertikale Partitionierung, bei der verschiedene Tabellenspalten auf verschiedene physikalische Partitionen gespeichert würden. Es gibt auch noch keinerlei Pläne, vertikale Partitionierung in MySQL 5.1 einzuführen.

Partitionierungsunterstützung ist in den `-max`-Releases von MySQL 5.1 enthalten (d. h., dass die 5.1-`-max`-Binaries mit der Option `--with-partition` erstellt werden). Wenn die MySQL-Binary mit Partitionierungsunterstützung gebaut wird, muss nichts weiter unternommen werden, um diese zu aktivieren (es sind beispielsweise keine speziellen Einträge in der `my.cnf`-Datei erforderlich). Ob Ihr MySQL Server Partitionierung unterstützt, verrät Ihnen der Befehl `SHOW VARIABLES`:

    mysql> SHOW VARIABLES LIKE '%partition%';

    +-------------------+-------+
    | Variable_name     | Value |
    +-------------------+-------+
    | have_partitioning | YES   |
    +-------------------+-------+
    1 row in set (0.00 sec)

Wenn Sie in der Ausgabe Ihres ensprechenden `SHOW VARIABLES`-Befehls nicht wie hier eine `have_partitioning`-Variable mit dem Wert `YES` zu sehen bekommen, dann unterstützt Ihre MySQL-Version keine Partitionierung.

Vor MySQL 5.1.6 hieß diese Variable `have_partition_engine` (Bug#16718).

Um partitionierte Tabellen zu erstellen, können Sie jede vom MySQL Server unterstützte Speicher-Engine einsetzen; die MySQL-Partitionierungs-Engine läuft in ihrer eigenen Schicht und kann mit allen diesen Speicher-Engines umgehen. In MySQL 5.1 müssen alle Partitionen derselben partitionierten Tabelle auch dieselbe Speicher-Engine benutzen. Sie können beispielsweise nicht für die eine Partition `MyISAM` und für die andere `InnoDB` benutzen. Allerdings hindert nichts Sie daran, verschiedene Speicher-Engines für verschiedene partitionierte Tabellen auf demselben MySQL Server oder sogar in derselben Datenbank zu benutzen.

Um eine bestimmte Speicher-Engine für eine partitionierte Tabelle zu verwenden, müssen Sie lediglich die passende `[STORAGE] ENGINE`-Option einstellen, wie Sie es auch bei einer nichtpartitionierten Tabelle tun würden. Allerdings müssen Sie daran denken, dass `[STORAGE] ENGINE` (und andere Tabellenoptionen) in einer `CREATE TABLE`-Anweisung vor den Partitionierungsoptionen stehen müssen. Das folgende Beispiel zeigt, wie man eine Tabelle anlegt, die per Hash in 6 Partitionen zerlegt wird und die Speicher-Engine `InnoDB` verwendet:

    CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)
        ENGINE=INNODB
        PARTITION BY HASH(MONTH(tr_date))
        PARTITIONS 6;

(Beachten Sie, dass jede `PARTITION`-Klausel auch eine `[STORAGE] ENGINE`-Option enthalten kann, die jedoch in MySQL 5.1 wirkungslos bleibt.)

Es ist durchaus möglich, auch partitionierte temporäre Tabellen zu erstellen, doch diese haben nur so lange wie die aktuelle MySQL-Session Bestand. Dasselbe gilt für nichtpartitionierte temporäre Tabellen.

Hinweis: Eine Partitionierung betrifft alle Daten und Indizes einer Tabelle. Sie können weder die Daten ohne ihre Indizes partitionieren noch die Indizes ohne die Daten, ebenso wenig, wie Sie nur einen Teil einer Tabelle partitionieren können.

Die Daten und Indizes jeder Partition können mit den Optionen `DATA DIRECTORY` und `INDEX DIRECTORY` der `PARTITION`-Klausel der `CREATE TABLE`-Anweisung, mit der die partitionierte Tabelle angelegt wird, einem bestimmten Verzeichnis zugewiesen werden. Überdies können Sie mit `MAX_ROWS` und `MIN_ROWS` festlegen, wie viele Zeilen höchstens bzw. mindestens in jeder Partition gespeichert werden dürfen. Genaueres über diese Optionen erfahren Sie unter Abschnitt 17.3, „Partitionsverwaltung“. Hinweis: Dieses Feature läuft zurzeit wegen Bug#13250 nicht; dies dürfte jedoch behoben sein, wenn die ersten 5.1-Binaries zur Verfügung gestellt werden.

Eine Partitionierung hat folgende Vorteile:

- In einer einzigen Tabelle können mehr Daten gespeichert werden, als auf eine einzelne Festplatte oder Dateisystempartition passen.

- Unnütz gewordene Daten lassen sich oft einfacher aus der Tabelle entfernen, wenn man nur eine Partition löschen muss, die ebendiese Daten enthält. Umgekehrt lassen sich in einigen Fällen Daten auch einfacher hinzufügen, indem man einfach eine neue Partition speziell für diese Daten erschafft.

Normalerweise bietet eine Partitionierung auch noch die in der folgenden Liste aufgeführten Vorteile. Diese Features sind zwar in MySQL Partitioning noch nicht implementiert, stehen aber ganz oben auf unserer Prioritätenliste. Wir hoffen, sie im Produktionsrelease der Version 5.1 bereits einbringen zu können.

- Manche Anfragen lassen sich dadurch optimieren, dass die Daten, die auf eine `WHERE`-Klausel zutreffen, auf bestimmten Partitionen gespeichert werden können, wodurch die restlichen Partitionen von der Suche ausgeschlossen bleiben. Da Partitionen nach der Erstellung einer partitionierten Tabelle geändert werden können, sind Sie in der Lage, Ihre Daten so zu reorganisieren, dass häufige Anfragen schneller verarbeitet werden, als es im ursprünglichen Partitionierungsschema der Fall war.

- Anfragen mit Aggregatfunktionen wie `SUM()` und `COUNT()` lassen sich leicht parallelisieren. Ein einfaches Beispiel einer solchen Anfrage wäre `SELECT salesperson_id, COUNT(orders) as order_total FROM sales GROUP BY salesperson_id;`. Mit „parallelisieren“ ist gemeint, dass die Anfrage auf allen Partitionen gleichzeitig ausgeführt werden kann und das Endergebnis dann die Summe der Resultate der einzelnen Partitionen ist.

- Da Suchoperationen auf mehrere Festplatten verteilt werden können, wird ein größerer Durchsatz an Anfragen erzielt.

Bitte schauen Sie regelmäßig im englischsprachingen Handbuch, Kapitel „Partitions“, nach aktuellen Entwicklungen in der Implementierung der Partitionierung in MySQL 5.1, da diese Entwicklung noch nicht abgeschlossen ist.

## 17.2. Partitionstypen

Dieser Abschnitt beschreibt die in MySQL 5.1 verfügbaren Arten der Partitionierung, nämlich:

- `RANGE`-Partitionierung (Bereichspartitionierung): Weist den Partitionen Zeilen zu, je nachdem, ob ihre Spaltenwerte in einen bestimmten Wertebereich fallen. Siehe Abschnitt 17.2.1, „`RANGE`-Partitionierung“.

- `LIST`-Partitionierung (Listenpartitionierung): Ähnelt der Bereichspartitionierung, nur dass hier die Partition anhand der Frage ausgewählt wird, ob sich die Spaltenwerte in einer Menge eigenständiger Werte wiederfinden. Siehe Abschnitt 17.2.2, „`LIST`-Partitionierung“.

- `HASH`-Partitionierung: Hierbei wird eine Partition anhand des Rückgabewerts eines benutzerdefinierten Ausdrucks ausgewählt, der auf Spaltenwerten der Zeilen operiert, die in die Tabelle eingefügt werden sollen. Die Funktion kann jeden in MySQL zulässigen Ausdruck enthalten, der einen nichtnegativen Integer ergibt. Siehe Abschnitt 17.2.3, „`HASH`-Partitionierung“.

- `KEY`-Partitionierung (Schlüsselpartitionierung): Ähnelt der Hash-Partitionierung, aber mit dem Unterschied, dass nur bestimmte auszuwertende Spalten übergeben werden und der MySQL Server seine eigene Hash-Funktion liefert. Die Spalte(n) dürfen nur Integer-Werte enthalten. Siehe Abschnitt 17.2.4, „`KEY`-Partitionierung“.

Bitte vergessen Sie nicht: Egal welche Art von Partitionierung Sie verwenden, Partitionen werden immer automatisch der Reihe nach bei ihrer Erstellung durchnummeriert, und zwar beginnend mit `0`. Wenn eine neue Zeile in eine partitionierte Tabelle eingefügt wird, wird die richtige Partition anhand dieser laufenden Nummern gefunden. Wenn beispielsweise Ihre Tabellen 4 Partitionen nutzen, so habe diese Partitionen die Nummern `0`, `1`, `2` und `3`. Bei einer `RANGE`- oder `LIST`-Partitionierung müssen Sie gewährleisten, dass für jede Partitionsnummer auch eine Partition definiert ist. Bei einer `HASH`-Partitionierung muss die verwendete benutzerdefinierte Funktion einen Integer größer `0` zurückgeben. Bei einer `KEY`-Partitionierung wird dieses Problem automatisch von der Hash-Funktion gelöst, die der MySQL Server intern einsetzt.

Partitionsnamen halten sich generell an dieselben Regeln, die auch für andere MySQL-Bezeichner gelten, wie beispielsweise die für Tabellen und Datenbanken. Allerdings müssen Sie daran denken, dass Partitionsnamen nicht zwischen Groß- und Kleinschreibung unterscheiden. So würde beispielsweise die folgende `CREATE TABLE`-Anweisung scheitern:

    mysql> CREATE TABLE t2 (val INT)
        -> PARTITION BY LIST(val)(
        ->     PARTITION mypart VALUES IN (1,3,5),
        ->     PARTITION MyPart VALUES IN (2,4,6)
        -> );
    ERROR 1488 (HY000): All partitions must have unique names in the table

Der Fehler liegt daran, dass MySQL keinen Unterschied zwischen den Partitionsnamen `mypart` und `MyPart` erkennen kann.

In den folgenden Abschnitten geben wir nicht immer alle nur denkbaren Syntaxvarianten zur Erstellung der Partitionstypen an. Diese Informationen können Sie unter Abschnitt 13.1.5, „`CREATE TABLE`“, nachschlagen.

### 17.2.1. `RANGE`-Partitionierung

Wenn eine Tabelle nach Wertebereichen partitioniert wird, enthält später jede Partition die Zeilen, für die der Partitionierungsausdruck einen Wert hat, der in einem bestimmten Wertebereich liegt. Die Wertebereiche sollten aneinander grenzen, aber sich nicht überschneiden, und sie sollten mit dem `VALUES LESS THAN`-Operator definiert werden. Bei den nächsten Beispielen gehen wir davon aus, dass wie unten beschrieben eine Tabelle für die Personaldaten einer Kette von 20 Videotheken (mit den Nummern 1 bis 20) eingerichtet wird:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT NOT NULL,
        store_id INT NOT NULL
    );

Diese Tabelle kann je nach Bedarf auf unterschiedliche Weise nach Bereichen partitioniert werden. Eine Möglichkeit wäre es, die `store_id`-Spalte zu verwenden. Sie könnten die Tabelle beispielsweise mit einer `PARTITION BY RANGE`-Klausel auf 4 Partitionen verteilen:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT NOT NULL,
        store_id INT NOT NULL
    )
    PARTITION BY RANGE (store_id) (
        PARTITION p0 VALUES LESS THAN (6),
        PARTITION p1 VALUES LESS THAN (11),
        PARTITION p2 VALUES LESS THAN (16),
        PARTITION p3 VALUES LESS THAN (21)
    );

In diesem Partitionierungsschema werden die Angestelltendaten der Zweigstellen 1 bis 5 in Partition `p0` gespeichert, die Angestelltendaten der Zweigstellen 6 bis 10 in Partition `p1` und so weiter. Beachten Sie, dass die Partitionen der Reihe nach von der niedrigsten bis zur höchsten Nummer definiert werden. Dies verlangt die Syntax von `PARTITION BY RANGE`, die in dieser Hinsicht einer `switch ... case`-Anweisung in C oder Java ähnelt.

Es ist einfach, festzustellen, dass eine neue Zeile mit den Daten `(72, 'Michael', 'Widenius', '1998-06-25', NULL, 13)` in die Partition `p2` eingefügt wurde, doch was geschieht, wenn die Videothekenkette eine 21. Zweigstelle eröffnen möchte? In dem vorliegenden Schema gibt es keine Regeln für Zeilen mit einer `store_id` größer 20. Daher wird ein Fehler gemeldet, weil der Server nicht weiß, wohin damit. Dieses Verhalten können Sie mit einem so genannten „Catchall“ verhindern: einer `VALUES LESS THAN`-Klausel in der `CREATE TABLE`-Anweisung, die für alle Werte Vorsorge trifft, die den größten explizit angegebenen Wert übersteigen:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT NOT NULL,
        store_id INT NOT NULL
    )
    PARTITION BY RANGE (store_id) (
        PARTITION p0 VALUES LESS THAN (6),
        PARTITION p1 VALUES LESS THAN (11),
        PARTITION p2 VALUES LESS THAN (16),
        PARTITION p3 VALUES LESS THAN MAXVALUE
    );

`MAXVALUE` ist der größte mögliche Integer-Wert. Nun werden alle Zeilen, deren `store_id`-Spaltenwert größer oder gleich dem größten definierten Wert 16 ist, in Partition `p3` gespeichert. Irgendwann einmal, wenn die Anzahl der Zweigstellen auf 25, 30 oder mehr angewachsen ist, können Sie mit einer `ALTER TABLE`-Anweisung neue Partitionen für die Zweigstellen 21 bis 25, 26 bis 30 und so weiter hinzufügen (Einzelheiten zur Vorgehensweise finden Sie unter Abschnitt 17.3, „Partitionsverwaltung“.)

In ähnlicher Weise können Sie die Tabelle auf der Grundlage von Job-Codes partitionieren, d. h. anhand der Werte der Spalte `job_code`. Nehmen wir beispielsweise an, reguläre (in der Filiale arbeitende) Angestellte haben einen zweistelligen Job-Code, Büro- und Support-Mitarbeiter einen dreistelligen und Manager einen vierstelligen. Dann könnten Sie Ihre partitionierte Tabelle folgendermaßen definieren:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT NOT NULL,
        store_id INT NOT NULL
    )
    PARTITION BY RANGE (job_code) (
        PARTITION p0 VALUES LESS THAN (100),
        PARTITION p1 VALUES LESS THAN (1000),
        PARTITION p2 VALUES LESS THAN (10000)
    );

In diesem Beispiel würden die Daten der Filialmitarbeiter in der Partition `p0`, die der Büro- und Support-Mitarbeiter in der Partition `p1` und die der Manager in der Partition `p2` gespeichert.

In `VALUES LESS THAN`-Klauseln kann auch ein Ausdruck verwendet werden, allerdings nur mit der Maßgabe, dass MySQL in der Lage sein muss, den Rückgabewert dieses Ausdrucks in einem `LESS THAN` (`<`)-Vergleich auszuwerten; der Wert des Ausdrucks darf also nicht `NULL` sein. Dies ist der Grund, weshalb die Spalten `hired`, `separated`, `job_code` und `store_id` der Tabelle `employees` als `NOT NULL` definiert wurden.

Anstatt die Tabellendaten anhand der Zweigstellennummer zu verteilen, können Sie auch einen Ausdruck verwenden, der auf den beiden `DATE`-Spalten basiert. Nehmen wir beispielsweise an, Sie möchten die Tabelle nach dem Jahr partitionieren, in welchem die Mitarbeiter das Unternehmen verlassen, also nach dem Wert der Spalte `YEAR(separated)`. Folgende `CREATE TABLE`-Anweisung würde ein solches Partitionierungsschema implementieren:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY RANGE ( YEAR(separated) ) (
        PARTITION p0 VALUES LESS THAN (1991),
        PARTITION p1 VALUES LESS THAN (1996),
        PARTITION p2 VALUES LESS THAN (2001),
        PARTITION p3 VALUES LESS THAN MAXVALUE
    );

In diesem Schema werden die Daten der Mitarbeiter, die das Unternehmen vor 1991 verließen, in der Partition `p0` gespeichert, die Daten derjenigen, die zwischen 1991 und 1995 gingen, in Partition `p1`, die Daten derjenigen, die zwischen 1996 und 2000 gingen, in Partition `p2` und die Daten derjenigen, die nach 2000 gingen, in Partition `p3`.

Eine Bereichspartitionierung ist in folgenden Fällen besonders nützlich:

- Sie möchten oder müssen „alte“ Daten löschen. Wenn Sie das soeben gezeigte Partitionierungsschema umsetzen, brauchen Sie nur noch `ALTER TABLE employees DROP PARTITION p0;` aufzurufen, um alle Datensätze der Angestellten zu löschen, die vor 1991 das Unternehmen verlassen haben. (Weitere Informationen finden Sie unter Abschnitt 13.1.2, „`ALTER TABLE`“, und Abschnitt 17.3, „Partitionsverwaltung“.) Wenn Sie eine Tabelle mit sehr vielen Zeilen haben, kann diese Vorgehensweise sehr viel effizienter als eine `DELETE`-Anfrage wie etwa `DELETE FROM employees WHERE YEAR(separated) <= 1990;` sein.

- Sie möchten eine Spalte benutzen, die Datums- oder Uhrzeitwerte oder Werte aus einer anderen Wertfolge enthält.

- Es werden oft Anfragen ausgeführt, die direkt von einer für die Partitionierung der Tabelle verwendeten Spalte abhängen. So kann MySQL beispielsweise bei einer Anfrage wie `SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BY store_id;` ganz schnell herausfinden, dass nur die Partition `p2` durchsucht werden muss, da die restlichen Partitionen gar keine zu der `WHERE`-Klausel passenden Einträge enthalten können. Hinweis: Dies Optimierung wurde in den Quelldateien von MySQL 5.1 zwar noch nicht aktiviert, aber wir arbeiten daran.

### 17.2.2. `LIST`-Partitionierung

Die Listenpartitionierung in MySQL ähnelt in vieler Hinsicht der Bereichspartitionierung. Wie bei dieser muss jede Partition explizit definiert werden. Der Hauptunterschied besteht darin, dass bei einer Listenpartitionierung die einzelnen Partitionen anhand der Frage gebildet werden, ob ein Spaltenwert in einer von mehreren Wertelisten vorkommt, während bei der Bereichspartitionierung gefragt wird, ob er in einer von mehreren Wertefolgen vorkommt. Diese Form der Partitionierung nehmen Sie mit `PARTITION BY LIST(`expr`)` vor, wobei `expr` ein Spaltenwert oder ein auf einem Spaltenwert basierender Ausdruck ist, der einen Integer zurückgibt. Die einzelnen Partitionen werden sodann durch `VALUES IN (`value_list`)` definiert, wobei `value_list` eine kommagetrennte Liste von Integern ist.

Hinweis: In MySQL 5.1 kann der Spaltenwert bei einer `LIST`-Partitionierung nur mit einer Integer-Liste verglichen werden.

Im Gegensatz zu Bereichspartitionen müssen Listenpartitionen nicht in einer bestimmten Reihenfolge definiert werden. Genauere Hinweise zur Syntax finden Sie unter Abschnitt 13.1.5, „`CREATE TABLE`“.

In den nachfolgenden Beispielen gehen wir davon aus, dass die Grunddefinition der zu partitionierenden Tabelle die der nachfolgenden `CREATE TABLE`-Anweisung ist:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    );

(Dies ist dieselbe Tabelle, die auch als Grundlage der Beispiele in Abschnitt 17.2.1, „`RANGE`-Partitionierung“, diente.)

Angenommen, wir haben eine Kette von 20 Videotheken, die auf 4 Franchisenehmer verteilt ist, wie in der folgenden Tabelle gezeigt:

Region | Store ID Numbers
North | 3, 5, 6, 9, 17
East | 1, 2, 10, 11, 19, 20
West | 4, 12, 13, 14, 18
Central | 7, 8, 15, 16

Um diese Tabelle so zu partitionieren, dass jeweils die Zweigstellen einer Region zusammenhängend gespeichert werden, könnten Sie die folgende `CREATE TABLE`-Anweisung einsetzen:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY LIST(store_id) (
        PARTITION pNorth VALUES IN (3,5,6,9,17),
        PARTITION pEast VALUES IN (1,2,10,11,19,20),
        PARTITION pWest VALUES IN (4,12,13,14,18),
        PARTITION pCentral VALUES IN (7,8,15,16)
    );

Nun ist es ganz einfach, regionale Angestelltendaten in die Tabelle zu laden oder aus ihr zu löschen. Nehmen wir beispielsweise an, alle Zweigstellen der Region West werden an ein anderes Unternehmen verkauft. Dann könnten alle Angestelltendaten der Zweigstellen dieser Region mit der Anfrage `ALTER TABLE employees DROP PARTITION pWest;` gelöscht werden, die viel schneller als die entsprechende `DELETE`-Anfrage `DELETE FROM employees WHERE store_id IN (4,12,13,14,18);` ausgeführt wird.

Wichtig: Wenn Sie versuchen, eine Zeile einzufügen, deren Spaltenwert (oder Rückgabewert für den Partitionierungsausdruck) in den Listen mit den Partitionierungswerten nicht vorkommt, scheitert die `INSERT`-Anfrage mit einer Fehlermeldung. So würde beispielsweise die folgende Anfrage bei dem oben skizzierten Schema einer `LIST`-Partitionierung fehlschlagen:

    INSERT INTO employees VALUES
        (224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 21);

Dieser Fehler tritt ein, da der `store_id`-Spaltenwert `21` in keiner der Wertelisten auftritt, die zur Definition der Partitionen `pNorth`, `pEast`, `pWest` und `pCentral` angegeben wurden. Es ist wichtig zu wissen, dass für Listenpartitionen keine „Catchall“-Definition wie `VALUES LESS THAN MAXVALUE` existiert, um Werte unterzubringen, die in keiner der Wertelisten auftauchen. Mit anderen Worten: Jeder Partitionierungswert muss in einer der Wertelisten vorhanden sein.

Wie die `RANGE`-Partitionierung kann auch die `LIST`-Partitionierung mit einer Hash- oder Schlüsselpartitionierung kombiniert werden, um eine zusammengesetzte Partitionierung (Teilpartitionierung) zu bilden. Siehe Abschnitt 17.2.5, „Unterpartitionen“.

### 17.2.3. `HASH`-Partitionierung

Eine Partitionierung nach `HASH` wird hauptsächlich eingesetzt, um eine gleichmäßige Verteilung der Daten auf eine im Voraus festgelegte Anzahl von Partitionen zu erzielen. Bei einer Bereichs- oder Listenpartitionierung müssen Sie explizit angeben, in welcher Partition Spaltenwerte gespeichert werden sollen, während MySQL Ihnen dies bei einer Hash-Partitionierung abnimmt. Hier müssen Sie lediglich einen Spaltenwert oder einen auf einem Spaltenwert basierenden Ausdruck für den Hash angeben und sagen, auf wie viele Partitionen die partitionierte Tabelle verteilt werden soll.

Um eine Tabelle mit einer `HASH`-Partitionierung aufzuteilen, müssen Sie an die `CREATE TABLE`-Anweisung eine `PARTITION BY HASH (`expr`)`-Klausel anfügen, wobei `expr` ein Ausdruck ist, der einen Integer zurückgibt. Das kann auch einfach der Name einer Spalte sein, die einen der Integer-Typen von MySQL hat. Zusätzlich wird normalerweise noch eine `PARTITIONS `num``-Klausel angefügt, wobei `num` ein nichtnegativer Integer ist und angibt, auf wie viele Partitionen die Tabelle verteilt werden soll.

Die folgende Anweisung erzeugt beispielsweise eine Tabelle, die einen Hash der Spalte `store_id` verwendet und auf 4 Partitionen verteilt wird:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY HASH(store_id)
    PARTITIONS 4;

Wenn Sie keine `PARTITIONS`-Klausel verwenden, ist die Anzahl der Partitionen nach Voreinstellung `1`. Ausnahme: Bei `NDB Cluster`-Tabellen ist die vorgegebene Anzahl der Partitionen gleich der Anzahl der Datenknoten im Cluster, eventuell berichtigt um eine `MAX_ROWS`-Einstellung, um zu gewährleisten, dass alle Zeilen in die Partitionen hineinpassen. (Siehe Kapitel 16, MySQL Cluster.)

Wenn Sie das Schlüsselwort `PARTITIONS` ohne darauf folgende Zahlenangabe verwenden, wird ein Syntaxfehler ausgelöst.

Sie können für `expr` auch einen SQL-Ausdruck einsetzen, der einen Integer zurückliefert. Wenn Sie Ihre Tabelle beispielsweise anhand des Einstellungsjahres der Angestellten partitionieren möchten, gehen Sie folgendermaßen vor:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY HASH( YEAR(hired) )
    PARTITIONS 4;

Sie können für `expr` alle Funktionen und Ausdrücke verwenden, die in MySQL zulässig sind, vorausgesetzt, der Rückgabewert ist ein nichtkonstanter, nichtzufälliger Integer. (Mit anderen Worten: Der Wert soll variieren, aber deterministisch sein.) Sie müssen allerdings daran denken, dass dieser Ausdruck jedes Mal, wenn eine Zeile eingefügt, aktualisiert oder eventuell auch gelöscht wird, ausgewertet werden muss. Das bedeutet, dass komplexe Ausdrücke die Leistung beeinträchtigen können, insbesondere, wenn Operationen ausgeführt werden, die viele Zeilen auf einmal betreffen (wie etwa Masseneinfügungen).

Am effizientesten sind Hash-Funktionen, die nur eine einzige Tabellenspalte bearbeiten und deren Wert mit dem Spaltenwert konsistent zu- oder abnimmt, da hierdurch ein „Pruning“ von Partitionsbereichen möglich wird. D. h.: Je enger sich der Wert des Ausdrucks an dem Wert der zugrunde liegenden Spalte orientiert, umso effizienter kann MySQL ihn für die Hash-Partitionierung einsetzen.

Wenn beispielsweise `date_col` eine Spalte vom Typ `DATE` ist, dann ändert sich der Ausdruck `TO_DAYS(date_col)` unmittelbar mit dem Wert von `date_col`, da jede Änderung des Werts von `date_col` auch den Wert des Ausdrucks ändert, und zwar in völlig konsistenter Form. Der Ausdruck `YEAR(date_col)` ändert sich bei `date_col`-Änderungen nicht ganz so unmittelbar wie `TO_DAYS(date_col)`, da nicht jede Änderung in `date_col` eine entsprechende Änderung in `YEAR(date_col)` nach sich zieht. Dennoch ist auch `YEAR(date_col)` ein guter Kandidat für eine Hash-Funktion, da es sich direkt mit einem Teil von `date_col` ändert und nicht die Gefahr besteht, dass eine Änderung von `date_col` zu einer unverhältnismäßigen Änderung von `YEAR(date_col)` führt.

Nehmen wir dagegen an, Sie hätten eine Spalte namens `int_col` vom Typ `INT`. Nun betrachten Sie den Ausdruck `POW(5-int_col,3) + 6`. Dieser wäre ganz schlecht als Hash-Funktion geeignet, da nicht garantiert ist, dass Änderungen von `int_col` proportionale Änderungen im Wert des Ausdrucks nach sich ziehen. Wenn Sie den Wert von `int_col` um einen gegebenen Betrag ändern, können dadurch ganz unterschiedliche Änderungen im Wert des Ausdrucks eintreten. Ändern Sie beispielsweise `int_col` von `5` in `6`, so ändert sich der Wert des Ausdrucks um `-1`, ändern Sie dagegen `int_col` von `6` in `7`, so ändert sich der Wert des Ausdrucks um `-7`.

Mit anderen Worten: Je enger der Graph des Spaltenwerts im Verhältnis zum Wert des Ausdrucks einer geraden Linie folgt, wie sie durch die Gleichung `y=`n`x` vorgegeben ist, wenn `n` eine von null verschiedene Konstante ist, umso besser eignet sich der Ausdruck für das Hashing. Denn je weniger linear ein Ausdruck ist, umso ungleichmäßiger werden die Daten auf die Partitionen verteilt, die dieser Ausdruck anlegt.

Theoretisch ist bei Ausdrücken, an denen mehrere Spaltenwerte beteiligt sind, auch Pruning möglich, aber es kann schwierig und langwierig sein, herauszufinden, welche dieser Ausdrücke nun wirklich für Pruning geeignet sind. Daher sind Hashing-Ausdrücke mit mehreren Spalten nicht sonderlich zu empfehlen.

Wenn `PARTITION BY HASH` verwendet wird, ermittelt MySQL anhand des Modulus des Ergebnisses der Benutzerfunktion, welche von `num` Partitionen verwendet wird. Anders ausgedrückt: Für einen Ausdruck `expr` wird der Datensatz in der Partition `N` gespeichert, wobei ``N` = MOD(`expr`, `num`)` ist. Nehmen wir beispielsweise an, Tabelle `t1` ist folgendermaßen definiert, sodass sie 4 Partitionen hat:

    CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
        PARTITION BY HASH( YEAR(col3) )
        PARTITIONS 4;

Wenn Sie in `t1` einen Datensatz einfügen, dessen `col3`-Wert `'2005-09-15'` ist, dann wird die Partition, in der er gespeichert wird, folgendermaßen ermittelt:

    MOD(YEAR('2005-09-01'),4)
    =  MOD(2005,4)
    =  1

MySQL 5.1 unterstützt auch eine Variante der `HASH`-Partitionierung namens lineares Hashing, die einen komplexen Algorithmus einsetzt, um neu in eine partitionierte Tabelle eingefügte Zeilen zu platzieren. Eine Beschreibung dieses Algorithmus finden Sie unter Abschnitt 17.2.3.1, „`LINEAR HASH`-Partitionierung“.

Die Benutzerfunktion wird bei jeder Einfügung oder Aktualisierung und unter Umständen auch bei einer Löschung eines Datensatzes ausgewertet.

Hinweis: Wenn die Tabelle, die partitioniert werden soll, einen `UNIQUE`-Schlüssel hat, müssen Spalten, die als Argumente an die `HASH`-Benutzerfunktion oder die `column_list` des `KEY`s übergeben werden, Teil dieses Schlüssels sein.

#### 17.2.3.1. `LINEAR HASH`-Partitionierung

MySQL unterstützt auch lineares Hashing, das sich vom regulären Hashing insofern unterscheidet, als es einen linearen Zweierpotenz-Algorithmus verwendet, während das reguläre Hashing den Modulus des Werts der Hashing-Funktion benutzt.

Der einzige syntaktische Unterschied zwischen der linearen und der regulären Hash-Partitionierung besteht darin, dass der `PARTITION BY`-Klausel das Schlüsselwort `LINEAR` hinzugefügt wird:

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY LINEAR HASH( YEAR(hired) )
    PARTITIONS 4;

Wenn Sie einen Ausdruck `expr` haben, wird beim linearen Hashing der Datensatz in Partition Nummer `N` von `num` Partitionen gespeichert, wobei `N` nach dem folgenden Algorithmus abgeleitet ist:

1. Finde die nächste Zweierpotenz größer `num`. Wir nennen diesen Wert `V`; er kann folgendermaßen berechnet werden:

       V = POWER(2, CEILING(LOG(2, num)))

   (Nehmen wir beispielsweise an, `num` sei 13. Dann ist `LOG(2,13)` gleich 3.7004397181411. `CEILING(3.7004397181411)` ist 4 und `V` = `POWER(2,4)`, was 16 ergibt.)

2. Setze `N` = `F`(`column_list`) & (`V` - 1).

3. Wobei `N` >= `num`:

   - Setze `V` = CEIL(`V` / 2)

   - Setze `N` = `N` & (`V` - 1)

Angenommen, die Tabelle `t1`, die lineare Hash-Partitionierung nutzt und 6 Partitionen hat, wird mit folgender Anweisung angelegt:

    CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
        PARTITION BY LINEAR HASH( YEAR(col3) )
        PARTITIONS 6;

Nehmen wir weiterhin an, Sie möchten in `t1` zwei Datensätze einfügen, in denen die Spalte `col3` die Werte `'2003-04-14'` und `'1998-10-19'` aufweist. Die Partitionsnummer für die erste dieser Spalten wird folgendermaßen ermittelt:

    V = POWER(2, CEILING( LOG(2,7) )) = 8
    N = YEAR('2003-04-14') & (8 - 1)
       = 2003 & 7
       = 3

    (3 >= 6 is FALSE: record stored in partition #3)

Die Partitionsnummer für den zweiten Eintrag wird berechnet mit:

    V = 8
    N = YEAR('1998-10-19') & (8-1)
      = 1998 & 7
      = 6

    (6 >= 6 is TRUE: additional step required)

    N = 6 & CEILING(5 / 2)
      = 6 & 3
      = 2

    (2 >= 6 is FALSE: record stored in partition #2)

Der Vorteil einer linearen Hash-Partitionierung besteht darin, dass sich Partitionen weit schneller hinzufügen, löschen, zusammenführen und aufspalten lassen. Das kann ein Segen sein, wenn man mit Tabellen arbeiten muss, die extrem große Datenmengen (Terabytes) enthalten. Der Nachteil ist, dass die Daten wahrscheinlich nicht so gleichmäßig auf die Partitionen verteilt werden wie bei der normalen Hash-Partitionierung.

### 17.2.4. `KEY`-Partitionierung

Die Partitionierung durch Schlüssel gleicht der Partitionierung durch Hash, nur dass dort, wo die Hash-Partitionierung einen benutzerdefinierten Ausdruck verwendet, die Schlüsselpartitionierung eine vom MySQL Server gelieferte Hash-Funktion einsetzt. MySQL Cluster verwendet zu diesem Zweck `MD5()`; für Tabellen, die andere Speicher-Engines benutzen, setzt der Server seine eigene interne Hash-Funktion ein, die auf demselben Algorithmus wie `PASSWORD()` beruht.

Die Syntaxregeln für `CREATE TABLE ... PARTITION BY KEY` sind dieselben wie bei der Erstellung einer Hash-partitionierten Tabelle. Die Hauptunterschiede sind:

- Statt `HASH` wird `KEY` eingesetzt.

- `KEY` nimmt nur eine Liste mit einem oder mehreren Spaltennamen entgegen. Seit MySQL 5.1.5 müssen die Spalten, die als Partitionierungsschlüssel eingesetzt werden, den Primärschlüssel der Tabelle (sofern sie einen hat) ganz oder teilweise abdecken.

  Seit MySQL 5.1.6 nimmt `KEY` eine Liste mit null oder mehr Spaltennamen entgegen. Wenn kein Spaltenname als Partitionierungsschlüssel angegeben ist, wird der Primärschlüssel der Tabelle verwendet. Die folgende `CREATE TABLE`-Anweisung gilt beispielsweise in MySQL 5.1.6 oder höher:

      CREATE TABLE k1 (
          id INT NOT NULL PRIMARY KEY,
          name VARCHAR(20)
      )
      PARTITION BY KEY()
      PARTITIONS 2;

  In diesem Fall ist der Partitionierungsschlüssel die Spalte `id`, auch wenn das in der Ausgabe von `SHOW CREATE TABLE` oder in der Spalte `PARTITION_EXPRESSION` der Tabelle `INFORMATION_SCHEMA.PARTITIONS` nicht erkennbar ist.

  Hinweis: Seit MySQL 5.1.6 werden außerdem Tabellen, die die Speicher-Engine `NDB Cluster` verwenden, implizit mit `KEY` partitioniert, wobei auch hier wieder der Primärschlüssel der Tabelle als Partitionierungsschlüssel dient. Nehmen wir als Beispiel die Tabelle, die mit folgender Anweisung erzeugt wird:

      CREATE TABLE kndb (
          id INT NOT NULL PRIMARY KEY,
          name VARCHAR(20) NOT NULL
      )
      ENGINE=NDBCLUSTER;

  Obwohl die Anweisung keine `PARTITION BY`-Klausel enthält, zeigt die Ausgabe von `SHOW CREATE TABLE kndb` Folgendes an:

      CREATE TABLE `kndb` (
          `id` int(11) NOT NULL,
          `name` varchar(20) NOT NULL.
          PRIMARY KEY  (`id`)
      )
      ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY ();

  Falls die Cluster-Tabelle keinen expliziten Primärschlüssel hat, wird der „verborgene“ Primärschlüssel, den die Speicher-Engine `NDB` automatisch für jede Cluster-Tabelle generiert, als Partitionierungsschlüssel eingesetzt.

  Wichtig: Auf einer schlüsselpartitionierten Tabelle, die eine andere Speicher-Engine als `NDB CLuster` verwendet, können Sie keine `ALTER TABLE DROP PRIMARY KEY`-Anweisung ausführen, ansonsten tritt der Fehler ERROR 1466 (HY000): Field in list of fields for partition function not found in table ein. Dieses Problem betrifft keine MySQL CLuster-Tabellen, die durch `KEY` partitioniert werden: In solchen Fällen wird die Tabelle reorganisiert, wobei der „verborgene“ Primärschlüssel als neuer Partitionierungsschlüssel der Tabelle verwendet wird. Siehe Kapitel 16, MySQL Cluster.

Es ist auch möglich, eine Tabelle durch linearen Schlüssel zu partitionieren. Hier sehen Sie ein einfaches Beispiel:

    CREATE TABLE tk (
        col1 INT NOT NULL,
        col2 CHAR(5),
        col3 DATE
    )
    PARTITION BY LINEAR KEY (col1)
    PARTITIONS 3;

Die Verwendung von `LINEAR` hat auf die `KEY`-Partitionierung denselben Effekt wie auf die `HASH`-Partitionierung, wobei die Partitionsnummer mit einem Zweierpotenz-Algorithmus anstatt mit Modulo-Arithmetik abgeleitet wird. Eine Beschreibung dieses Algorithmus und seiner Implikationen finden Sie unter Abschnitt 17.2.3.1, „`LINEAR HASH`-Partitionierung“.

### 17.2.5. Unterpartitionen

Teilpartitionierung, auch als zusammengesetzte Partitionierung bezeichnet, ist die weitere Unterteilung von Partitionen einer partitionierten Tabelle. Betrachten Sie als Beispiel die folgende `CREATE TABLE`-Anweisung:

    CREATE TABLE ts (id INT, purchased DATE)
        PARTITION BY RANGE(YEAR(purchased))
        SUBPARTITION BY HASH(TO_DAYS(purchased))
        SUBPARTITIONS 2 (
            PARTITION p0 VALUES LESS THAN (1990),
            PARTITION p1 VALUES LESS THAN (2000),
            PARTITION p2 VALUES LESS THAN MAXVALUE
        );

Die Tabelle `ts` hat 3 `RANGE`-Partitionen. Jede dieser Partitionen, also `p0`, `p1` und `p2`, ist ihrerseits in 2 Teilpartitionen unterteilt. Im Endeffekt ist die gesamte Tabelle auf `3 * 2 = 6` Partitionen verteilt. Durch die `PARTITION BY RANGE`-Klausel speichern allerdings die ersten beiden dieser Partitionen nur Datensätze, die in der `purchased`-Spalte einen Wert kleiner als 1990 aufweisen.

In MySQL 5.1 können Sie Tabellen, die durch `RANGE` oder `LIST` partitioniert werden, noch weiter aufteilen. Teilpartitionen können entweder die `HASH`- oder die `KEY`-Partitionierung verwenden. Dies bezeichnet man auch als zusammengesetzte Partitionierung.

Außerdem ist es möglich, Teilpartitionen explizit mit `SUBPARTITION`-Klauseln zu definieren, um Optionen für einzelne Teilpartitionen angeben zu können. So könnte man dieselbe `ts`-Tabelle wie im vorigen Beispiel auch wortreicher erzeugen:

    CREATE TABLE ts (id INT, purchased DATE)
        PARTITION BY RANGE( YEAR(purchased) )
        SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
            PARTITION p0 VALUES LESS THAN (1990) (
                SUBPARTITION s0,
                SUBPARTITION s1
            ),
            PARTITION p1 VALUES LESS THAN (2000) (
                SUBPARTITION s2,
                SUBPARTITION s3
            ),
            PARTITION p2 VALUES LESS THAN MAXVALUE (
                SUBPARTITION s4,
                SUBPARTITION s5
            )
        );

An dieser Syntax ist Folgendes bemerkenswert:

- Jede Partition muss dieselbe Anzahl Teilpartitionen haben.

- Wenn Sie mit `SUBPARTITION` explizit Teilpartitionen für eine Partition einer partitionierten Tabelle definieren, müssen Sie sie für alle anderen auch definieren. Die folgende Anweisung wird scheitern:

      CREATE TABLE ts (id INT, purchased DATE)
          PARTITION BY RANGE( YEAR(purchased) )
          SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
              PARTITION p0 VALUES LESS THAN (1990) (
                  SUBPARTITION s0,
                  SUBPARTITION s1
              ),
              PARTITION p1 VALUES LESS THAN (2000),
              PARTITION p2 VALUES LESS THAN MAXVALUE (
                  SUBPARTITION s2,
                  SUBPARTITION s3
              )
          );

  Die Anweisung würde sogar dann fehlschlagen, wenn sie eine `SUBPARTITIONS 2`-Klausel enthielte.

- Jede `SUBPARTITION`-Klausel muss (mindestens) den Namen für die Teilpartition enthalten. Ansonsten können Sie alle Optionen setzen, die Sie wünschen, oder aber die Teilpartition mit den Standardeinstellungen anlegen.

- Teilpartitionsnamen müssen innerhalb einer Partition eindeutig sein, aber nicht innerhalb der Tabelle als Ganzes. So ist beispielsweise die folgende `CREATE TABLE`-Anweisung zulässig:

      CREATE TABLE ts (id INT, purchased DATE)
          PARTITION BY RANGE( YEAR(purchased) )
          SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
              PARTITION p0 VALUES LESS THAN (1990) (
                  SUBPARTITION s0,
                  SUBPARTITION s1
              ),
              PARTITION p1 VALUES LESS THAN (2000) (
                  SUBPARTITION s0,
                  SUBPARTITION s1
              ),
              PARTITION p2 VALUES LESS THAN MAXVALUE (
                  SUBPARTITION s0,
                  SUBPARTITION s1
              )
          );

Teilpartitionen können bei extrem großen Tabellen helfen, die Daten und Indizes über viele Festplatten zu verteilen. Angenommen, Sie haben 6 Festplatten als `/disk0`, `/disk1`, `/disk2` und so weiter gemountet. Nun schauen Sie sich folgendes Beispiel an:

    CREATE TABLE ts (id INT, purchased DATE)
        PARTITION BY RANGE( YEAR(purchased) )
        SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
            PARTITION p0 VALUES LESS THAN (1990) (
                SUBPARTITION s0
                    DATA DIRECTORY = '/disk0/data'
                    INDEX DIRECTORY = '/disk0/idx',
                SUBPARTITION s1
                    DATA DIRECTORY = '/disk1/data'
                    INDEX DIRECTORY = '/disk1/idx'
            ),
            PARTITION p1 VALUES LESS THAN (2000) (
                SUBPARTITION s0
                    DATA DIRECTORY = '/disk2/data'
                    INDEX DIRECTORY = '/disk2/idx',
                SUBPARTITION s1
                    DATA DIRECTORY = '/disk3/data'
                    INDEX DIRECTORY = '/disk3/idx'
            ),
            PARTITION p2 VALUES LESS THAN MAXVALUE (
                SUBPARTITION s0
                    DATA DIRECTORY = '/disk4/data'
                    INDEX DIRECTORY = '/disk4/idx',
                SUBPARTITION s1
                    DATA DIRECTORY = '/disk5/data'
                    INDEX DIRECTORY = '/disk5/idx'
            )
        );

In diesem Fall wird für die Daten und die Indizes jedes `RANGE` eine eigene Festplatte genutzt. Es sind aber auch viele andere Varianten möglich; ein anderes Beispiel wäre:

    CREATE TABLE ts (id INT, purchased DATE)
        PARTITION BY RANGE(YEAR(purchased))
        SUBPARTITION BY HASH(TO_DAYS(purchased)) (
            PARTITION p0 VALUES LESS THAN (1990) (
                SUBPARTITION s0a
                    DATA DIRECTORY = '/disk0'
                    INDEX DIRECTORY = '/disk1',
                SUBPARTITION s0b
                    DATA DIRECTORY = '/disk2'
                    INDEX DIRECTORY = '/disk3'
            ),
            PARTITION p1 VALUES LESS THAN (2000) (
                SUBPARTITION s1a
                    DATA DIRECTORY = '/disk4/data'
                    INDEX DIRECTORY = '/disk4/idx',
                SUBPARTITION s1b
                    DATA DIRECTORY = '/disk5/data'
                    INDEX DIRECTORY = '/disk5/idx'
            ),
            PARTITION p2 VALUES LESS THAN MAXVALUE (
                SUBPARTITION s2a,
                SUBPARTITION s2b
            )
        );

Hier wird nach folgenden Regeln gespeichert:

- Da Zeilen mit `purchased`-Daten aus der Zeit vor 1990 sehr viel Platz belegen, werden sie auf vier Arten aufgeteilt, wobei jeweils eine eigene Festplatte den Daten und Indizes jeder der beiden Teilpartitionen (`s0a` und `s0b`) der Partition `p0` gewidmet ist. Mit anderen Worten:

  - Die Daten für die Teilpartition `s0a` werden auf `/disk0` gespeichert.

  - Die Indizes für die Teilpartition `s0a` werden auf `/disk1` gespeichert.

  - Die Daten für die Teilpartition `s0b` werden auf `/disk2` gespeichert.

  - Die Indizes für die Teilpartition `s0b` werden auf `/disk3` gespeichert.

- Die Zeilen mit den Daten der Jahre 1990 bis 1999 (Partition `p1`) belegen nicht so viel Speicher wie die Daten von vor 1990. So werden sie auf zwei Festplatten (`/disk4` und `/disk5`) verteilt, nicht auf vier, wie wir es mit den alten in `p0` gespeicherten Daten getan haben:

  - Die Daten und Indizes der ersten Teilpartition von `p1` (`s1a`) werden auf `/disk4` gespeichert, und zwar die Daten im Verzeichnis `/disk4/data` und die Indizes im Verzeichnis `/disk4/idx`.

  - Die Daten und Indizes der zweiten Teilpartition von `p1` (`s1b`) werden auf `/disk5` gespeichert, und zwar die Daten im Verzeichnis `/disk5/data` und die Indizes im Verzeichnis `/disk5/idx`.

- Die Zeilen der Daten, die die Jahre ab 2000 betreffen (Partition `p2`), nehmen noch weniger Speicherplatz in Anspruch als die beiden anderen Datumsbereiche. Zurzeit reicht der Standardspeicherort noch für sie aus.

  Wenn später einmal die Verkaufsdaten für die mit dem Jahr 2000 beginnende Dekade so umfangreich werden, dass der Standardspeicherort nicht mehr ausreicht, können die entsprechenden Zeilen mit einer `ALTER TABLE ... REORGANIZE PARTITION`-Anweisung verschoben werden. Wie das geht, erfahren Sie unter Abschnitt 17.3, „Partitionsverwaltung“.

### 17.2.6. Wie die MySQL-Partitionierung `NULL`-Werte handhabt

Die Partitionierung in MySQL gestattet durchaus auch einen Partitionierungsausdruck mit dem Wert `NULL`, sei es nun ein Spaltenwert oder der Wert eines vom Benutzer angegebenen Ausdrucks. Normalerweise behandelt MySQL `NULL` in solchen Fällen als null. Wenn Sie dieses Verhalten unterbinden möchten, müssen Sie Tabellen so entwerfen, dass sie keine Nullwerte erlauben, indem Sie die Spalten als `NOT NULL` deklarieren.

In diesem Abschnitt geben wir einige Beispiele, die zeigen sollen, wie MySQL `NULL`-Werte verarbeitet, wenn die passende Partition für eine Zeile ermittelt werden soll.

Wenn Sie eine Zeile in eine `RANGE`- oder `LIST`-partitionierte Tabelle einfügen und der Spaltenwert, der zur Bestimmung der Partition herangezogen wird, `NULL` ist, so wird dieser Wert als `0` interpretiert. Betrachten Sie beispielsweise die folgenden beiden Tabellen:

    mysql> CREATE TABLE tnlist (
        ->     id INT,
        ->     name VARCHAR(5)
        -> )
        -> PARTITION BY LIST(id) (
        ->     PARTITION p1 VALUES IN (0),
        ->     PARTITION p2 VALUES IN (1)
        -> );
    Query OK, 0 rows affected (0.09 sec)

    mysql> CREATE TABLE tnrange (
        ->     id INT,
        ->     name VARCHAR(5)
        -> )
        -> PARTITION BY RANGE(id) (
        ->     PARTITION p1 VALUES LESS THAN (1),
        ->     PARTITION p2 VALUES LESS THAN MAXVALUE
        -> );
    Query OK, 0 rows affected (0.09 sec)

    mysql> INSERT INTO tnlist VALUES (NULL, 'bob');
    Query OK, 1 row affected (0.00 sec)

    mysql> INSERT INTO tnrange VALUES (NULL, 'jim');
    Query OK, 1 row affected (0.00 sec)

    mysql> SELECT * FROM tnlist;
    +------+------+
    | id   | name |
    +------+------+
    | NULL | bob  |
    +------+------+
    1 row in set (0.00 sec)

    mysql> SELECT * FROM tnrange;
    +------+------+
    | id   | name |
    +------+------+
    | NULL | jim  |
    +------+------+
    1 row in set (0.00 sec)

In beiden Tabellen war die Spalte `id` nicht als `NOT NULL` deklariert, kann also `NULL`-Werte annehmen. Dass die Zeilen in den `p1`-Partitionen der Tabellen gespeichert wurden, können Sie überprüfen, indem Sie diese Partitionen löschen und dann die `SELECT`-Anweisungen erneut ausführen:

    mysql> ALTER TABLE tnlist DROP PARTITION p1;
    Query OK, 0 rows affected (0.16 sec)

    mysql> ALTER TABLE tnrange DROP PARTITION p1;
    Query OK, 0 rows affected (0.16 sec)

    mysql> SELECT * FROM tnlist;
    Empty set (0.00 sec)

    mysql> SELECT * FROM tnrange;
    Empty set (0.00 sec)

Bei einer `HASH`- oder `KEY`-Partitionierung wird ein Partitionierungsausdruck, der `NULL` ergibt, so behandelt, als sei sein Rückgabewert null. Dieses Verhalten können wir überprüfen, indem wir betrachten, wie sich die Erstellung einer `HASH`-partitionierten Tabelle, in die eine Zeile mit den entsprechenden Daten geladen wird, auf das Dateisystem auswirkt. Angenommen, Sie haben eine Tabelle namens `tnhash` in der Datenbank `test` mit folgender Anweisung angelegt:

    CREATE TABLE tnhash (
        id INT,
        name VARCHAR(5)
    )
    PARTITION BY HASH(id)
    PARTITIONS 2;

Wenn wir eine RPM-Installation von MySQL auf Linux zugrunde legen, erzeugt diese Anweisung zwei `.MYD`-Dateien in `/var/lib/mysql/test`, die in der bash-Shell wie folgt angezeigt werden können:

    /var/lib/mysql/test> ls *.MYD -l
    -rw-rw----  1 mysql mysql 0 2005-11-04 18:41 tnhash#P#p0.MYD
    -rw-rw----  1 mysql mysql 0 2005-11-04 18:41 tnhash#P#p1.MYD

(Hinweis: Vor MySQL 5.1.5 hätten diese Dateien `tnhash_p0.MYD` und `tnhash_p1.MYD` geheißen. Unter Abschnitt D.1.1, „Änderungen in Release 5.1.6 (Noch nicht veröffentlicht)“, und Bug#13437 finden Sie weitere Informationen darüber, wie sich diese Änderung auf Upgrades auswirkt.)

Beachten Sie, dass die beiden Dateien 0 Byte groß sind. Fügen Sie nun in `tnhash` eine Zeile ein, die in der Spalte `id` den Wert `NULL` aufweist, und überprüfen Sie, ob die Zeile tatsächlich eingefügt wurde:

    mysql> INSERT INTO tnhash VALUES (NULL, 'sam');
    Query OK, 1 row affected (0.00 sec)

    mysql> SELECT * FROM tnhash;
    +------+------+
    | id   | name |
    +------+------+
    | NULL | sam  |
    +------+------+
    1 row in set (0.01 sec)

Bitte erinnern Sie sich, dass für einen Integer `N` der Wert von `NULL MOD `N`` immer `NULL` ist. Dieses Ergebnis wird behandelt, um `0` als die korrekte Partition festzulegen. Wenn wir nun wieder in die System-Shell gehen (wobei weiterhin bash zugrunde gelegt wird), können wir erkennen, dass der Wert in die erste Partition (die nach Voreinstellung `p0` heißt) eingefügt wurde, indem wir die Datendateien erneut auflisten:

    var/lib/mysql/test> ls *.MYD -l
    -rw-rw----  1 mysql mysql 20 2005-11-04 18:44 tnhash#P#p0.MYD
    -rw-rw----  1 mysql mysql  0 2005-11-04 18:41 tnhash#P#p1.MYD

Wie Sie sehen, hat die `INSERT`-Anweisung nur die Datei `tnhash_p0.MYD` modifiziert, deren Umfang auf der Platte angewachsen ist, ohne auf die andere Datendatei Einfluss zu nehmen.

Nehmen wir nun an, wir hätten folgende Tabelle:

    CREATE TABLE tndate (
        id INT,
        dt DATE
    )
    PARTITION BY RANGE( YEAR(dt) ) (
        PARTITION p0 VALUES LESS THAN (1990),
        PARTITION p1 VALUES LESS THAN (2000),
        PARTITION p2 VALUES LESS THAN MAXVALUE
    );

Wie andere MySQL-Funktionen gibt auch `YEAR(NULL)` den Wert `NULL` zurück. Eine Zeile, die in der `dt`-Spalte den Wert `NULL` hat, wird behandelt, als wäre der Partitionierungsausdruck in Wirklichkeit `0`, und folglich in die Partition `p0` eingefügt.

## 17.3. Partitionsverwaltung

MySQL 5.1 bietet eine Reihe von Möglichkeiten, um partitionierte Tabellen zu modifizieren. Man kann Partitionen hinzufügen, löschen, umdefinieren, zusammenführen oder aufspalten. Alle diese Aktionen werden mit den Partitionierungserweiterungen des `ALTER TABLE`-Befehls ausgeführt (zur Syntax siehe Abschnitt 13.1.2, „`ALTER TABLE`“). Darüber hinaus können Sie sich Informationen über partitionierte Tabellen und Partitionen beschaffen. Diese Themen werden in den nachfolgenden Abschnitten behandelt.

- Informationen über die Partitionsverwaltung in `RANGE`- oder `LIST`-partitionierten Tabellen finden Sie unter Abschnitt 17.3.1, „Verwaltung von `RANGE`- und `LIST`-Partitionen“.

- Um die Verwaltung von `HASH`- und `KEY`-Partitionen geht es in Abschnitt 17.3.2, „Verwaltung von `HASH`- und `KEY`-Partitionen“.

- Mechanismen, mit denen Sie in MySQL 5.1 Informationen über partitionierte Tabellen and Partitionen erlangen können, werden in Abschnitt 17.3.4, „Abruf von Informationen über Partitionen“, vorgestellt.

- Wartungsoperationen auf Partitionen werden in Abschnitt 17.3.3, „Wartung von Partitionen“, beschrieben.

Hinweis: In MySQL 5.1 müssen alle Partitionen einer partitionierten Tabelle die gleiche Anzahl von Teilpartitionen haben. Nach dem Anlegen der Tabelle ist es nicht mehr möglich, die Teilpartitionierung zu ändern.

Die Anweisung `ALTER TABLE ... PARTITION BY ...` funktioniert seit MySQL 5.1.6; zuvor in MySQL 5.1 wurde ihre Syntax zwar als gültig akzeptiert, aber Auswirkungen hatte sie keine.

Um das Partitionierungsschema einer Tabelle zu ändern, müssen Sie nur dem `ALTER TABLE`-Befehl eine `partition_options`-Klausel hinzufügen. Diese Klausel hat dieselbe Syntax, die auch in `CREATE TABLE` zur Erstellung einer partitionierten Tabelle verwendet wird, und beginnt immer mit den Schlüsselwörtern `PARTITION BY`. Angenommen, Sie haben eine nach Bereichen partitionierte Tabelle mit folgender `CREATE TABLE`-Anweisung angelegt:

    CREATE TABLE trb3 (id INT, name VARCHAR(50), purchased DATE)
        PARTITION BY RANGE( YEAR(purchased) ) (
            PARTITION p0 VALUES LESS THAN (1990),
            PARTITION p1 VALUES LESS THAN (1995),
            PARTITION p2 VALUES LESS THAN (2000),
            PARTITION p3 VALUES LESS THAN (2005)
        );

Um aus dieser Tabelle eine schlüsselpartitionierte Tabelle mit zwei Partitionen zu machen, wobei der Wert der Spalte `id` die Grundlage für den Schlüssel liefert, können Sie folgende Anweisung geben:

    ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;

Auf die Struktur der Tabelle hat dies dieselben Auswirkungen wie eine Löschung und Rekonstruktion mit `CREATE TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;`.

### 17.3.1. Verwaltung von `RANGE`- und `LIST`-Partitionen

Da das Hinzufügen und Löschen von Partitionen für Bereichs- und Listenpartitionen ganz ähnlich behandelt wird, fassen wir die Verwaltung dieser beiden Partitionierungsarten im vorliegenden Abschnitt zusammen. Über den Umgang mit Hash- oder schlüsselpartitionierten Tabellen erfahren Sie unter Abschnitt 17.3.2, „Verwaltung von `HASH`- und `KEY`-Partitionen“, Genaueres. Weil das Löschen einer `RANGE`- oder `LIST`--Partition einfacher ist als das Hinzufügen, beginnen wir mit dem Löschen.

Um eine Partition aus einer `RANGE`- oder `LIST`-partitionierten Tabelle zu löschen, verwenden Sie den `ALTER TABLE`-Befehl mit einer `DROP PARTITION`-Klausel. Das folgende sehr einfach gehaltene Beispiel geht davon aus, dass Sie mit den folgenden `CREATE TABLE`- und `INSERT`-Anweisungen bereits eine nach Bereich partitionierte Tabelle angelegt und mit 10 Datensätzen bevölkert haben:

    mysql> CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)
        ->     PARTITION BY RANGE( YEAR(purchased) ) (
        ->         PARTITION p0 VALUES LESS THAN (1990),
        ->         PARTITION p1 VALUES LESS THAN (1995),
        ->         PARTITION p2 VALUES LESS THAN (2000),
        ->         PARTITION p3 VALUES LESS THAN (2005)
        ->     );
    Query OK, 0 rows affected (0.01 sec)

    mysql> INSERT INTO tr VALUES
        ->     (1, 'desk organiser', '2003-10-15'),
        ->     (2, 'CD player', '1993-11-05'),
        ->     (3, 'TV set', '1996-03-10'),
        ->     (4, 'bookcase', '1982-01-10'),
        ->     (5, 'exercise bike', '2004-05-09'),
        ->     (6, 'sofa', '1987-06-05'),
        ->     (7, 'popcorn maker', '2001-11-22'),
        ->     (8, 'aquarium', '1992-08-04'),
        ->     (9, 'study desk', '1984-09-16'),
        ->     (10, 'lava lamp', '1998-12-25');
    Query OK, 10 rows affected (0.01 sec)

Welche Daten in die Partition `p2` geladen wurden, sehen Sie hier:

    mysql> SELECT * FROM tr
        -> WHERE purchased BETWEEN '1995-01-01' AND '1999-12-31';
    +------+-----------+------------+
    | id   | name      | purchased  |
    +------+-----------+------------+
    |    3 | TV set    | 1996-03-10 |
    |   10 | lava lamp | 1998-12-25 |
    +------+-----------+------------+
    2 rows in set (0.00 sec)

Um nun die Partition `p2` zu löschen, führen Sie folgenden Befehl aus:

    mysql> ALTER TABLE tr DROP PARTITION p2;
    Query OK, 0 rows affected (0.03 sec)

Hinweis: Die Speicher-Engine `NDB Cluster` in MySQL 5.1 kennt kein `ALTER TABLE ... DROP PARTITION`. Doch immerhin unterstützt sie die anderen in diesem Kapitel beschriebenen Partitionierungserweiterungen der `ALTER TABLE`-Anweisung.

Es ist sehr wichtig, sich zu merken, dass Sie beim Löschen einer Partition alle darin gespeicherten Daten mit löschen. Dies erkennen Sie, wenn Sie die obige `SELECT`-Anfrage erneut ausführen:

    mysql> SELECT * FROM tr WHERE purchased
        -> BETWEEN '1995-01-01' AND '1999-12-31';
    Empty set (0.00 sec)

Wenn Sie alle Daten aus allen Partitionen löschen, aber die Tabellendefinition und ihr Partitionierungsschema erhalten möchten, verwenden Sie den Befehl `TRUNCATE TABLE`. (Siehe auch Abschnitt 13.2.9, „`TRUNCATE`“.)

Wenn Sie die Partitionierung einer Tabelle ändern möchten, ohne Daten zu verlieren, verwenden Sie stattdessen `ALTER TABLE ... REORGANIZE PARTITION`. Weiter unten oder in Abschnitt 13.1.2, „`ALTER TABLE`“, finden Sie Informationen über `REORGANIZE PARTITION`.

Wenn Sie nun einen `SHOW CREATE TABLE`-Befehl ausführen, können Sie sehen, wie sich die Partitionierung der Tabelle geändert hat:

    mysql> SHOW CREATE TABLE tr\G
    *************************** 1. row ***************************
           Table: tr
    Create Table: CREATE TABLE `tr` (
      `id` int(11) default NULL,
      `name` varchar(50) default NULL,
      `purchased` date default NULL
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1
    PARTITION BY RANGE ( YEAR(purchased) ) (
      PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,
      PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,
      PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM
    )
    1 row in set (0.01 sec)

Wenn Sie nun in die geänderte Tabelle Zeilen einfügen, die in der Spalte `purchased` Werte zwischen `'1995-01-01'` und `'2004-12-31'` einschließlich aufweisen, werden diese Zeilen in der Partition `p3` gespeichert. Dies können Sie folgendermaßen überprüfen:

    mysql> INSERT INTO tr VALUES (11, 'pencil holder', '1995-07-12');
    Query OK, 1 row affected (0.00 sec)

    mysql> SELECT * FROM tr WHERE purchased
        -> BETWEEN '1995-01-01' AND '2004-12-31';
    +------+----------------+------------+
    | id   | name           | purchased  |
    +------+----------------+------------+
    |   11 | pencil holder  | 1995-07-12 |
    |    1 | desk organiser | 2003-10-15 |
    |    5 | exercise bike  | 2004-05-09 |
    |    7 | popcorn maker  | 2001-11-22 |
    +------+----------------+------------+
    4 rows in set (0.00 sec)

    mysql> ALTER TABLE tr DROP PARTITION p3;
    Query OK, 0 rows affected (0.03 sec)

    mysql> SELECT * FROM tr WHERE purchased
        -> BETWEEN '1995-01-01' AND '2004-12-31';
    Empty set (0.00 sec)

Beachten Sie, dass die Anzahl der Zeilen, die aus der Tabelle mit `ALTER TABLE ... DROP PARTITION` gelöscht wurden, vom Server nicht so gemeldet wird, wie es bei einer entsprechenden `DELETE`-Anweisung der Fall wäre.

Zum Löschen von `LIST`-Partitionen wird genau dieselbe `ALTER TABLE ... DROP PARTITION`-Syntax verwendet wie zum Löschen von `RANGE`-Partitionen. Allerdings unterscheiden sich die Auswirkungen, die dieses auf den späteren Gebrauch der Tabelle hat, in einer wichtigen Hinsicht: In die Tabelle können nun keine Zeilen mehr eingefügt werden, die irgendwelche in der Werteliste der gelöschten Partition vorkommenden Werte aufweisen. (Ein Beispiel finden Sie unter Abschnitt 17.2.2, „`LIST`-Partitionierung“.)

Um einer zuvor partitionierten Tabelle eine neue Bereichs- oder Listenpartition hinzuzufügen, verwenden Sie die `ALTER TABLE ... ADD PARTITION`-Anweisung. Für Tabellen, die nach `RANGE` partitioniert werden, können Sie mit dieser Anweisung einen neuen Wertebereich am Anfang oder Ende der Liste der vorhandenen Partitionen hinzufügen. Angenommen, Sie haben eine partitionierte Tabelle mit Mitgliedsdaten Ihrer Organisation wie folgt definiert:

    CREATE TABLE members (
        id INT,
        fname VARCHAR(25),
        lname VARCHAR(25),
        dob DATE
    )
    PARTITION BY RANGE( YEAR(dob) ) (
        PARTITION p0 VALUES LESS THAN (1970),
        PARTITION p1 VALUES LESS THAN (1980),
        PARTITION p2 VALUES LESS THAN (1990)
    );

Nehmen wir nun weiterhin an, das Mindestalter der Mitglieder ist 16 Jahre. Da es bereits auf Ende 2005 zugeht, stellen Sie fest, dass Sie demnächst Mitglieder aufnehmen müssen, die 1990 geboren wurden, und in den darauf folgenden Jahren wiederum Mitglieder, die noch später zur Welt kamen. Mit folgender Anweisung können Sie die `members`-Tabelle so umändern, dass auch Mitglieder zulässig sind, die in den Jahren 1990 bis 1999 geboren wurden:

    ALTER TABLE ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000));

Wichtig: Tabellen, die nach Bereich partitioniert wurden, können Sie mit dem Befehl `ADD PARTITION` nur am oberen Ende der Wertebereichsliste neue Partitionen hinzufügen. Wenn Sie versuchen, auf diesem Weg Partitionen zwischen oder vor vorhandenen Partitionen einzufügen, wird folgender Fehler generiert:

    mysql> ALTER TABLE members
         >     ADD PARTITION (
         >     PARTITION p3 VALUES LESS THAN (1960));
    ERROR 1463 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

In ähnlicher Weise können Sie auch einer `LIST`-partitionierten Tabelle neue Partitionen hinzufügen. Nehmen wir als Beispiel folgende Tabelle:

    CREATE TABLE tt (
        id INT,
        data INT
    )
    PARTITION BY LIST(data) (
        PARTITION p0 VALUES IN (5, 10, 15),
        PARTITION p1 VALUES IN (6, 12, 18)
    );

Mit der folgenden Anweisung können Sie eine neue Partition für Zeilen einrichten, deren `data`-Spalten die Werte `7`, `14` und `21` aufweisen:

    ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21));

Achtung: Sie können keine neue `LIST`-Partition hinzufügen, die irgendwelche bereits in den Wertelisten bestehender Partitionen enthaltenen Werte aufweist. Wenn Sie dieses versuchen, wird ein Fehler gemeldet:

    mysql> ALTER TABLE tt ADD PARTITION
         >     (PARTITION np VALUES IN (4, 8, 12));
    ERROR 1465 (HY000): Multiple definition of same constant in list partitioning

Da Zeilen mit dem Wert `12` in der `data`-Spalte bereits der Partition `p1` zugewiesen sind, können Sie nicht in Tabelle `tt` eine neue Partition anlegen, die ebenfalls `12` in ihrer Werteliste hat. Wenn Sie dies dennoch tun müssen, löschen Sie zuerst `p1` und fügen dann `np` und danach eine neue `p1` mit einer modifizierten Definition hinzu. Allerdings würden dadurch, wie zuvor bereits gesagt, alle in `p1` gespeicherten Daten verloren gehen, was im Allgemeinen nicht der angestrebte Effekt ist. Eine andere Lösung könnte so aussehen, dass Sie eine Kopie der Tabelle mit der neuen Partitionierung anlegen, dann die Daten mit einer `CREATE TABLE ... SELECT ...` hineinkopieren und schließlich die alte Tabelle löschen und die neue umbenennen. Wenn Sie es mit großen Datenmengen zu tun haben, könnte dies allerdings eine zeitraubende Angelegenheit werden, die dort, wo Hochverfügbarkeit gewährleistet werden muss, vielleicht vollends unmöglich ist.

Seit MySQL 5.1.6 können Sie mehrere Partitionen in einer einzigen `ALTER TABLE ... ADD PARTITION`-Anweisung anlegen:

    CREATE TABLE employees (
      id INT NOT NULL,
      fname VARCHAR(50) NOT NULL,
      lname VARCHAR(50) NOT NULL,
      hired DATE NOT NULL
    )
    PARTITION BY RANGE( YEAR(hired) ) (
      PARTITION p1 VALUES LESS THAN (1991),
      PARTITION p2 VALUES LESS THAN (1996),
      PARTITION p3 VALUES LESS THAN (2001),
      PARTITION p4 VALUES LESS THAN (2005)
    );

    ALTER TABLE employees ADD PARTITION (
        PARTITION p5 VALUES LESS THAN (2010),
        PARTITION p6 VALUES LESS THAN MAXVALUE
    );

Zum Glück bietet die Implementierung der Partitionierung in MySQL Möglichkeiten, Partitionen ohne Datenverlust umzudefinieren. Betrachten wir zunächst eine Reihe von einfachen Beispielen zur `RANGE`-Partitionierung. Bitte erinnern Sie sich an die `members`-Tabelle, die nun folgendermaßen definiert ist:

    mysql> SHOW CREATE TABLE members\G
    *************************** 1. row ***************************
           Table: members
    Create Table: CREATE TABLE `members` (
      `id` int(11) default NULL,
      `fname` varchar(25) default NULL,
      `lname` varchar(25) default NULL,
      `dob` date default NULL
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1
    PARTITION BY RANGE ( YEAR(dob) ) (
      PARTITION p0 VALUES LESS THAN (1970) ENGINE = MyISAM,
      PARTITION p1 VALUES LESS THAN (1980) ENGINE = MyISAM,
      PARTITION p2 VALUES LESS THAN (1990) ENGINE = MyISAM.
      PARTITION p3 VALUES LESS THAN (2000) ENGINE = MyISAM
    )

Angenommen, Sie möchten alle Zeilen der Mitglieder, die vor 1960 geboren wurden, in eine separate Partition speichern. Wir wir bereits sahen, ist dies mit der Anweisung `ALTER TABLE ... ADD PARTITION` nicht möglich. Sie können jedoch eine andere Partitionierungserweiterung von `ALTER TABLE` benutzen, um dies zu erreichen:

    ALTER TABLE members REORGANIZE PARTITION p0 INTO (
        PARTITION s0 VALUES LESS THAN (1960),
        PARTITION s1 VALUES LESS THAN (1970)
    );

Im Endeffekt spaltet dieser Befehl die Partition `p0` in zwei neue Partitionen `s0` und `s1` auf. Außerdem werden die bisher in `p0` gespeicherten Daten nach den Regeln der beiden `PARTITION ... VALUES ...`-Klauseln auf die neuen Partitionen verteilt, sodass `s0` nur Datensätze aufnimmt, deren `YEAR(dob)`-Wert kleiner als 1960 ist, und `s1` nur Datensätze, deren `YEAR(dob)`-Wert größer oder gleich 1960, aber kleiner als 1970 ist.

Sie können auch mit einer `REORGANIZE PARTITION`-Klausel benachbartete Partitionen verschmelzen. Mit dem folgenden Befehl stellen Sie die `members`-Tabelle wieder auf ihre vorherige Partitionierung um:

    ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (
        PARTITION p0 VALUES LESS THAN (1970)
    );

Wenn Sie Partitionen mit `REORGANIZE PARTITION` aufteilen oder zusammenführen, gehen keine Daten verloren. Bei der Ausführung dieser Anweisung verschiebt MySQL alle Datensätze, die zuvor in den Partitionen `s0` und `s1` gespeichert waren, wieder in die Partition `p0`.

`REORGANIZE PARTITION` hat folgende allgemeine Syntax:

    ALTER TABLE tbl_name
        REORGANIZE PARTITION partition_list
        INTO (partition_definitions);

`tbl_name` ist hier der Name der partitionierten Tabelle und `partition_list` ist eine kommagetrennte Liste mit den Namen einer oder mehrerer vorhandener Partitionen, die geändert werden sollen. `partition_definitions` ist eine kommagetrennte Liste neuer Partitionsdefinitionen, für die dieselben Regeln gelten, wie die `partition_definitions` einer `CREATE TABLE`-Anweisung (siehe Abschnitt 13.1.5, „`CREATE TABLE`“). Doch `REORGANIZE PARTITION` kann noch mehr, als nur Partitionen aufspalten oder zusammenführen: Mit demselben Befehl können Sie auch beispielsweise aus den vier Partitionen der `members`-Tabelle zwei machen:

    ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (
        PARTITION m0 VALUES LESS THAN (1980),
        PARTITION m1 VALUES LESS THAN (2000)
    );

`REORGANIZE PARTITION` ist auch für `LIST`-partitionierte Tabellen geeignet. Greifen wir noch einmal das Problem auf, wie man eine neue Partition zu der listenpartitionierten Tabelle `tt` hinzufügen könnte. Der Versuch scheiterte, weil die neue Partition einen Wert hatte, der bereits in den Wertelisten der vorhandenen Partitionen auftauchte. Dies können wir umgehen, indem wir eine Partition hinzufügen, deren Liste nur zulässige Werte enthält, und dann die neue und die vorhandenen Partitionen so umorganisieren, dass der konfliktbeladene Wert aus der Werteliste der alten in die Werteliste der neuen Partition übertragen wird:

    ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8));
    ALTER TABLE tt REORGANIZE PARTITION p1,np INTO (
        PARTITION p1 VALUES IN (6, 18),
        PARTITION np VALUES in (4, 8, 12)
    );

Die folgenden Punkte sind wichtig, wenn Sie `ALTER TABLE ... REORGANIZE PARTITION` zur Neupartitionierung von `RANGE`- oder `LIST`-partitionierten Tabellen einsetzen:

- Für die `PARTITION`-Klauseln, mit denen das neue Partitionierungsschema festgelegt wird, gelten dieselben Regeln wie für die, die in einer `CREATE TABLE`-Anweisung benutzt werden.

  Zuallererst müssen Sie daran denken, dass das neue Partitionierungsschema keine sich überschneidenden Wertebereiche (`RANGE`-partitionierte Tabellen) bzw. keine Überschneidungen in den Wertemengen (`LIST`-partitionierte Tabellen) haben darf.

  Hinweis: Vor MySQL 5.1.4 konnten Sie die Namen vorhandener Partitionen nicht in der `INTO`-Klausel wiederverwenden, selbst wenn diese Partitionen gelöscht oder umdefiniert werden sollten. Weitere Informationen gibt es unter Abschnitt D.1.3, „Änderungen in Release 5.1.4 (21. Dezember 2005)“.

- Die Kombination der Partitionen in der `partition_definitions`-Liste sollte insgesamt denselben Wertebereich oder dieselbe Wertemenge abdecken wie die Kombination der Partitionen, die in der `partition_list` aufgeführt sind.

  In der `members`-Tabelle, die in diesem Abschnitt als Beispiel dient, decken zum Beispiel die Partitionen `p1` und `p2` zusammen die Jahre 1980 bis 1999 ab. Daher muss jede Neuorganisation dieser beiden Partitionen insgesamt denselben Zeitraum in Jahren abdecken.

- In `RANGE`-partitionierten Tabellen können Sie nur benachbarte Partitionen reorganisieren, da keine Bereichspartitionen übersprungen werden dürfen.

  Sie können beispielsweise die `members`-Tabelle nicht mit einer Anweisung reorganisieren, die mit `ALTER TABLE members REORGANIZE PARTITION p0,p2 INTO ...` anfängt, da `p0` die Jahre vor 1970 und `p2` die Jahre von 1990 bis einschließlich 1999 umfasst und somit die beiden Partitionen nicht benachbart sind.

- Sie können `REORGANIZE PARTITION` nicht einsetzen, um den Partitionierungstyp einer Tabelle zu ändern, also beispielsweise nicht aus `RANGE`-Partitionen `HASH`-Partitionen oder umgekehrt machen. Auch können Sie diesen Befehl nicht verwenden, um den Partitionierungsausdruck oder die Partitionierungsspalte zu ändern. Um dieses zu tun, ohne die Tabelle löschen und rekonstruieren zu müssen, verwenden Sie `ALTER TABLE ... PARTITION BY ...`. Zum Beispiel:

      ALTER TABLE members
          PARTITION BY HASH( YEAR(dob) )
          PARTITIONS 8;

  Hinweis: In MySQL 5.1 5.1.5-alpha ist `ALTER TABLE ... PARTITION BY ...` noch nicht implementiert. Stattdessen müssen Sie die Tabelle entweder löschen und mit der gewünschten Partitionierung neu erzeugen oder, wenn Sie die Daten der Tabelle bewahren möchten, mit `CREATE TABLE ... SELECT ...` die neue Tabelle anlegen und die Daten aus der alten herüberkopieren, um anschließend die alte Tabelle zu löschen und in einem letzten Schritt die neue Tabelle umzubenennen, wenn dies gewünscht wird.

### 17.3.2. Verwaltung von `HASH`- und `KEY`-Partitionen

Die Partitionierung von Hash- oder schlüsselpartitionierten Tabellen lässt sich in ähnlicher Weise ändern, wobei sich Tabellen mit diesen beiden Formen der Partitionierung in mehrerer Hinsicht von bereichs- oder listenpartitionierten Tabellen unterscheiden. Daher werden in diesem Abschnitt nur Änderungen von Hash- oder schlüsselpartitionierten Tabellen behandelt. Das Hinzufügen und Löschen der Partitionen von bereichs- oder listenpartitionierten Tabellen wird in Abschnitt 17.3.1, „Verwaltung von `RANGE`- und `LIST`-Partitionen“, beschrieben.

Sie können Partitionen aus `HASH`- oder `KEY`-partitionierten Tabellen nicht genauso löschen wie aus `RANGE`- oder `LIST`-partitionierten Tabellen. Doch zum Zusammenführen von `HASH`- oder `KEY`-partitionierten Tabellen können Sie ebenfalls den `ALTER TABLE ... COALESCE PARTITION`-Befehl verwenden. Angenommen, Sie haben eine Tabelle mit Kundendaten, die in 12 Partitionen aufgeteilt ist. Diese `clients`-Tabelle ist folgendermaßen definiert:

    CREATE TABLE clients (
        id INT,
        fname VARCHAR(30),
        lname VARCHAR(30),
        signed DATE
    )
    PARTITION BY HASH( MONTH(signed) )
    PARTITIONS 12;

Um die Anzahl der Partitionen von 12 auf 6 zu reduzieren, führen Sie folgenden `ALTER TABLE`-Befehl aus:

    mysql> ALTER TABLE clients COALESCE PARTITION 6;
    Query OK, 0 rows affected (0.02 sec)

`COALESCE` funktioniert genauso gut mit `HASH`-, `KEY`-, `LINEAR HASH`- oder `LINEAR KEY`-partitionierten Tabellen. Hier sehen Sie ein ähnliches Beispiel wie oben, allerdings dieses Mal mit einer Tabelle, die nach `LINEAR KEY` partitioniert ist:

    mysql> CREATE TABLE clients_lk (
        ->     id INT,
        ->     fname VARCHAR(30),
        ->     lname VARCHAR(30),
        ->     signed DATE
        -> )
        -> PARTITION BY LINEAR KEY(signed)
        -> PARTITIONS 12;
    Query OK, 0 rows affected (0.03 sec)

    mysql> ALTER TABLE clients_lk COALESCE PARTITION 6;
    Query OK, 0 rows affected (0.06 sec)
    Records: 0  Duplicates: 0  Warnings: 0

`COALESCE` kann nicht verwendet werden, um die Anzahl der Partitionen zu erhöhen. Ein Versuch, dies zu tun, verursacht folgenden Fehler:

    mysql> ALTER TABLE clients COALESCE PARTITION 18;
    ERROR 1478 (HY000): Cannot remove all Partitionen, use DROP TABLE instead

Um die Anzahl der Partitionen der `clients`-Tabelle von 12 auf 18 zu erhöhen, erteilen Sie folgende `ALTER TABLE ... ADD PARTITION`-Anweisung:

    ALTER TABLE clients ADD PARTITION PARTITIONS 18;

Hinweis: `ALTER TABLE ... REORGANIZE PARTITION` kann nicht für `HASH`- oder `KEY`-partitionierte Tabellen verwendet werden.

### 17.3.3. Wartung von Partitionen

Hinweis: Die Befehle, die dieser Abschnitt beschreibt, sind in MySQL 5.1 noch nicht implementiert. Sie werden vor allem vorgestellt, um Feedback von Benutzern zu bekommen, die die Software während des Entwicklungszyklus der Version 5.1 vor der Produktionsreife testen. (Mit anderen Worten: Bitte schicken Sie uns keine Bugreports mit dem Hinweis, dass diese Befehle nicht funktionieren.) Die Informationen in diesem Abschnitt ändern sich noch ständig, da die Entwicklung der Partitionierung für MySQL 5.1 weitergeht. Wir werden den Abschnitt aktualisieren, wenn Partitionierungsfeatures implementiert oder verbessert werden.

MySQL 5.1 ermöglicht viele Wartungsarbeiten im Bereich der Partitionierung. MySQL unterstützt für partitionierte Tabellen nicht die Befehle `CHECK TABLE`, `OPTIMIZE TABLE`, `ANALYZE TABLE` oder `REPAIR TABLE`. Dagegen können Sie jedoch eine Reihe von Erweiterungen des `ALTER TABLE`-Befehls einsetzen, um diese Operationen auf einer oder mehreren Partitionen direkt auszuführen. Diese Erweiterungen sind im Folgenden dargestellt:

- Partitionen neu erstellen: Legt die Partition neu an, hat denselben Effekt wie das Löschen und anschließende Wiedereinfügen aller in der Partition gespeicherten Datensätze. Kann bei der Defragmentierung nützlich sein.

  Beispiel:

      ALTER TABLE t1 REBUILD PARTITION (p0, p1);

- Partitionen optimieren: Wenn Sie viele Zeilen aus einer Partition gelöscht oder viele Änderungen an einer partitionierten Tabelle mit Zeilen variabler Länge (`VARCHAR`-, `BLOB`- oder `TEXT`-Spalten) vorgenommen haben, können Sie mit `ALTER TABLE ... OPTIMIZE PARTITION` unbenutzten Speicherplatz zurückholen und die Datendatei der Partition defragmentieren.

  Beispiel:

      ALTER TABLE t1 OPTIMIZE PARTITION (p0, p1);

  Wenn Sie auf einer Partition `OPTIMIZE PARTITION` ausführen, ist dies dasselbe, als würden Sie `CHECK PARTITION`, `ANALYZE PARTITION` und `REPAIR PARTITION` aufrufen.

- Partitionen analysieren: Liest und speichert die Schlüsselverteilungen für Partitionen.

  Beispiel:

      ALTER TABLE t1 ANALYZE PARTITION (p3);

- Partitionen reparieren: Repariert beschädigte Partitionen.

  Beispiel:

      ALTER TABLE t1 REPAIR PARTITION (p0,p1);

- Partitionen überprüfen: Sie können Partitionen in ganz ähnlicher Weise auf Fehler untersuchen, wie Sie es mit `CHECK TABLE` für nicht partitionierte Tabellen machen würden.

  Beispiel:

      ALTER TABLE trb3 CHECK PARTITION (p1);

  Dieser Befehl sagt Ihnen, ob die Daten oder Indizes in Partition `p1` der Tabelle `t1` beschädigt sind. Wenn dies der Fall ist, können Sie die Partition mit `ALTER TABLE ... REPAIR PARTITION` wieder reparieren.

Diese Aufgaben können Sie auch lösen, indem Sie mysqlcheck oder myisamchk auf den separaten `.MYI`-Dateien ausführen, die bei der Partitionierung einer Tabelle generiert werden. Siehe Abschnitt 8.9, „mysqlcheck — Hilfsprogramm für die Wartung und Reparatur von Tabellen“. (Diese Möglichkeit steht auch bereits im Pre-Alpha-Code zur Verfügung.)

### 17.3.4. Abruf von Informationen über Partitionen

Dieser Abschnitt beschreibt, wie Sie sich Informationen über bestehende Partitionen beschaffen können. Da sich diese Funktionalität noch in der Planung befindet, sind die folgenden Ausführungen zurzeit nur eine Absichtserklärung über die Dinge, die wir in MySQL 5.1 implementieren wollen.

Wie bereits an anderer Stelle in diesem Kapitel gesagt, zeigt die Ausgabe von `SHOW CREATE TABLE` auch eine `PARTITION BY`-Klausel an, mit der eine partitionierte Tabelle angelegt wurde. Zum Beispiel:

    mysql> SHOW CREATE TABLE trb3\G
    *************************** 1. row ***************************
           Table: trb3
    Create Table: CREATE TABLE `trb3` (
      `id` int(11) default NULL,
      `name` varchar(50) default NULL,
      `purchased` date default NULL
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1
    PARTITION BY RANGE (YEAR(purchased)) (
      PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,
      PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,
      PARTITION p2 VALUES LESS THAN (2000) ENGINE = MyISAM,
      PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM
    )
    1 row in set (0.00 sec)

Hinweis: In frühen MySQL 5.1-Releases wurde die `PARTITIONS`-Klausel nicht für Tabellen angezeigt, die nach `HASH` oder `KEY` partitioniert waren. Dies wurde in MySQL 5.1.6 behoben.

`SHOW TABLE STATUS` funktioniert auch für partitionierte Tabellen und hat dieselbe Ausgabe wie für nichtpartitionierte Tabellen, nur dass die Spalte `Engine` immer den Wert `'PARTITION'` hat. (Mehr über diesen Befehl erfahren Sie in Abschnitt 13.5.4.21, „`SHOW TABLE STATUS`“.)

Informationen über Partitionen liefert Ihnen auch das `INFORMATION_SCHEMA`, zu dem auch eine `PARTITIONS`-Tabelle gehört. Siehe Abschnitt 22.19, „Die Tabelle `INFORMATION_SCHEMA PARTITIONS`“.

Seit MySQL 5.1.5 können Sie mit `EXPLAIN PARTITIONS` herausfinden, welche Partitionen einer partitionierten Tabelle an einer `SELECT`-Anfrage beteiligt sind. Das Schlüsselwort `PARTITIONS` fügt der Ausgabe von `EXPLAIN` eine `partitions`-Spalte hinzu, in der die Partitionen aufgeführt sind, in denen die Anfrage Datensätze erkannt hat.

Angenommen, Sie haben eine Tabelle `trb1`, die folgendermaßen definiert und mit Daten gefüllt ist:

    CREATE TABLE trb1 (id INT, name VARCHAR(50), purchased DATE)
        PARTITION BY RANGE(id)
        (
            PARTITION p0 VALUES LESS THAN (3),
            PARTITION p1 VALUES LESS THAN (7),
            PARTITION p2 VALUES LESS THAN (9),
            PARTITION p3 VALUES LESS THAN (11)
        );

    INSERT INTO trb1 VALUES
        (1, 'desk organiser', '2003-10-15'),
        (2, 'CD player', '1993-11-05'),
        (3, 'TV set', '1996-03-10'),
        (4, 'bookcase', '1982-01-10'),
        (5, 'exercise bike', '2004-05-09'),
        (6, 'sofa', '1987-06-05'),
        (7, 'popcorn maker', '2001-11-22'),
        (8, 'aquarium', '1992-08-04'),
        (9, 'study desk', '1984-09-16'),
        (10, 'lava lamp', '1998-12-25');

Mit folgendem Befehl können Sie sich darüber informieren, welche Partitionen in einer Anfrage wie `SELECT * FROM trb1;` benutzt werden:

    mysql> EXPLAIN PARTITIONS SELECT * FROM trb1\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: trb1
       partitions: p0,p1,p2,p3
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 10
            Extra: Using filesort

In diesem Fall wurden 4 vier Partitionen durchsucht. Wenn Sie der Anfrage jedoch eine einschränkende Bedingung hinzufügen, die den Partitionierungsschlüssel enthält, können Sie erkennen, dass nur diejenigen Partitionen gescannt werden, die passende Werte enthalten:

    mysql> EXPLAIN PARTITIONS SELECT * FROM trb1 WHERE id < 5\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: trb1
       partitions: p0,p1
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 10
            Extra: Using where

`EXPLAIN PARTITIONS` informiert über verwendete und mögliche Schlüssel ebenso wie die standardmäßige `EXPLAIN SELECT`-Anweisung:

    mysql> ALTER TABLE trb1 ADD PRIMARY KEY (id);
    Query OK, 10 rows affected (0.03 sec)
    Records: 10  Duplicates: 0  Warnings: 0

    mysql> EXPLAIN PARTITIONS SELECT * FROM trb1 WHERE id < 5\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: trb1
       partitions: p0,p1
             type: range
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: NULL
             rows: 7
            Extra: Using where

Beachten Sie jedoch folgende Restriktionen von `EXPLAIN PARTITIONS`:

- Sie dürfen die Schlüsselwörter `PARTITIONS` und `EXTENDED` nicht zusammen in derselben `EXPLAIN ... SELECT`-Anweisung benutzen, sonst verursachen Sie einen Syntaxfehler.

- Der Befehl `EXPLAIN PARTITIONS` liefert nur dann brauchbare Ergebnisse, wenn er zur Untersuchung von Anfragen auf `RANGE`- oder `LIST`-partitionierten Tabellen verwendet wird. (Bei `KEY`- oder `HASH`-partitionierten Tabellen listet der Befehl einfach alle Partitionen in der `partitions`-Spalte seiner Ausgabe auf.)

  Wenn Sie mit `EXPLAIN PARTITIONS` eine Anfrage auf einer nichtpartitionierten Tabelle untersuchen, wird zwar kein Fehler ausgelöst, aber der Wert der `partitions`-Spalte ist dann immer `NULL`.

- `EXPLAIN PARTITIONS` funktioniert zurzeit nur mit Tabellen, die auf einer Integer-Spalte partitioniert werden.

## 17.4. Beschränkungen und Grenzen der Partitionierung

Dieser Abschnitt beschreibt die gegenwärtigen Restriktionen der MySQL-Partitionierungsunterstützung:

- Partitionierte Tabellen unterstützen keine Fremdschlüssel. Dies schließt auch Tabellen ein, die mit der Speicher-Engine `InnoDB` arbeiten.

- Partitionierte Tabellen können zwar jede beliebige Speicher-Engine von MySQL benutzen, aber alle Partitionen und Teilpartitionen der Tabelle (wenn vorhanden) müssen dieselbe Engine verwenden.

  Wir hoffen, diese Beschränkung in einem künftigen MySQL-Release aufzuheben.

- Ein Partitionierungsschlüssel muss entweder eine Integer-Spalte oder ein Ausdruck sein, der einen Integer ergibt. In jedem Fall muss der verwendete Wert nichtnegativ sein. Zurzeit sind auch `NULL`-Werte zulässig, aber dies wird sich noch ändern.

- Teilpartitionen können nur `HASH`- oder `KEY`-Partitionierung verwenden.

Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.

Zurück |  | Weiter
Kapitel 16. MySQL Cluster | Zum Anfang | Kapitel 18. Raumbezogene Erweiterungen in MySQL