Lavorare con il database in Drupal 7 – Lezione 11 – Query di unione (MERGE)
Le query di unione (MERGE) sono un tipo speciale di query ibrida. Sebbene la sintassi per queste query sia stata definita nello standard SQL 2003, di fatto non esistono database che supportano completamente tale sintassi. Tuttavia, la maggior parte dei database offre un’implementazione alternativa attraverso una propria sintassi specifica. Il costruttore di query di unione di Drupal astrae il concetto di merge in una struttura a oggetti, così che l’oggetto possa essere compilato in modo diverso a seconda del database utilizzato.
In generale, una query di unione è una combinazione di un’operazione di inserimento (INSERT) e di aggiornamento (UPDATE). Se la condizione specificata è soddisfatta — ad esempio, se esiste già una riga con una determinata chiave — viene eseguita una query; altrimenti, ne viene eseguita un’altra. Nella maggior parte dei casi, questo è equivalente al seguente codice:
<?php if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) { // Esegui un aggiornamento usando WHERE id = $id } else { // Esegui un inserimento, aggiungendo $id come id } ?>
L’implementazione effettiva varia da database a database. Anche se le query di tipo MERGE sono concettualmente operazioni atomiche, potrebbero esserlo o meno a seconda del database. Ad esempio, in MySQL il merge è effettivamente atomico, ma nel codice mostrato sopra non lo sarebbe.
La forma più comune di una query di unione è la seguente:
Semplice da usare
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->execute(); ?>
In questo esempio operiamo sulla tabella example. Definiamo un campo chiave name con il valore $name
e impostiamo un array di valori da inserire o aggiornare.
Se una riga con il valore $name
nel campo name
esiste già, i campi field1
e field2
verranno aggiornati con i valori corrispondenti. Se tale riga non esiste, verrà creata una nuova riga con name = $name
, field1 = $value1
e field2 = $value2
.
Impostare condizioni diverse
In alcuni casi potresti voler impostare valori diversi a seconda che il record definito da key()
esista o meno. Esistono due modi per farlo.
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateFields(array( 'field1' => $alternate1, )) ->execute(); ?>
Questo esempio si comporta come il precedente, ma se il record esiste già, il campo field1
verrà aggiornato con $alternate1
. Se invece il record non esiste, verrà creato con field1 = $value1
e field2 = $value2
. Il metodo updateFields() accetta un array associativo o due array numerici — uno per i campi e uno per i valori — che devono essere nello stesso ordine.
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->expression('field1', 'field1 + :inc', array(':inc' => 1)) ->execute(); ?>
In questo esempio, se il record esiste già, il campo field1
verrà incrementato di 1. Questa funzionalità è utile per i cosiddetti “query contatori”, quando è necessario aumentare un valore ogni volta che si verifica un determinato evento. Se il record non esiste, verrà creato con i valori field1
e field2
impostati come specificato. Nota che il metodo expression() può essere chiamato più volte, una per ciascun campo, per impostare un’espressione SQL nel caso in cui il record esista già. Il primo parametro è il nome del campo, il secondo è il frammento SQL dell’espressione e il terzo (opzionale) è un array di segnaposto con i relativi valori.
Non è necessario che un campo usato in expression() sia anche presente in fields().
Aggiornamento limitato
<?php db_merge('example') ->key(array('name' => $name)) ->fields(array( 'field1' => $value1, 'field2' => $value2, )) ->updateExcept('field1') ->execute(); ?>
Il metodo updateExcept() accetta un array di campi o un elenco di campi separati da virgola. I campi specificati in updateExcept()
non verranno aggiornati se il record esiste già. Quindi, se il record con name = $name
esiste, il campo field2
verrà aggiornato a $value2
, mentre field1
verrà ignorato e manterrà il valore attuale. Se invece il record non esiste, field1
sarà impostato a $value1
.
Priorità
L’API mostrata sopra consente di definire query che in alcuni casi potrebbero risultare incoerenti. Ad esempio, si potrebbe escludere un campo da updateExcept()
e contemporaneamente definirne un’espressione in expression()
. Per ridurre al minimo tali errori, si applicano le seguenti regole:
- Se un campo ha una definizione in expression(), questa ha la priorità su update() e updateExcept().
- Se un valore è definito in update(), l’impostazione di updateExcept() viene ignorata.
- Se i valori sono definiti in update(), solo questi campi verranno modificati quando il record esiste già. I campi non inclusi in update() non verranno toccati.
Ricorda che è comunque possibile definire query che non abbiano molto senso logico.