Pagine

domenica 26 maggio 2013

SQL Server 2012 : Viste, problemi e soluzioni....

Comodissime le viste, non c'è che dire, salvo il caso in cui vadano in errore, non ci restituiscano tutti i campi che ci aspetteremmo o peggio ancora, ci restituiscano campi con valori incoerenti.
Se create correttamente sono un ausilio perfetto per reperire i dati in modo semplice e performante.... Sempre che si mettano in opera alcuni semplicissimi accorgimenti.

Creo un db di esempio

Use Master
Go

create database TestMetaData
Go 

Use TestMetaData
Go


Create table TestTable1
(
 T1id int
 ,T1campo1 varchar(25)
 ,T1campo2 varchar(25)
 ,T1campo3 varchar(25)
)

Create table TestTable2
(
 T2id int
 ,T2campo1 varchar(25)
 ,T2campo2 varchar(25)
 ,T2campo3 varchar(25)
)


;with cte as (Select 1 Riga union all Select 1)
 ,cte4 as (select A.Riga from cte A, Cte B)
 ,results as (select Row_number() Over(Order by Riga)Riga from cte4)
Insert into TestTable1 
Select 
 Riga
 ,'Campo1 - ' + Cast(Riga as varchar(5)) 
 ,'Campo2 - ' + Cast(Riga as varchar(5))
 ,'Campo3 - ' + Cast(Riga as varchar(5))
from results

;with cte as (Select 1 Riga union all Select 1)
 ,cte4 as (select A.Riga from cte A, Cte B)
 ,results as (select Row_number() Over(Order by Riga)Riga from cte4)
Insert into TestTable2 
Select 
 Riga
 ,'Campo1 - ' + Cast(Riga as varchar(5)) 
 ,'Campo2 - ' + Cast(Riga as varchar(5))
 ,'Campo3 - ' + Cast(Riga as varchar(5))
from results

Create View TestView
as
Select * from dbo.TestTable1
Go

Caso 1 : La vista va in errore dopo avere modificato una tabella

Creo ora una vista denominata TestViewDropColumn che restituisce tutte le colonne e tutte le righe della tabella TestTable1

Create View TestViewDropColumn
as
Select * from dbo.TestTable1
Go

Come vedete l'ho creata con una Select *, mi aspetto quindi che tutte le colonne della tabella vengano restituite dalla vista, ed in effetti è così...



Cosa succede se elimino una colonna dalla tabella TestTable1 dopo aver creato la vista e faccio una query sulla vista stessa?

Alter table dbo.TestTable1 drop column T1campo3
GO
Select * from TestViewDropColumn



Succede che la vista va in errore.....

Caso 2 : La vista non restituisce tutte le colonne contenute in una tabella

Creo una vista sulla tabella dbo.TestTable2

Create View TestViewAddColumn
as
Select * from dbo.TestTable2
Go

Anche in questo caso la vista è creata con Select *.
Aggiungo ora una colonna di tipo int e autoincrementate alla tabella dbo.TestTable2

Alter table dbo.TestTable1 add NewField int identity

Vediamo cosa succede se eseguo una select sulla vista e poi sulla tabella



In questo caso la vista non restituisce la nuova colonna mentre la seconda select, direttamente sulla tabella, funziona alla perfezione.....

Caso 3 : La vista restituisce dati incongruenti

In questo caso creo una vista contenente una join tra dbo.TestTable1 e dbo.TestTable2

Create View TestViewJoin
as
Select T1.*, T2.* 
 from dbo.TestTable1 T1
 inner join dbo.TestTable2 T2 on T1.T1id = T2.T2id 
Go

Aggiungo una nuova colonna alla tabella dbo.TestTable1

Alter table dbo.TestTable1 add ComputedColumn as (T1campo1 + T1Campo2)

Eseguo la vista

Select * from TestViewJoin

ed ecco il risultato



Come vedete la vista non da errore ma restituisce i valori in modo totalmente errato.
Questo secondo me è il caso peggiore perché rischia di compromettere in modo devastante e totalmente imprevedibile le applicazioni che fanno riferimento alla vista.
Il valore "Campo1 - 4Campo2 - 4" è il valore relativo alla colonna "ComputedColumn" aggiunta alla tabella dbo.TestTable1, non è di certo il valore della colonna T2id della tabella dbo.TestTable2.

Come facciamo a porre rimedio ai problemi ?

In modo semplice, richiamando la stored procedure di sistema sp_refreshview che aggiorna i metadati della vista specificata.
Nel mio caso devo aggiornare tutte 3 le viste create per risolvere i problemi

Exec sp_refreshview TestViewDropColumn
Exec sp_refreshview TestViewAddColumn
Exec sp_refreshview TestViewJoin

Se rieseguo le select dalle viste tutto funzionerà in modo corretto

Select * from TestViewDropColumn
Select * from TestViewAddColumn
Select * from TestViewJoin




Come vedete ora le tre viste restituiscono i risultati aspettati.

Per evitare che i casi d'esempio si verifichino abbiamo 3 strade:

1. Eseguire una ALTER VIEW a seguito di aggiornamenti delle tabelle referenziate
2. Eseguire un refresh con la sp_refreshview a seguito di aggiornamenti delle tabelle referenziate
3. Creare le viste con l'opzione SCHEMABINDING che impedisce qualunque modifica strutturale alle tabelle referenziate dalla vista.

Ciao

Luca

Nessun commento:

Posta un commento