L'altro giorno mi è successa una cosa apparentemente strana. Una di quelle cose che quando capitano, se non ci si pensa abbastana, vengono definite dalla frase "MA E' IMPOSSIBILE!!"
.
Vi illustro la situazione:
una semplice stored procedure che esegue una select su di una tabella.
Procediamo per step.
Come prima cosa, accedo su di un database con il mio utente, che a sua volta ha
dbo come default schema.
SQL Server accede agli oggetti con la seguente logica:
1) Tenta di accedere allo schema di default
2) Tenta di accedere all'oggetto sotto
dbo.
Prendiamo la seguente query di esempio:
SELECT * FROM Movimenti
Per accedere alla tabella
Movimenti con un ipotetico utente
User1 che ha come default schema
Schema1, SQL server tenterà prima di accedere all'oggetto
Schema1.Movimenti e poi, se non è stato trovato, a
dbo.Movimenti.
Detto questo (ricordiamoci che il mio utente ha dbo come schema di default) ricostruiamo lo scenario che mi si è presentato.
Creo lo schema non di default per l'utente, chiamandolo
MioSchema
-- creo lo schema
CREATE SCHEMA MioSchema
---------------------------------------------------------------
Successivamente creo due oggetti con lo stesso nome:
-- creo due tabelle con lo stesso nome, ma con schema diverso
---------------------------------------------------------------
CREATE TABLE dbo.Eventi
(
IDEvento int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
Evento varchar(100)
)
CREATE TABLE MioSchema.Eventi
(
IDEvento int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
Evento varchar(100)
)
INSERT dbo.Eventi (Evento) VALUES ('Gran premio di formula 1')
INSERT dbo.Eventi (Evento) VALUES ('ATP Tour golf')
INSERT dbo.Eventi (Evento) VALUES ('Tennis Master Series')
INSERT dbo.Eventi (Evento) VALUES ('Campionato di Sci')
INSERT dbo.Eventi (Evento) VALUES ('Mondiali di nuoto')
INSERT MioSchema.Eventi (Evento) VALUES ('SERIE A Tim')
INSERT MioSchema.Eventi (Evento) VALUES ('Premier League')
INSERT MioSchema.Eventi (Evento) VALUES ('Liga')
INSERT MioSchema.Eventi (Evento) VALUES ('Bundesliga')
INSERT MioSchema.Eventi (Evento) VALUES ('Ligue')
---------------------------------------------------------------
Ora, lanciando una selezione dell'intera tabella omettendo lo schema otterremo il seguente risultato:
SELECT * FROM Eventi
Questo è il risultato che ci si aspettava, poichè il primo oggetto ad essere letto è quello identificato da
dbo.Eventi poichè dbo è il default schema.
Rilanciando la stessa query, utilizzando un utente che ha come default schema
MioSchema (creato all'inizio), il risultato sarà:
Fino a qui tutto ok. Ma ora passiamo a quello che mi ha decisamente ingannato.
Creiamo la seguente stored procedure:
CREATE PROCEDURE MioSchema.proc_Eventi
AS
SET NOCOUNT ON;
SELECT * FROM Eventi
GO
Che ci si aspetta? Dati dalla tabella su dbo? dalla tabella di
MioSchema?
Erroneamente, e ci sono proprio cascato, mi aspettavo dati da
dbo. Ma non è così. I BOL lo evidenziano con la seguente frase passatami da
Lorenzo:
"Inside a stored procedure, object names used with statements (for example,
SELECT or INSERT) that are not schema-qualified default to the schema of the
stored procedure."In poche parole.. i dati tornati dalla stored procedure in esame sono proprio quelli della tabella
MioSchema.Eventi. Questo a prescindere dal default schema del mio utente. Gli oggetti indicati senza schema nella stored proc vengono considerati come oggetti dipendenti dallo stesso schema della stored, in casi di ambiguità.
Nella sitauzione reale, questo problema si è verificato dopo il porting di un database SQL Server 2000 verso la versione 2005. Il mio collega Marco ha aggiunto uno schema per aggiungere nuovi oggetti e non appena si è trovato questa situazione di ambiguità (due viste con lo stesso nome ma diverso schema), si è subito reso conto che qualcosa non andava. I dati che si è trovato di fronte non erano quelli che si aspettava.
La prima operazione eseguita per capire dove stesse il problema (sinceramente non ci abbiamo pensato subito) è stata quella di copia-incollare il contenuto della stored procedure in una nuova query per poi eseguirla al di fuori dello scope della procedura. Il tutto funzionava come ci si aspettava.
Dopo i primi tentativi, siamo arrivati alla conclusione che il problema stesse proprio nello schema sotto cui è salvata la stored proc. E grazie alla frase nei BOL, abbiamo capito definitivamente il problema, anche se a questo punto non lo era più effettivamente
Questo esempio dovrebbe far capire quanto è importante seguire alcune regole molto importanti:
- leggere bene i BOL
- usare
SEMPRE la naming
NomeSchema.NomeTabella per evitare problemi legati all'ambiguità
- conseguentemente alla precedente regola, indicare anche lo schema
dbo che alcuni sicuramente omettono ancora abituati a non utilizzarlo spesso sulla versione 2000.
Personalmente utilizzo sempre la naming
NomeSchema.NomeTabella ma questo porting mi ha decisamente "fregato"
Ho deciso di postare questo caso, perchè alcuni di questi errori possono portare a considerazioni errate. Non è da tutti i giorni trovarsi una stored procedure che non torna i dati attesi ed il cui contenuto, con gli stessi parametri, fa quello che deve fare. Si tratta di quelle cose a cui subito, magari, non si pensa.. ma che è sempre bene tenere a mente. Certo è che, seguendo la naming convention, nulla di tutto ciò sarebbe accaduto. Un codice scritto chiaro vince sempre!
Stay tuned!