Arbeiten mit der Datenbank in Drupal 7 – Lektion 11 – Merge-Abfragen (MERGE)
Merge-Abfragen sind spezielle hybride Abfragen. Obwohl der Syntax-Standard dafür in SQL 2003 definiert wurde, unterstützt keine Datenbank diesen Syntax direkt. Allerdings bieten die meisten Datenbanken alternative Implementierungen mit spezifischem Syntax an. Der Merge-Query-Builder in Drupal abstrahiert das Konzept einer Merge-Abfrage in einem Objekt, das je nach Datenbank unterschiedlich kompiliert wird und deren Besonderheiten berücksichtigt.
Im Grunde kombiniert eine Merge-Abfrage eine Einfüge- (INSERT) und eine Aktualisierungsabfrage (UPDATE). Wenn eine Zeile mit dem definierten Schlüssel existiert, wird ein UPDATE ausgeführt, ansonsten ein INSERT. In vielen Fällen entspricht das folgendem Muster:
<?php if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) { // Update mit WHERE id = $id ausführen } else { // Insert, wobei id den Wert $id bekommt } ?>
Die eigentliche Implementierung ist je nach Datenbank unterschiedlich. Beachten Sie, dass Merge-Queries zwar konzeptionell atomar sind, aber in der Praxis nur in manchen Datenbanken wie MySQL wirklich atomar ausgeführt werden.
Beispiel „Einfach machen“
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->execute(); ?>
Im Beispiel operieren wir auf der Tabelle „example“. Wir definieren einen Schlüssel 'name' mit dem Wert $name sowie ein Feld-Array mit Werten. Existiert bereits eine Zeile mit diesem 'name', werden die Felder 'field1' und 'field2' aktualisiert. Falls nicht, wird eine neue Zeile mit diesen Werten angelegt.
Unterschiedliche Werte je nach Existenz
Manchmal möchte man unterschiedliche Werte setzen, je nachdem, ob die Zeile existiert. Dafür gibt es zwei Möglichkeiten:
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateFields(array( 'field1' => $alternate1, )) ->execute(); ?>
Im Vergleich zum ersten Beispiel wird bei einer existierenden Zeile nur 'field1' mit $alternate1 aktualisiert. Existiert die Zeile nicht, werden die Felder gemäß fields() gesetzt. Die Methode updateFields()
akzeptiert entweder ein assoziatives Array oder zwei numerische Arrays für Felder und Werte, die in der gleichen Reihenfolge stehen müssen.
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->expression('field1', 'field1 + :inc', array(':inc' => 1)) ->execute(); ?>
In diesem Beispiel wird bei einer existierenden Zeile das Feld 'field1' um 1 erhöht. Das ist nützlich für Zähler-Updates bei Ereignissen. Existiert die Zeile nicht, werden die Felder 'field1' und 'field2' mit den angegebenen Werten gesetzt. Die Methode expression()
kann für jedes Feld aufgerufen werden, um einen SQL-Ausdruck zu definieren. Parameter sind Feldname, SQL-Ausdruck und optional ein Array mit Platzhalterwerten.
Es ist nicht notwendig, dass das Feld in expression()
auch in fields()
definiert ist.
Einschränkung bei Updates
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateExcept('field1') ->execute(); ?>
Die Methode updateExcept()
akzeptiert entweder ein Array oder mehrere Felder als Parameter. Felder, die in updateExcept()
angegeben sind, werden bei existierenden Zeilen nicht aktualisiert. Im Beispiel wird bei existierender Zeile nur 'field2' mit $value2 gesetzt, 'field1' bleibt unverändert. Existiert die Zeile nicht, wird auch 'field1' auf $value1 gesetzt.
Priorität der Methoden
Das API erlaubt es, Anfragen so zu definieren, dass sie keinen Sinn ergeben. Um Fehler zu minimieren, gelten folgende Regeln:
- Felder, die mit
expression()
gesetzt werden, haben Vorrang vorupdateFields()
undupdateExcept()
. - Felder, die in
updateFields()
definiert sind, werden vonupdateExcept()
ignoriert. - Nur Felder, die in
updateFields()
angegeben sind, werden bei existierenden Zeilen aktualisiert. Andere Felder bleiben unverändert.
Es ist möglich, sinnlose Anfragen zu definieren, dies sollte vermieden werden.