Programmazione.it v6.2
Ciao, per farti riconoscere devi fare il login. Non ti sei ancora iscritto? Che aspetti, registrati adesso!
Info Pubblicità Collabora Autori Sottoscrizioni Preferiti Bozze Scheda personale Privacy Archivio Libri Corsi per principianti Chat Forum
Il MERGE con SQL Server 2008
Scritto da Ciro Fiorillo il 14-10-2008 ore 11:56
Un'operazione molto frequente nei processi Extract Transform Load (ETL) tipici del popolamento dei data warehouse è quella di MERGE, anche detta UPSERT: essa consiste nell'eseguire una funzione diversa, di INSERT piuttosto che di UPDATE, a seconda che il record da caricare sia già presente o meno all'interno della tabella di destinazione.

Consideriamo ad esempio il popolamento periodico di un data warehouse relativo a dei contratti telefonici: in ogni caricamento successivo al primo occorrerà verificare, per ogni contratto in esame, se lo stesso è già presente in tabella; in tal caso occorrerà aggiornarne i dati eventualmente variati dall'ultimo caricamento, quali ad esempio il piano tariffario prescelto, piuttosto che l'indirizzo di fatturazione, mentre in caso contrario bisognerà inserire i dati del nuovo contratto; in alcuni casi sarà necessario inoltre provvedere anche alla cancellazione di quei contratti non più in vigore, e quindi non più presenti nella base dati di origine, ma ancora memorizzati in quella di destinazione.

Fino ad oggi tale compito era affidato agli sviluppatori, che spesso incapsulavano una logica di questo tipo all'interno di più o meno complesse stored procedure Transact SQL; con l'avvento di SQL Server 2008, Microsoft ha introdotto l'istruzione MERGE, che consente di attivare in un unico comando l'insieme delle operazioni dell'esempio precedente, con evidenti vantaggi in termini di tempo ed una minore possibilità di errori da parte dello sviluppatore.

Per utilizzare il nuovo comando è necessario specificare le tabelle/query di origine e di destinazione da relazionare con una join e il tipo di azione da mettere in atto per i record, che sono presenti in entrambe e per quelli presenti solo nell'una o nell'altra. Grazie all'utilizzo della clausola di OUTPUT, introdotta in SQL Server 2005, è inoltre possibile ottenere le informazioni che si desiderano sui record inseriti, modificati o cancellati dall'esecuzione del MERGE.

La sintassi completa del comando è consultabile su MSDN; vediamo subito un esempio, tratto da un articolo introduttivo sul nuovo statement opportunamente commentato:
  1. <span style="font-size:1.0em">
  2. MERGE 
  3. -- tabella DESTINAZIONE
  4. dbo.CricketTeams AS TARGET
  5. -- tabella SORGENTE
  6. USING dbo.CricketTeams_UpdatedList AS SOURCE                   
  7. -- criterio di join
  8. ON (TARGET.CricketTeamID = SOURCE.CricketTeamID)          
  9.  
  10. -- cosa fare quando il record è presente in entrambe le tabelle
  11. WHEN MATCHED 
  12. -- ...con eventuali condizioni aggiuntive
  13. AND TARGET.CricketTeamContinent  SOURCE.CricketTeamContinent 
  14. OR TARGET.CricketTeamCountry  SOURCE.CricketTeamCountry THEN 
  15. UPDATE SET TARGET.CricketTeamContinent = SOURCE.CricketTeamContinent,
  16. TARGET.CricketTeamCountry = SOURCE.CricketTeamCountry               
  17.  
  18. -- cosa fare quando il record NON è presente nella tabella DESTINAZIONE
  19. WHEN NOT MATCHED THEN                                                                           
  20. INSERT (CricketTeamID, CricketTeamCountry, CricketTeamContinent)            
  21. VALUES (SOURCE.CricketTeamID, SOURCE.CricketTeamCountry, SOURCE.CricketTeamContinent)
  22.  
  23. -- cosa fare quando il record NON è presente nella tabella SORGENTE
  24. WHEN NOT MATCHED BY SOURCE THEN                                                   
  25.     DELETE
  26.  
  27. -- clausola di OUTPUT, restituisce informazioni sulle operazioni eseguite
  28. OUTPUT $action,       
  29.      INSERTED.CricketTeamID AS SourceCricketTeamID,
  30.      INSERTED.CricketTeamCountry AS SourceCricketTeamCountry,
  31.      INSERTED.CricketTeamContinent AS SourceCricketTeamContinent,
  32.      DELETED.CricketTeamID AS TargetCricketTeamID,
  33.      DELETED.CricketTeamCountry AS TargetCricketTeamCountry,
  34.      DELETED.CricketTeamContinent AS TargetCricketTeamContinent;
  35. </span>
Notiamo la presenza delle tre possibili operazioni da eseguire a seconda dell'esistenza o meno nella tabella CricketTeams (destinazione) degli elementi della tabella CricketTeams_UpdatedList (sorgente); non è necessario specificare sempre tutte e tre le opzioni, ma è ovviamente obbligatorio definirne almeno una.

Due dettagli importanti da notare sono il carattere di punto e virgola con cui occorre sempre terminare l'istruzione MERGE, e la colonna $action, definita come varchar(10), che conterrà i valori INSERT, UPDATE o DELETE per ogni riga della tabella di destinazione rispettivamente aggiunta, modificata o cancellata. L'utilizzo del comando MERGE mantiene inoltre un comportamento coerente con quanto avviene per le operazioni base in cui lo stesso viene scomposto, per cui verranno scatenati gli eventuali trigger definiti sulla tabella destinazione e saranno rispettati i vincoli impostati su tale tabella.

Una nota sull'utilizzo della istruzione @@ROWCOUNT che, dopo un'operazione di MERGE, restituirà il numero totale di righe inserite, modificate o cancellate nella tabella destinazione; dal punto di vista della sicurezza, inoltre, un utente deve avere i diritti di lettura sulla tabella sorgente e quelli di INSERT, UPDATE e DELETE su quella di destinazione per poter eseguire uno statement di MERGE.

Per finire, è d'obbligo dare un'occhiata a come ottimizzare le performance del nuovo nato, prima di iniziare ad utilizzare il comando MERGE per tutte le nostre necessità di UPSERT.
Precedente: Espressioni regolari, un aiuto per comprenderle
Successiva: Un nuovo sleeping state per Linux
Copyright Programmazione.it™ 1999-2009. Alcuni diritti riservati. Testata giornalistica iscritta col n. 569 presso il Tribunale di Milano in data 14/10/2002. Pagina generata in 0.964 secondi. Sito ottimizzato per Mozilla Firefox. Powered by Kyron.