I sinonimi in SQL Server L`identificazione di un oggetto in SQL

I sinonimi in SQL Server
Di Gianluca Negrelli
L'identificazione di un oggetto in SQL Server necessita sempre di un
riferimento alla gerarchia che lo contiene. Al vertice della gerarchia si posiziona
il server mentre in coda troviamo l'oggetto desiderato.
In termini formali la sintassi da utilizzare è la seguente
server.database.schema_name.object_name
Nella maggior parte dei casi l'individuazione di un oggetto necessita del solo
nome (object_name) in quanto SQL Server propone come risolutore di default
del percorso, il server corrente, il database corrente e lo schema corrente.
Ecco perché una semplice selezione sulla tabella Anagrafica viene risolta sia
con la sintassi completa che con quella compressa
-- Sintassi completa
SELECT * FROM Server1.Ordini.dbo.Anagrafica
-- Sintassi compressa
SELECT * FROM Anagrafica
I sinonimi, introdotti per la prima volta in SQL Server 2005, rappresentano dei
costrutti il cui unico scopo è quello di accedere ad oggetti esistenti attraverso
alias.
In altre parole se dal Server2 è necessario selezionare i record della tabella
Anagrafica residente sul Server1 o si sta utilizzando il percorso gerarchico
completo visto poc'anzi, è possibile accorciare il testo necessario mappando
attraverso un sinonimo la tabella. Il risultato sarà quello di poter accedere alla
tabella Anagrafica con una sintassi più compatta.
Un po' di pratica
Per capire quali siano le caratteristiche operative dei sinonimi dobbiamo
immaginare un ambiente su cui poter effettuare i nostri test.
Prevediamo quindi una struttura che si componga di due database: nel primo
saranno contenuti i dati consolidati mentre nell'altro quelli in lavorazione.
La scelta di usare due database separati per le diverse tipologie di dati
piuttosto che tabelle distinte residenti in uno stesso database, non è
infrequente e rappresenta un buon sistema per mantenere divise anche le
logiche di lavoro dei due ambienti.
Il nostro primo database conterrà i dati del gestionale aziendale,
completamente gestito attraverso un'applicazione di back office, mentre il
secondo conterrà gli ordini dei nostri clienti che si trovino in una fase di
lavorazione non definitiva.
Una volta che un ordine viene consolidato, una procedura di importazione lo
migra al database principale e lo elimina dal database di lavoro.
Chiamiamo i nostri database rispettivamente Gestionale e Ordini.
Il database Gestionale contiene, tra le altre cose, l'anagrafica dei nostri clienti
che ritroviamo nell'omonima tabella. La tabella Anagrafica viene giornalmente
integrata e modificata dall'ufficio amministrazione e ogni singolo cambiamento
deve essere subito reso disponibile all'applicazione che si occupa della gestione
del database Ordini.
A questo punto è necessario decidere se creare una tabella Anagrafica
speculare nel database Ordini, con i conseguenti problemi di sincronizzazione,
o se riferirsi direttamente alla tabella Anagrafica originale.
Naturalmente, ai fini della nostra trattazione, la scelta ricadrà sul puntamento
diretto.
Eseguiamo una query di selezione per vedere come l'accesso da un database
agli oggetti di un altro database sia cosa semplice.
SELECT * FROM Gestionale.dbo.Anagrafica
Gestionale è naturalmente il nome del nostro database, dbo è lo schema con
cui accediamo all'oggetto e Anagrafica è la tabella. In questo caso, risiedendo i
due database sullo stesso server, non c’è bisogno di specificare anche il vertice
della gerarchia di accesso, che è l'identificatore del server
(server.database.schema_name.object_name), perché SQL Server si occupa
autonomamente del completamento utilizzando il valore corrente (Server1).
Creazione di un sinonimo
La creazione di un sinonimo che risieda nel database Ordini e che punti alla
tabella Anagrafica del database Gestionale permette di accedere alla tabella
remota con una sintassi più concisa e chiara.
CREATE SYNONYM dbo.Anagrafica FOR Gestionale.dbo.Anagrafica
GO
SELECT * FROM Anagrafica
Modifica dei sinonimi
Un sinonimo non può essere modificato attraverso il costrutto ALTER ma può
essere eliminato attraverso l'istruzione DROP.
Il sinonimo definito precedentemente può essere quindi modificato solo
attraverso la sua cancellazione e ricreazione.
DROP SYNONYM Anagrafica
Vincoli
I sinonimi possono essere usati per mappare i seguenti oggetti.
 Tabelle
 Viste
 Stored procedure (sia CLR che SQL)
 Funzioni scalari, tabellari e aggregate (sia CLR che SQL)
 Procedure di filtro per repliche (Replication filter procedures)
 Extended Stored procedure
Inoltre i sinonimi possono essere utilizzati nelle seguenti istruzioni TSQL:
 SELECT
 sub-Select
 UPDATE
 INSERT
 DELETE
 EXECUTE
Un altro vincolo alla creazione dei sinonimi riguarda la nomenclatura utilizzata.
Non può essere creato un sinonimo con un nome di oggetto già esistente nel
database in quanto esso vive all'interno di uno schema e come gli altri oggetti
appartenenti ad uno schema, deve poter essere individuato univocamente.
La natura dei sinonimi fa sì che essi vengano risolti a run-time a differenza di
ogni altro oggetto esistente in SQL Server.
E' possibile infatti creare un sinonimo per un oggetto non (ancora) esistente
nel database.
Prendiamo la seguente istruzione:
CREATE SYNONYM dbo.Anagrafica2 FOR Gestionale.dbo.Anagrafica2
La creazione del sinonimo andrà a buon fine malgrado l'oggetto
Gestionale.dbo.Anagrafica2 non esista.
Naturalmente l'istruzione di selezione su Anagrafica2 ritornerà un errore.
SELECT * FROM Anagrafica2
Msg 208, Level 16, State 1, Line 1
Invalid object name 'Anagrafica2'.
La ragione di questo "strano comportamento" di SQL Server va cercata nel
modo in cui i sinonimi funzionano. Essenzialmente si tratta di costrutti che
attivano il collegamento all'oggetto puntato solo in late binding quindi al
momento della chiamata e non al momento della creazione.
Ritengo che la scelta nasca dal fatto che il sinonimo è tipicamente un legame
tra database diversi e che non si vogliano creare correlazioni troppo strette tra
entità che sono per loro natura separate. Inoltre l’assenza di legami dei
sinonimi verso gli oggetti puntati permette l’utilizzo degli stessi in task di
refactoring della struttura dati altrimenti impossibili.
Utilizzi pratici dei sinonimi
Scorciatoia
Il primo e più immediato uso che i sinonimi ispirano è senz'altro quello della
scorciatoia.
Se nel nostro codice SQL dobbiamo spesso riferirci ad un oggetto residente su
un diverso DB come ad esempio l'anagrafica del database Ordini, la creazione
di un sinonimo ci risparmia dall'imputazione continua dell'intero percorso
dell'oggetto e la nostra query di selezione si riduce alla già vista:
SELECT * FROM Anagrafica
Ma proviamo ad approfondire la questione per capire se esistono altri vantaggi
meno evidenti nell'utilizzo dei sinonimi.
Stabilizzatore
I sinonimi possono essere utilizzati come "stabilizzatori" di aree del database
che siano in una fase ancora instabile e non definitiva.
La caratteristica dei sinonimi di essere attivati solo in late binding permette di
creare una struttura di riferimenti stabile, garantita dai sinonimi stessi, cui
appoggiarsi per la consultazione dei dati, mentre la struttura di riferimento può
variare anche di molto senza per questo dover modificare altre aree del
database. Siamo cioè di fronte ad uno strumento che ci permette di costruire
un layer di astrazione che separa le logiche di interrogazione da quelle di
costruzione del database.
Pensiamo ad esempio ad un sinonimo che punti una tabella di un DB residente
su un server differente.
Uno spostamento del DB verso un'altra macchina non obbliga alla riscrittura di
tutto il codice di interrogazione ma alla distruzione e ricreazione del solo
sinonimo.
Questa caratteristica dei sinonimi permette altresì di procedere ad operazioni di
refactoring della struttura del DB senza la necessità di alterare la logica di
consultazione.
Se ad esempio la nostra tabella Anagrafica soffrisse di problemi di scarsa
normalizzazione, potrebbe essere sostituita da una serie di nuove tabelle. Il
sinonimo Anagrafica del database Ordini andrebbe a questo punto ricreato per
puntare ad una vista che rimetta assieme le tabelle normalizzate.
I chiamanti, puntando al sinonimo Anagrafica, non si accorgerebbero della
sostituzione della sorgente dei dati né della modifica della struttura del
database, né del fatto di puntare ad una vista piuttosto che alla tabella
originale.
Security
Attraverso l'uso di un sinonimo, l'oggetto puntato non è completamente
disponibile. Infatti sono precluse tutte le operazioni di modifica e/o
cancellazione dell'oggetto.
L'aggiunta di una colonna alla tabella Anagrafica funziona solo se è la tabella
originale ad essere puntata e non il sinonimo.
L'istruzione:
USE Ordini
ALTER TABLE Anagrafica ADD Test varchar(1)
ha come conseguenza il lancio dell'eccezione: "Cannot alter 'Anagrafica'
because it is not a table." mentre l'istruzione:
USE Ordini
ALTER TABLE Gestionale.dbo.Anagrafica ADD Test varchar(1)
va naturalmente a buon fine.
In altre parole l'utente che utilizza il sinonimo si ritrova implicitamente
autorizzato all'accesso alla tabella Anagrafica in selezione, inserimento e
cancellazione dei record ma non in modifica della struttura della tabella.
Va sottolineato comunque che l'utilizzo dei sinonimi come gestori di
autorizzazioni sugli oggetti non è un approccio condivisibile né auspicabile.
Prestazioni
Dal punto di vista delle prestazioni non ci sono particolari controindicazioni
nell'uso dei sinonimi.
I piani di esecuzione di SQL Server, nel caso di uso di un sinonimo, non
aggiungono nessun livello di elaborazione rispetto all'uso diretto dell'oggetto
puntato.
Un esempio d'uso
Come abbiamo visto i sinonimi sono costrutti comodi ma apparentemente mai
necessari.
Vediamo un contesto invece in cui diventano insostituibili.
Spesso a fronte di un ambiente effettivo, le nostre applicazioni devono essere
preventivamente rilasciate anche in un ambiente di test.
I database seguono di norma la suddivisione degli ambienti applicativi per cui
l'applicazione di test riferirà al database di test mentre l'applicazione effettiva
all'effettivo.
Nel caso più semplice le applicazioni, che conoscono il loro ambito di
esecuzione, si collegano ai database attraverso stringhe di connessione
differenti, coordinando di fatto l’attribuzione del database di test all’ambiente
di test e viceversa.
Ipotizziamo però un caso particolare.
La nostra applicazione è costituita di due database: Gestionale e Ordini.
Il layer dati è costituito da Stored procedure che sono presenti sia in uno che
nell'altro database.
La SP che si occupa della migrazione e consolidamento degli ordini in
compilazione verso il database Gestionale, risiede nel DB Ordini e contiene
alcune istruzioni come queste:
...
INSERT INTO Gestionale.dbo.DocumentoOrdini(Numero, Data)
SELECT '123_2010', GETDATE()
...
Già a questo livello potremmo apprezzare l'utilità di un sinonimo per accorciare
il riferimento alla tabella DocumentoOrdini residente sull’altro database, ma
procediamo con ordine.
Siccome la nostra applicazione necessita di un completo ambiente di test i
database coinvolti saranno quattro:
Gestionale e GestionaleTest, Ordini e OrdiniTest
A questo punto nasce un problema. C'è l'esigenza di mantenere la nostra SP di
migrazione perfettamente allineata nei due ambienti (e naturalmente anche in
sviluppo) per facilitare il processo di deploy dall'ambiente di testing a quello
effettivo, ma, abbiamo visto, nella SP c'è l'esigenza di puntare al database
Gestionale da parte di Ordini e GestionaleTest da parte di OrdiniTest.
Esistono varie possibilità che evitano un deploy particolare per ogni ambiente:
a) l'applicazione chiamante comunica il tipo di ambiente in cui sta eseguendo,
alla SP attraverso un parametro.
La SP di conseguenza cambierà in questo modo:
…
IF (@Ambiente = 'effettivo')
INSERT INTO Ordini.dbo.DocumentoOrdini(Numero, Data)
SELECT '123_2010', GETDATE()
ELSE
INSERT INTO OrdiniTest.dbo.DocumentoOrdini(Numero, Data)
SELECT '123_2010', GETDATE()
...
b) la SP stessa attraverso un check del nome del database corrente, riconosce
l'ambiente in cui sta eseguendo sollevando dal compito l'applicazione client.
DECLARE @Ambiente varchar(10)
IF (db_name() like '%Test')
SET @Ambiente = 'test'
ELSE
SET @Ambiente = 'effettivo'
IF (@Ambiente = 'effettivo')
...
c) senza infarcire il codice di istruzioni condizionali IF è anche possibile far
ricorso alla costruzione dinamica delle query sia nel caso di parametro passato
dall'applicazione (caso a) sia nel caso di nome del db costruito dalla SP (caso
b)
DECLARE @DBName varchar(10)
IF (db_name() LIKE '%Test')
SET @DBName = 'OrdiniTest'
ELSE
SET @DBName = 'Ordini'
EXEC ('INSERT INTO ' + @DBName + '.dbo.DocumentoOrdini(Numero, Data)
SELECT '123_2010', GETDATE()
Come si può facilmente intuire nessuna di queste proposte rappresenta una
soluzione elegante e soddisfacente, se non altro perché la gestione di ambienti
differenti diventa invasiva e sconfina all'interno delle logiche del nostro layer di
interrogazione.
Si pensi poi al caso in cui ci venga richiesto di approntare un altro ambiente
(per presentare una demo ai clienti ad esempio). Dovremmo ritoccare tutte le
SP dotate di questi artifici per implementare la nuova redirezione, aumentando
inutilmente e pericolosamente il codice necessario.
Avrete già capito che la soluzione ideale ricade invece sull'uso dei sinonimi.
Creando due sinomini sintatticamente uguali ma con puntamenti diversi ai
database Ordini e OrdiniTest avremo modo di accedere alla nostra tabella
DocumentoOrdini nello stesso identico modo sia da un database che dall'altro.
USE Ordini
CREATE SYNONYM dbo.DocumentoOrdini FOR Gestionale.dbo.DocumentoOrdini
GO
USE OrdiniTest
CREATE SYNONYM dbo.DocumentoOrdini FOR GestionaleTest.dbo.DocumentoOrdini
GO
Con questo piccolo accorgimento la nostra SP sia in ambiente effettivo che in
ambiente di test, così come in sviluppo e in demo, tornerà alla sua forma
originaria beneficiando anche della sintassi ottimizzata del sinonimo.
...
INSERT INTO DocumentoOrdini(Numero, Data)
SELECT '123_2010', GETDATE()
...
Bibliografia
http://msdn.microsoft.com/en-us/library/ms187552.aspx