Werken met de database in Drupal 7 – les 11 – Merge-query’s (MERGE)
Merge-query’s zijn een speciaal hybride type query. Hoewel de syntaxis voor dergelijke query’s werd gedefinieerd in de SQL-standaard van 2003, ondersteunt vrijwel geen enkele database deze exacte syntaxis. De meeste databases bieden echter een alternatieve manier om vergelijkbare functionaliteit te bereiken via hun eigen specifieke syntaxis. De merge-query builder in Drupal abstraheert dit concept in een objectstructuur, zodat de query per database kan worden gecompileerd volgens haar eigen implementatie.
In het algemeen is een merge-query een combinatie van een INSERT- en een UPDATE-query. Als aan een bepaalde voorwaarde wordt voldaan — bijvoorbeeld wanneer een record met een bestaande sleutel aanwezig is — wordt de ene query uitgevoerd. Zo niet, dan wordt de andere uitgevoerd. In de meeste gevallen is dit conceptueel gelijk aan:
<?php if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) { // Voer een update uit met WHERE id = $id } else { // Voer een insert uit, waarbij $id wordt ingevoegd voor het veld id } ?>
De daadwerkelijke implementatie verschilt van database tot database. Hoewel merge-query’s conceptueel atomaire bewerkingen zijn, kunnen ze in de praktijk wel of niet echt atomair zijn, afhankelijk van de database-implementatie. In MySQL bijvoorbeeld is de merge-actie atomaire, maar in het bovenstaande voorbeeld niet.
De meest algemene vorm van een merge-query ziet er als volgt uit:
Eenvoudig voorbeeld
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->execute(); ?>
In dit voorbeeld werken we met de tabel example. We definiëren één sleutelveld name met de waarde $name. Daarnaast definiëren we een array met waarden die moeten worden ingesteld.
Als er al een record bestaat waarin name gelijk is aan $name, dan worden de velden field1 en field2 bijgewerkt met de opgegeven waarden. Als er nog geen record bestaat, wordt een nieuw record aangemaakt met name = $name en de waarden $value1 en $value2 voor de andere velden.
Voorwaardelijke updates
In sommige gevallen wil je verschillende waarden instellen, afhankelijk van of een record dat via key() wordt bepaald, al bestaat. Er zijn twee manieren om dit te doen:
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateFields(array( 'field1' => $alternate1, )) ->execute(); ?>
Het bovenstaande voorbeeld werkt net als het eerste, behalve dat als het record al bestaat, het veld field1 wordt bijgewerkt met de waarde $alternate1. Als het record nog niet bestaat, wordt het aangemaakt met de waarden field1 = $value1 en field2 = $value2. De methode updateFields() accepteert één associatieve array of twee numerieke arrays (één voor velden en één voor waarden) die in dezelfde volgorde moeten staan.
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->expression('field1', 'field1 + :inc', array(':inc' => 1)) ->execute(); ?>
In dit voorbeeld wordt, als het record al bestaat, field1 verhoogd met 1. Dit is handig voor “teller-query’s”, waarbij een teller telkens moet worden opgehoogd bij een bepaalde gebeurtenis. Als het record niet bestaat, worden de velden field1 en field2 ingesteld op hun standaardwaarden.
Merk op dat de methode expression() meerdere keren kan worden aangeroepen — één keer per veld waarvoor je een expressie wilt instellen. De eerste parameter is de veldnaam, de tweede is een SQL-fragment dat de berekening of bewerking beschrijft, en de derde (optionele) parameter is een array met placeholders die in de expressie worden gebruikt.
Het veld dat in expression() wordt gebruikt, hoeft niet noodzakelijkerwijs ook in fields() te staan.
Beperkte update
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateExcept('field1') ->execute(); ?>
De methode updateExcept() accepteert een array van veldnamen of meerdere veldnamen als losse parameters. De velden die in updateExcept() zijn opgegeven, worden niet bijgewerkt als het record al bestaat. Dus als een record met name = $name al bestaat, wordt alleen field2 bijgewerkt met $value2 en blijft field1 ongewijzigd. Als het record nog niet bestaat, worden beide velden ingesteld met hun waarden.
Prioriteit
Het bovenstaande API maakt het mogelijk query’s te definiëren die in sommige gevallen onlogisch kunnen zijn. Bijvoorbeeld wanneer een veld tegelijkertijd wordt uitgesloten met updateExcept() en een expressie krijgt toegewezen. Om fouten te minimaliseren gelden de volgende regels:
- Als een veld een expression() heeft, heeft dit voorrang boven update() en updateExcept().
- Als een waarde is opgegeven in zowel update() als updateExcept(), wordt deze genegeerd.
- Als waarden zijn opgegeven in update(), worden alleen die velden bijgewerkt wanneer het record al bestaat. Velden die niet in update() zijn opgenomen, worden overgeslagen.
Let op: het is nog steeds mogelijk om query’s te definiëren die geen logisch resultaat hebben, dus controleer altijd de consistentie van je code.