Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
Häufig wird bei Geschwindigkeitsproblemen auf die MySQL-Engine MyISAM verwiesen. Diese sei deutlich schneller als die InnoDB Variante. Wenn nur Performance wichtig ist, mag dies stimmen, aber wenn das Kriterium Datenqualität dazu kommt, dann ist MyISAM nur noch zweite Wahl.
* MySQL 5.1.30
Die Datenbank-Tabellen hierfür sehen wie folgt aus:
/* Engine=MyISAM */
CREATE TABLE personal (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
CREATE TABLE organigramm (
id INT NOT NULL,
pers_id INT NOT NULL,
chef_id INT,
PRIMARY KEY (id),
CONSTRAINT fk_organigramm_01
FOREIGN KEY (pers_id)
REFERENCES personal (id)
ON DELETE CASCADE, -- ebenfalls löschen
CONSTRAINT fk_organigramm_02
FOREIGN KEY (chef_id)
REFERENCES personal (id)
ON DELETE SET NULL -- auf NULL setzen
) ENGINE=MyISAM;
Nun noch die Testdaten für die nachfolgenden SQL-Abfragen.
/**
* Personal
*/
INSERT INTO personal VALUES
( 1, 'Chef-1'),
( 2, 'Abteilungsleiter-1' ),
( 3, 'Abteilungsleiter-2' ),
( 4, 'Abteilungsleiter-3' ),
( 5, 'Angestellter-1' ),
( 6, 'Angestellter-2' ),
( 7, 'Angestellter-3' ),
( 8, 'Angestellter-4' ),
( 9, 'Angestellter-5' ),
(10, 'Angestellter-6' ),
(11, 'Angestellter-7' );
/**
* Organigramm: Person hat Vorgesetzten
*/
INSERT INTO organigramm VALUES
( 1, 1, NULL ),
( 2, 2, 1 ),
( 3, 3, 1 ),
( 4, 4, 1 ),
( 5, 5, 2 ),
( 6, 6, 2 ),
( 7, 7, 3 ),
( 8, 8, 3 ),
( 9, 9, 4 ),
(10, 10, 4 ),
(11, 11, 4 );
Der Inhalt der Tabelle organigramm sieht jetzt so aus. Der einzige, der keinen Vorgesetzten hat, ist der Chef. Dies wird dann mit einem '-' dargestellt
/**
* Organigramm
*/
SELECT p1.name AS person,
COALESCE(p2.name,'-') AS hat_vorgesetzten
FROM organigramm o1
JOIN personal p1
ON p1.id = o1.pers_id
LEFT JOIN personal p2
ON p2.id = o1.chef_id
ORDER BY o1.pers_id;
+--------------------+--------------------+
| person | hat_vorgesetzten |
+--------------------+--------------------+
| Chef-1 | - |
| Abteilungsleiter-1 | Chef-1 |
| Abteilungsleiter-2 | Chef-1 |
| Abteilungsleiter-3 | Chef-1 |
| Angestellter-1 | Abteilungsleiter-1 |
| Angestellter-2 | Abteilungsleiter-1 |
| Angestellter-3 | Abteilungsleiter-2 |
| Angestellter-4 | Abteilungsleiter-2 |
| Angestellter-5 | Abteilungsleiter-3 |
| Angestellter-6 | Abteilungsleiter-3 |
| Angestellter-7 | Abteilungsleiter-3 |
+--------------------+--------------------+
11 rows in set (0.01 sec)
Gesetzt den Fall, einem Abteilungsleiter wird gekündigt, was passiert mit dem Organigramm?
/**
* Abteilungsleiter-2 wird entlassen (MyISAM)
*/
DELETE FROM personal
WHERE id = 3;
Query OK, 1 row affected (0.01 sec)
Nach dem Löschen werfen wir einen Blick auf das Organigramm. Das Ergebnis stellt sich eigentlich wie erwartet dar. Also alles paletti, Aufgabe gelöst?
/**
* Organigramm II
*/
SELECT p1.name AS person,
COALESCE(p2.name,'-') AS hat_vorgesetzten
FROM organigramm o1
JOIN personal p1
ON p1.id = o1.pers_id
LEFT JOIN personal p2
ON p2.id = o1.chef_id
ORDER BY o1.pers_id;
+--------------------+--------------------+
| person | hat_vorgesetzten |
+--------------------+--------------------+
| Chef-1 | - |
| Abteilungsleiter-1 | Chef-1 |
| Abteilungsleiter-3 | Chef-1 | (<= Der Abteilungleiter-2 ist verschwunden)
| Angestellter-1 | Abteilungsleiter-1 |
| Angestellter-2 | Abteilungsleiter-1 |
| Angestellter-3 | - | <= kein direkter Vorgesetzter zugeordnet
| Angestellter-4 | - | <= kein direkter Vorgesetzter zugeordnet
| Angestellter-5 | Abteilungsleiter-3 |
| Angestellter-6 | Abteilungsleiter-3 |
| Angestellter-7 | Abteilungsleiter-3 |
+--------------------+--------------------+
10 rows in set (0.00 sec)
Aber "unter der Haube" sieht das Ganze nicht mehr so gut aus. Denn nur aufgrund der LEFT JOIN Funktionalität hat das Ergebnis der SQL-Ausgabe (s. Listing: 5) eher zufällig gestimmt.
/**
* Inhalt der Tabelle organigramm
*/
SELECT o1.pers_id AS person,
o1.chef_id AS hat_vorgesetzten
FROM organigramm o1
ORDER BY o1.pers_id;
+--------+------------------+
| person | hat_vorgesetzten |
+--------+------------------+
| 1 | NULL |
| 2 | 1 |
| 3 | 1 | <= Datenmüll (bereits gelöschte ID)
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 | <= Datenmüll (bereits gelöschte ID)
| 8 | 3 | <= Datenmüll (bereits gelöschte ID)
| 9 | 4 |
| 10 | 4 |
| 11 | 4 |
+--------+------------------+
11 rows in set (0.00 sec)
Diese zusätzlichen SQL-Befehle sind bei der MyISAM Variante notwendig, damit die abhängigte Tabelle organigramm keine ungültigen Daten mehr enthält.
/**
* Aufräumarbeiten
*/
DELETE FROM organigramm
WHERE pers_id = 3;
UPDATE organigramm
SET chef_id = NULL
WHERE chef_id = 3;
Um sich diesen zusätzlichen Aufwand zu sparen, hatten wir eingangs (siehe Listing: 1) in der Tabelle organigramm folgende Regeln (Constraints) definiert, die aber von der MyISAM Engine nicht berücksichtigt werden. Was sollten diese eigentlich bewirken?
ON DELETE CASCADE bedeutet:
Beim Löschen der Elterntabelle (hier Personal), werden automatisch die abhängigen Datensätze in der Tabelle organigramm gelöscht.
ON DELETE SET NULL bedeutet:
Beim Löschen der Elterntabelle (hier Personal), werden automatisch die referenzierenden Spalten (chef_id) der Tabelle organigramm auf den Wert NULL gesetzt.
Entfernen wir die beiden Tabellen noch einmal und legen sie neu an. Diesmal weisen wir als Engine "InnoDB" zu.
/**
* Tabellen entfernen
*/
DROP TABLE organigramm;
DROP TABLE personal;
/* Engine=InnoDB */
CREATE TABLE personal (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE organigramm (
id INT NOT NULL,
pers_id INT NOT NULL,
chef_id INT,
PRIMARY KEY (id),
CONSTRAINT fk_organigramm_01
FOREIGN KEY (pers_id)
REFERENCES personal (id)
ON DELETE CASCADE, -- ebenfalls löschen
CONSTRAINT fk_organigramm_02
FOREIGN KEY (chef_id)
REFERENCES personal (id)
ON DELETE SET NULL -- auf NULL setzen
) ENGINE=InnoDB;
Jetzt fügen wird nochmals die Daten ein (siehe Listing: 2). Damit haben wir wieder den gleichen Datenbestand wie vor unserem "Löschtest" (siehe Listing: 4).
/**
* Abteilungsleiter-2 wird entlassen (InnoDB)
*/
DELETE FROM personal
WHERE id = 3;
Query OK, 1 row affected (0.01 sec)
/**
* Inhalt der Tabelle organigramm
*/
SELECT o1.pers_id AS person,
o1.chef_id AS hat_vorgesetzten
FROM organigramm o1
ORDER BY o1.pers_id;
+--------+------------------+
| person | hat_vorgesetzten |
+--------+------------------+
| 1 | NULL |
| 2 | 1 | <= der Datensatz zur Person 3 ist nun weg
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | NULL | <= der Vorgesetzte ist autom. auf NULL gesetzt
| 8 | NULL | <= der Vorgesetzte ist autom. auf NULL gesetzt
| 9 | 4 |
| 10 | 4 |
| 11 | 4 |
+--------+------------------+
11 rows in set (0.00 sec)
Das Ergebnis und auch die Datenbasis ist nun wirklich korrekt und es gibt "keine Datenleichen" mehr im Keller.
Mit Hilfe der Fremdschlüsselbeziehungen (Foreign-Key-Constraints) ermöglichst es die MySQL-Engine InnoDB deutlich einfacher Datenqualität zu garantieren. Ob der damit eingehandelte Geschwindigkeitsnachteil gegenüber MyISAM durch die deutlich bessere Datenqualität nicht ausglichen wird, hängt vom Projekt und den jeweiligen Anforderungen ab.