SQL Server - Aspetti Avanzati PDF
SQL Server - Aspetti Avanzati PDF
SQL Server - Aspetti Avanzati PDF
29-10-2007
16:01
Pagina 1
SQL SERVER
ASPETTI AVANZATI
icrosoft SQL Server per molti versi
non solo un database. Lo si pu
considerare come un ambiente
completo che espone al programmatore
funzionalit complesse che abbracciano a 360
gradi la gestione dei dati. Come si possono
creare programmi in T-SQL? Quali sono le
modalit con cui SQL server interagisce con il
.NET framework? Come posso ottimizzare al
massimo le query verso il database? e ancora
quali sono gli strumenti di programmazione
diretta che SQL server ci mette a
disposizione? A queste e a tante altre
domande risponde questo Handbook.
Essenziale quanto diretto, Vito Vessia ci
mostra una serie di esempi concreti su come
utilizzare al meglio tutte le tecniche pi
avanzate legate al gigantesco database
server di casa Microsoft
SQL SERVER
ASPETTI AVANZATI
Vito Vessia
Frontespizio
31-08-2005
17:26
Pagina 2
Frontespizio:Frontespizio
29-10-2007
15:59
Pagina 1
SQL SERVER
ASPETTI AVANZATI
di Vito Vessia
Frontespizio:Frontespizio
29-10-2007
15:59
Pagina 2
003-012:003-006
29-10-2007
16:00
Pagina 3
SQL SERVER
ASPETTI
AVANZATI
Indice
introduzione
Scopo del volume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
Prerequisiti per la corretta fruizione del volume . . . . . . . . . . . . . .9
003-012:003-006
29-10-2007
16:00
Pagina 4
SQL SERVER
ASPETTI
AVANZATI
Indice
003-012:003-006
29-10-2007
16:00
Pagina 5
SQL SERVER
ASPETTI
AVANZATI
Indice
003-012:003-006
29-10-2007
16:00
Pagina 6
003-012:003-006
29-10-2007
16:00
Pagina 7
SQL SERVER
ASPETTI
AVANZATI
Introduzione
INTRODUZIONE
Microsoft SQL Server il sistema di database relazionale (RDBMS) prodotto e sviluppato da Microsoft. Affonda
le sue origini nel prodotto Sybase SQL Server 3 per Unix,
da cui per si fortemente evoluto e differenziato a partire dalla versione 7, che di fatto rappresenta una vera e
propria riscrittura del code base. L'obiettivo originario,
agli inizi degli anni 90, era quello di sviluppare una versione del prodotto per il sistema operativo OS/2.
Tuttavia questa decisione stata quasi da subito disattesa, seguendo la pi generale strategia di uscita adottata
da Microsoft nei confronti di OS/2, a favore del suo
Windows NT. Nel 1994 la collaborazione con Sybase non
viene rinnovata e da allora SQL Server subisce un fork di
codice notevole.
SQL Server 2005 il nome commerciale dell'ultima versione del prodotto RDBMS di Microsoft e durante tutta
la sua fase di sviluppo ha mantenuto il nome in codice
Yukon (un'area geografica del Canada). Come tutti i prodotti Microsoft, SQL Server 2005 gira solo su piattaforma
Windows e in particolare su Windows 2000 (Desktop e
Server), Windows XP e Windows Server 2003. Questo
consente al prodotto di trarre il massimo dalla piattaforma S.O. su cui si basa, permettendo una politica di fortissima integrazione (dal file system, alla gestione della
sicurezza e all'uso ottimizzato delle API di sistema), proprio perch non ha velleit di prodotto multipiattaforma. Inoltre gli consente di mantenere il look & feel dei
tipici prodotti per Windows oltre ad offrire una facilit di
installazione che non ha pari nelle versioni per Windows
di altri prodotti multipiattaforma. Ma probabilmente il
maggior vantaggio rispetto ad altri prodotto concepiti su
piattaforma Unix (praticamente tutti gli altri) la scalaI libri di ioPROGRAMMO/SQL Server aspetti avanzati
003-012:003-006
29-10-2007
16:00
Pagina 8
SQL SERVER
ASPETTI
AVANZATI
Introduzione
003-012:003-006
29-10-2007
16:00
Pagina 9
SQL SERVER
ASPETTI
AVANZATI
Introduzione
003-012:003-006
29-10-2007
16:00
Pagina 10
SQL SERVER
ASPETTI
AVANZATI
Introduzione
003-012:003-006
29-10-2007
16:00
Pagina 11
SQL SERVER
ASPETTI
AVANZATI
Introduzione
http://www.microsoft.com/downloads/details.aspx?famil
yid=06616212-0356-46a0-8da2-eebc53a68034&displaylang=en
Se vi risulta troppo complicato riportare questi indirizzi,
usata il buon vecchio Google usando le keyword SQL
Server 2005 Samples... Una volta scaricati i setup ed
installati, sufficiente aprire i file .Sql da SQL
Management Studio (o Express) ed eseguirli.
Genereranno l'intero database compresi i dati di esempio preziosi per i nostri esempi e per il vostro studio.
11
003-012:003-006
29-10-2007
16:00
Pagina 12
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 13
SQL SERVER
ASPETTI
AVANZATI
13
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 14
Capitolo 1
Le variabili vengono dichiarate nel blocco DECLARE, ciascuna variabile locale preceduta da una @ e dopo il nome segue il tipo della
variabile (secondo il sistema di tipi di SQL Server), preceduto dalla parola chiave AS. Se nella DECLARE vi sono pi variabili queste vanno
separate da virgole. Naturalmente possibile avere diversi blocchi DECLARE nello stesso blocco di codice. Le variabili possono essere usate all'interno di query (in tutte le clausole), all'interno i istruzioni INSERT, UPDATE e DELETE oltre che in blocchi di codice T-SQL di ogni
genere, come si pu intuire dall'esempio.
1.1.1 IF
La parola chiave IF consente di introdurre blocchi condizionali, come
mostrato nel seguente esempio commentato:
DECLARE
@Country AS NVARCHAR(15),
@RefEmployeeId AS INT
SET @Country = 'Italy'
--se vi sono almeno due dipendenti provenienti dal paese contenuto
--nella variabile locale @Country
IF (SELECT COUNT(*) FROM Employees WHERE Country = @Country) > 2
BEGIN
--allora viene recuperata la chiave (EmployeeId) del pi
SELECT TOP 1 @RefEmployeeId = EmployeeID
14
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 15
SQL SERVER
ASPETTI
AVANZATI
FROM Employees
WHERE Country = @Country
ORDER BY HireDate DESC
END
ELSE
BEGIN
--altrimenti se ne provvede ad inserire uno nuovo
INSERT INTO Employees
(LastName, FirstName, BirthDate, Country)
VALUES
('Vessia', 'Vito', '19740830', @Country)
--e a conservarne la chiave identity attribuita dal database attraverso
la variabile
--globale di sistema @@identity che contiene sempre l'ultima identity
assegnata
--ad una tabella del database contenente campi identity
SELECT @RefEmployeeId = @@identity
END
--stampa del risultato finale
PRINT @RefEmployeeId
1.1.2 WHILE
Si pu gestire, inoltre, il tipo costrutto condizionale WHILE che consente di ripetere un determinato blocco di codice fino a quando resta vera una certa condizione. Osserviamo il seguente esempio commentato:
DECLARE @Country AS NVARCHAR(15),
@RefEmployeeId AS INT
SET @Country = 'Italy'
--viene ripetuto il blocco nella WHILE fino a che non vi sono almeno quat
tro
--dipendenti provenienti dal paese contenuto nella variabile locale
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
15
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 16
Capitolo 1
@Country
WHILE (SELECT COUNT(*) FROM Employees WHERE Country = @Country)
<4
BEGIN
--altrimenti se ne provvede ad inserire uno nuovo
INSERT INTO Employees
(LastName, FirstName, BirthDate, Country)
VALUES
('Vessia', 'Vito', '19740830', @Country)
END
--allora viene recuperata la chiave (EmployeeId) pi recente
SELECT TOP 1 @RefEmployeeId = EmployeeID
FROM Employees
WHERE Country = @Country
ORDER BY EmployeeID DESC
--stampa del risultato finale
PRINT @RefEmployeeId
Una WHILE pu essere interrotta prima che la condizione di valutazione smetta di essere vera introducendo un'istruzione BREAK all'interno del codice stesso.
Infine SQL Server 2005 introduce una nuova e pi potente gestione
delle eccezioni. In passato era solo possibile sollevare un'eccezione
con l'istruzione RAISERROR e testando la variabile globale @@ERROR. Osserviamo il seguente esempio su Northwind:
IF (SELECT COUNT(*) FROM Employees WHERE Country = 'Italy') < 10
BEGIN
RAISERROR ('Non vi sono abbastanza dipendenti nella filiale',
--messaggio
1, --gravit
5) --stato
END
16
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 17
SQL SERVER
ASPETTI
AVANZATI
17
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 18
Capitolo 1
La gestione e l'intercettazione di errori sollevati direttamente dal sistema era un'operazione complicata basata sul vecchio approccio
alla BASIC e comunque non consentiva di annullare un errore ma
solo di intercettarlo. Osserviamo il seguente esempio:
--tentativo di inserimento di un record duplicato su Customers di Northwind
--l'operazione solleva l'errore 2627 di sistema che la violazione della
chiave primaria
INSERT INTO Customers
(CustomerId, CompanyName)
VALUES
('AROUT', 'Nuova Arout')
--viene riletto il valore di errore corrente dalla variabile globale @@ERROR
-- importante ricordare che essa viene azzerata ad ogni istruzione
e quindi
--il suo valore va riletto immediatamente dopo l'istruzione che pu causare
l'errore
IF @@ERROR = 0
BEGIN
--in caso di errore si salta alla label Fine
GOTO Fine
END
--esempio di label applicativa che gestisce il blocco di errore
Errore:
PRINT 'Si verificato un errore di inserimento'
--esempio di label applicativa posta alla fine del blocco di codice
Fine:
PRINT 'Fine operazione'
18
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 19
SQL SERVER
ASPETTI
AVANZATI
La gestione di errori rudimentali comunque riuscita ad intercettare e a gestire l'errore, ma non ad annullarne l'effetto nel blocco di codice. Proviamo a riscrivere l'esempio usando la nuova e potente gestione degli errori di SQL Server 2005 basata sul costrutto TRY CATCH:
BEGIN TRY
--blocco di codice che causa l'errore
INSERT INTO Customers
(CustomerId, CompanyName)
VALUES
('AROUT', 'Nuova Arout')
END TRY
BEGIN CATCH
--blocco di codice che gestisce l'errore
PRINT 'Si verificato un errore di inserimento'
END CATCH
PRINT 'Fine operazione'
19
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 20
Capitolo 1
I messaggi applicativi sono gli stessi dell'esempio nella vecchia maniera, ma l'errore stato annullato e l'esecuzione del resto del codice fatta salva perch si gestita applicativamente la situazione
d'errore.
20
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 21
SQL SERVER
ASPETTI
AVANZATI
dipendenti
--provenienti dal paese contenuto nella variabile locale @Country
IF (SELECT COUNT(*) FROM Employees WHERE Country = @Country) <= 2
BEGIN
--altrimenti se ne provvede ad inserire uno nuovo
INSERT INTO Employees
(LastName, FirstName, BirthDate, Country)
VALUES
(@LastName, @FirstName, @BirthDate, @Country)
END
--allora viene recuperata la chiave (EmployeeId) del pi
SELECT TOP 1 EmployeeId, FirstName, LastName, Country
FROM Employees
WHERE Country = @Country
ORDER BY EmployeeID DESC
21
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 22
Capitolo 1
Richiamare una stored procedura da codice SQL molto semplice. Ecco come richiamare la procedura appena scritta (in Figura 1.1 mostrato l'esempio in esecuzione):
EXEC GetLastestClerk 'Italy', 'Vessia', 'Vito'
1.4 LE FUNZIONI
Le funzioni hanno in SQL lo stesso significato dell'analogo termine
nella programmazione procedurale: si tratta di procedure a cui si
22
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 23
SQL SERVER
ASPETTI
AVANZATI
23
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 24
Capitolo 1
L'uso di una funzione semplice come l'uso di una stored procedure; dato che restituisce un valore scalare, pu essere adoperato direttamente con l'istruzione T-SQL PRINT che stampa un valore:
PRINT dbo.GetLatestClerkScalarFunction('Italy')
Oppure pu essere utilizzato all'interno di una query e, pi in generale, all'interno di qualsiasi blocco di codice T-SQL. Nel seguente
esempio viene usato per restituire la colonna di una query e quindi
usato nella clausola SELECT, inoltre compare anche nella clausola
WHERE come condizione:
24
Capitolo 1 (013-036):007-050
Capitolo 1
29-10-2007
16:02
Pagina 25
SQL SERVER
ASPETTI
AVANZATI
25
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 26
Capitolo 1
SELECT TOP 2 *
FROM Employees
WHERE Country = @Country
ORDER BY HireDate DESC
)
L'uso di queste funzioni molto potente perch possono essere adoperate nelle query come normali oggetti tabella o viste, come nell'esempio che segue.
SELECT *
FROM dbo.GetLatestClerkTableFunction('Italy')
Fino a SQL Server 2000, per, questa modalit d'uso era fortemente limitata dall'impossibilit di correlare in una query l'argomento
della funzione di tipo TABLE con i campi delle altre tabelle o viste in
JOIN. Era infatti possibile semplicemente possibile valori scalari dichiarati esternamente alla query o valori costanti, ma non valori di correlazione. SQL Server 2005 introduce una potente novit: l'istruzione APPLY che supera questo limite. Osserviamo il seguente esempio:
26
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 27
SQL SERVER
ASPETTI
AVANZATI
Esso va interpretato come una INNER JOIN tra Customers e la funzione GetLatestClerkTableFunction in cui la correlazione a livello
di argomento passato alla funzione. In Figura 1.3 se ne pu osservare il risultato.
1.5 CURSORI
SQL Server offre il supporto ai cursori, cio alla possibilit di non ottenere il risultato di una query come un blocco di dati unico e continuo, ma come un flusso su cui effettuare l'enumerazione, analogamente ad una for each di un linguaggio moderno ad alto livello. Piuttosto che procedere con lunghe e noiose spiegazioni teoriche, procediamo ad analizzare e studiare un esempio completo
DECLARE @Country as NVARCHAR(15)
--dichiarazione di un cursore
DECLARE EmployeesCountriesCursor CURSOR FOR
--la query su cui agir il cursore
SELECT Country
FROM Employees
GROUP BY Country
--apertura del cursore
OPEN EmployeesCountriesCursor
--recupero della prima riga del cursore
FETCH NEXT FROM EmployeesCountriesCursor
--il campo o i campi della query vanno a finire in altrettante variabili locali
dello stesso tipo;
--tali variabili vanno precedentemente dichiarate e nella INTO vanno
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
27
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 28
Capitolo 1
28
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 29
SQL SERVER
ASPETTI
AVANZATI
Osserviamo il seguente esempio d'uso della tabella temporanea apI libri di ioPROGRAMMO/SQL Server aspetti avanzati
29
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 30
Capitolo 1
30
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 31
SQL SERVER
ASPETTI
AVANZATI
INTO @Country
END
CLOSE EmployeesCountriesCursor
DEALLOCATE EmployeesCountriesCursor
Eseguiamo una interrogazione sulla tabella temporanea, analogamente a quanto faremmo con le tabelle fisiche. In Figura 1.5 possiamo osservarne il risultato.
SELECT *
FROM #NewClerks
31
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 32
Capitolo 1
impostazioni dei vari oggetti di sistema. Riportarli tutti sarebbe impossibile, ma anche riportarne una parte avrebbe poco senso vista l'eseguit dello spazio e dunque si rimanda alla documentazione ufficiale del produttore per consultare una lista esaustiva di queste informazioni. Esse verranno affrontate pi diffusamente in uno dei prossimi capitoli.
1.8 I TRIGGER
I trigger sono l'equivalente degli eventi nella programmazione tradizionale. In pratica sono porzioni di codice T-SQL che possono essere fatti eseguire in automatico dal database engine quando su una
tabella si verifica un'operazione di INSERT, UPDATE e/o DELETE. Osserviamone la sintassi:
CREATE TRIGGER [ schema.]nome_trigger
ON { nome_tabella | nome_vista }
{ FOR|AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
AS { sql_statement [ ; ] [ ...n ] }
Il trigger pu agire sul tabella o sulla vista il cui nome segue la clausola ON, pu agire su operazioni di INSERT, UPDATE e/o DELETE (lo
stesso trigger pu servire anche pi operazioni DML sulla stessa tabella o vista) e pu essere di due tipi:
G
32
" FOR, agisce prima che venga effettuata l'operazione di scrittura sulla tabella e pertanto pu essere usato per impedirne il
completamento in caso di inconsistenza del dato da scrivere;
" AFTER, agisce solo dopo che l'operazione di scrittura sulla tabella stata completata senza errori e non applicabile sulle viste;
" INSTEAD OF, agisce in sostituzione dell'operazione DML, signi-
Capitolo 1 (013-036):007-050
29-10-2007
Capitolo 1
16:02
Pagina 33
SQL SERVER
ASPETTI
AVANZATI
I trigger sono certamente molto comodi e potenti anche se a volte rendono meno comprensibile il flusso di modifica dei dati proprio perch sono asincroni e non vengono mai esplicitamente invocati dal chiamante. Inoltre rappresentano evidentemente un overhead per il sistema e quindi vanno adoperati con moderazione. Microsoft, per,
da SQL Server 2000 introduce una variante di trigger di tipo INSTEAD
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
33
Capitolo 1 (013-036):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:02
Pagina 34
Capitolo 1
OF che quindi non segue l'operazione di modifica della tabella o della vista, ma la sostituisce del tutto. Il tipico esempio d'uso costituito dalle applicazioni che non cancellano mai veramente i record, ma
si limitano ad operare cancellazioni logiche in modo da salvaguardare lo storico e poter facilmente recuperare eventuali situazione di inconsistenza dei dati a seguito di cancellazioni maldestre. L'approccio tipico di aggiungere un campo BIT Deleted nella tabella interessata dalla cancellazione logica, facendogli assumere il valore 0 quando il record valido e 1 quando il record cancellato. L'approccio tradizionale alle cancellazioni prevedeva l'uso di UPDATE (sul campo Deleted) per operare le cancellazioni logiche. Con il nuovo approccio basta introdurre un trigger INSTEAD OF DELETE che, a fronte di un'operazione di DELETE sulla riga, effettui l'UPDATE sul campo di scope Deleted.
Osserviamo, infine, un altro esempio di trigger INSTEAD OF DELETE
tratto da AdventureWorks:
ALTER TRIGGER [HumanResources].[dEmployee] ON
[HumanResources].[Employee]
INSTEAD OF DELETE NOT FOR REPLICATION AS
BEGIN
SET NOCOUNT ON;
DECLARE @DeleteCount int;
SELECT @DeleteCount = COUNT(*) FROM deleted;
IF @DeleteCount > 0
BEGIN
RAISERROR
(N'Employees cannot be deleted. They can only be marked as not
current.', -- Message
10, -- Severity.
1); -- State.
34
Capitolo 1 (013-036):007-050
Capitolo 1
29-10-2007
16:02
Pagina 35
SQL SERVER
ASPETTI
AVANZATI
35
Capitolo 1 (013-036):007-050
29-10-2007
16:02
Pagina 36
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 37
SQL SERVER
ASPETTI
AVANZATI
PROGRAMMARE
SQL SERVER IN .NET
SQL Server 2005 introduce il supporto alla scrittura di stored procedure, funzioni, trigger e tipi definiti dall'utente in un qualsiasi linguaggio
del CLR Common Language Runtime (C#, Visual Basic .NET ecc...). Questi oggetti verranno ospitati ed eseguiti all'interno di una istanza della
macchina virtuale integrata in SQL Server.
Le stored procedure, le funzioni e i trigger vengono scritti come metodi statici di classi .NET, invece i tipi definiti dall'utente sono scritti come
intere classi. Il codice cos definito viene compilato in un assembly che
viene caricato in SQL Server 2005 usando l'istruzione T-SQL CREATE
ASSEMBLY. Ogni stored procedure, funzioni, trigger e tipo viene registrato nel database con la corrispondente CREATE PROCEDURE, CREATE FUNCTION, CREATE TRIGGER, CREATE TYPE e CREATE AGGREGATE. Da quel momento essere possono essere impostate ed utilizzate
come se fossero oggetti nativi T-SQL e dunque richiamabili anche da TSQL.
La versione di .NET supportata da SQL Server 2005 la 2.0 e pertanto
Visual Studio 2005 ne supporta lo sviluppo, il caricamento e il debugging nel database. interessante rilevare che i progetti Visual Studio
2005 che producono codice .NET per SQL Server 2005 possono riferire assembly esterni, come ogni altra applicazione .NET, e che sia Visual
Studio che SQL Server offrono il supporto a questa funzionalit.
Come vedremo nel proseguo del capitolo, tutte le operazioni di caricamento degli assembly e di registrazione degli oggetti .NET in SQL Server 2005 sono effettuabili attraverso comandi T-SQL predisposti ad hoc,
pertanto possibile a costo zero costruire applicazioni .NET (il runtime
scaricabile gratuitamente) con il notepad o con qualsiasi editor di testo o con IDE gratuiti di sviluppo per .NET quali SharpDevelop o lo stesso Visual Studio 2005 Express, anch'esso gratuito. L'applicazione pu far
uso di SQL Server 2005 Express, che come abbiamo visto gratuito, e
gi in questa versione possiamo scrivere le nostre estensioni .NET per
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
37
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 38
Capitolo 2
SQL Server e farle girare nella versione Express di SQL Server 2005. Useremo, poi, i comandi T-SQL di registrazione di questi oggetti per caricarli e farli eseguire nel database della nostra applicazione. E, a costo di
risultare ripetitivo, tutto ci gratuito e quindi non dovrete spendere
nemmeno un euro per avere il tutto sul vostro PC e, poi, sul PC su cui dovr girare l'applicazione. E questo anche per utilizzi commerciali: dunque cio potrete vendere regolarmente le vostre applicazioni scritte in
C# con Visual Studio C# Express e SQL Server 2005 Express.
Tuttavia, acquistando Visual Studio 2005, almeno nella versione Professional, viene offerto il supporto alla scrittura, al debugging e al deployment automatico ed integrato di oggetti .NET all'interno di SQL
Server 2005 (qualsiasi versione, anche la versione gratuita Express). In
tal caso non avrete quasi per nulla a che fare con il codice T-SQL di installazione e manutenzione dei vostri assembly e oggetti .NET in SQL Server, ma Visual Studio far tutto il lavoro. E, soprattutto, vi offrir il supporto al debugging di questo codice.
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 39
SQL SERVER
ASPETTI
AVANZATI
39
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 40
Capitolo 2
l'interno del database risulta molto utile. Un primo esempio banale potrebbe essere la validazione del codice fiscale in una query su una tabella di anagrafiche. Ad esempio:
SELECT CodSoggetto, Cognome, Nome, CodiceFiscale,
CodiceFiscaleOK(CodiceFiscale)
FROM Soggetti
Scrive il codice di validazione del codice fiscale un'operazione possibile in T-SQL ma certamente non proprio banale e comunque e pi agevole se fatta, magari, in C#. SQL Server 2005 ci offre proprio quest'ultima possibilit. Ma esistono altri esempi in cui in T-SQL non proprio
possibile risolvere alcune classi di problemi. Si consideri, ad esempio,
la possibilit in una query di filtrare le righe da restituire in base al livello di autorizzazione dell'utente che invoca la query, magari attraverso
una maschera QBE dell'applicazione, e la verifica di autorizzazione riga per riga non pu essere effettuata direttamente da SQL, magari scrivendo la query in JOIN con una tabella autorizzativa del database, ma
debba essere un server esterno a validare riga per riga la tupla CodiceRiga, CodiceUtente. Vediamo un esempio semplice:
40
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 41
SQL SERVER
ASPETTI
AVANZATI
" la logica di autorizzazione e di filtro non centralizzata ma suddivisa tra database e codice applicativo;
" la restituzione dal database di righe che poi verranno comunque
scartate produce un inutile overhead prestazionale sia come occupazione di memoria per SQL Server che come occupazione di banda per la trasmissione del result set;
" potenzialmente presenta problemi di sicurezza perch eventuali dati non disponibili viaggiano comunque sulla rete.
Dal punto di vista del disegno dell'applicazione, poi, ne fa perdere compattezza e rende a pi fasi un'operazione come la validazione del dato
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
41
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 42
Capitolo 2
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 43
SQL SERVER
ASPETTI
AVANZATI
stesso lavoro: sfruttare la possibilit di invocare oggetti COM di automazione da T-SQL. In questo modo, costruendo un certo pattern basato su funzioni e stored procedure SQL che fungevano da proxy verso
l'oggetto COM stesso, era possibilit invocare questi oggetti esterni da
codice SQL come normali funzioni o stored procedure.
Le extended stored procedure in C++
Microsoft, gi dalle precedenti versioni SQL Server 7 e 2000, ha fornito un meccanismo per estendere le stored procedure attraverso la scrittura di nuove stored procedure non in linguaggio T-SQL, ma bens in
forma di dll binarie scritte in C++. Questa tecnologia detta Extended
Stored Procedure ed era supportata da un comodo wizard fornito dapprima con Visual C++ 6.0 e in seguito con il Visual C++ di Visual Studio .NET 2002 e 2003, dal quale possibile realizzare un esempio funzionante di extended stored procedure in pochi istanti (Figura 2.1).
Il wizard fa tutto il lavoro: prepara lo scheletro di una dll Win32, prediI libri di ioPROGRAMMO/SQL Server aspetti avanzati
43
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 44
Capitolo 2
spone la funzione di esportazione richiesta dalle extended sp e prepara gi un esempio completo del metodo che implementer la logica
della stored procedure. Osserviamone lesempio basato su quello generato dal wizard e presente nei sorgenti allegati nella cartella \extended_sp_cpp:
RETCODE __declspec(dllexport) xp_proc(SRV_PROC *srvproc)
{
DBSMALLINT i = 0;
DBCHAR colname[MAXCOLNAME];
DBCHAR spName[MAXNAME];
DBCHAR spText[MAXTEXT];
// Name of this procedure
_snprintf(spName, MAXNAME, "xp_proc");
//Set up the column names
_snprintf(colname, MAXCOLNAME, "ID");
srv_describe(srvproc, 1, colname, SRV_NULLTERM, SRVINT2,
sizeof(DBSMALLINT), SRVINT2, sizeof(DBSMALLINT), 0);
_snprintf(colname, MAXCOLNAME, "spName");
// Update field 2 "spName", same value for all rows
srv_setcoldata(srvproc, 2, spName);
srv_setcollen(srvproc, 2, static_cast<int>(strlen(spName)));
// Send multiple rows of data
for (i = 0; i < 3; i++) {
// Update field 1 "ID"
srv_setcoldata(srvproc, 1, &i);
srv_setcoldata(srvproc, 3, spText);
srv_setcollen(srvproc, 3, static_cast<int>(strlen(spText)));
44
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 45
SQL SERVER
ASPETTI
AVANZATI
Certo, con pochi clic un esempio funzionante gi pronto ed sufficiente copiare la dll generata nella directory \Binn di SQL Server ed eseguire la registrazione della nuova stored procedure in SQL Server con il comando:
sp_addextendedproc 'nome_storedprocedure', 'nomelibreria.DLL'
Si cos finalmente pronti ad utilizzare la nuova stored procedure, come mostrato in Figura 2.2.
45
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 46
Capitolo 2
Daltro canto, dalla produzione dellesempio funzionante proposto in automatico dal template del wizard alla realizzazione di una propria stored procedure da zero in C++ il passo non cos breve. Se poi si considera che non tutti conoscono il C++ o vogliono avere a che fare con
il livello di complessit di una soluzione del genere, questa soluzione presenta una complessit elevata.
46
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:03
Pagina 47
SQL SERVER
ASPETTI
AVANZATI
--identificativo dellistanza
www.ws-i.org
http://www.ws-i.org/Profiles/BasicProfile-1.1.html
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
47
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 48
Capitolo 2
In @ServerID finir il riferimento allistanza appena creata con la funzione di sistema sp_OACreate e verr utilizzato per linvocazione dei
metodi dellistanza. @OLEResult, invece, conterr il valore HResult della chiamata, infatti, in caso di valore diverso da 0, e quindi di errore,
sar possibile chiamare la funzione sp_OAGetErrorInfo che restituisce
proprio il numero e la descrizione precisa dellerrore:
sp_OAGetErrorInfo [ token_oggetto ]
[ , source OUTPUT ]
[,
description OUTPUT ]
[ , helpfile OUTPUT ]
[ , helpid OUTPUT ]
Queste informazioni finiranno nelle variabili @erroreSource ed @errorDescription Naturalmente questa eccezione pu anche essere prodotta applicativamente allinterno del nostro codice Visual Basic 6. Se listanziazione andata a buon fine, possiamo effettuare la chiamata al
metodo PrintName della nostra dll con la seguente sintassi:
sp_OAMethod token_oggetto,
nome_metodo
[ , valore_ritorno
OUTPUT ]
[ ...n ] ]
48
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 49
SQL SERVER
ASPETTI
AVANZATI
Abbiamo dichiarato le due variabili che conterranno i valori da passare al metodo PrintName, essere veranno passate con la sintassi @nomeParametro = valore, dove @nomeParametro proprio il nome del parametro cos come definito nella classe Visual Basic 6. Per il valore di
ritorno, invece, sufficiente aggiungere il postfisso OUTPUT. Si noti che
non necessario passare i parametri nellordine previsto dalla funzione da chiamare perch la convenzione di passaggio basata sui nomi
e non sulla posizione. lequivante di usare la seguente sintassi cara ai
programmatori Visual Basic:
Dim o As Object
Set o = CreateObject("SQLServerDemo.SampleClass")
Dim value as String
value = o.PrintName( prefix:= "ciro", number:= 544 )
Tornando alla nostra chiamata da T-SQL, se non si sono verificati errori, comunque tracciabili da una successiva chiamata a sp_OAGetErrorInfo, possibile leggere o stampare il valore di ritorno della funzione
VB6. Terminata la chiamata alla classe, non ci resta che rilasciare correttamente le risorse (listanza stessa della classe) al fine di evitare fastidiosi memory leak. Di questo si occupa unaltra stored procedure di
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
49
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 50
Capitolo 2
sistema, la sp_OADestroy:
sp_OADestroy objecttoken
Per effetto della location transparency, possiamo senza nessun problema effettura il debug della classe Visual Basic 6 direttamente dalla chiamata T-SQL. Sar suffciente aprire il sorgente del progetto della DLL
dallIDE di Visual Basic 6, mettere lIDE in run con lopzione Wait for
components to be created, impostare un breakpoint allinizio del metodo PrintName, effettuare la chiamata della funzione da T-SQL e attendere che Visual Basic 6 si attivi con listruzione col brealpoint i bella mostra...
Sebbene il meccanismo fin qui mostrato sia realmente molto potente e
permetta di ottenere risultati straordinari con poco sforzo, potrebbe risultare un po macchinoso data la complessit della sintassi prevista. Con
lapproccio alla C++ la chiamata ad una extended stored procedure
indistinguibile dalla chiamata ad una stored procedure tradizionale. E
allora perch non provare a semplificarsi la vita?
In SQL Server prevista la possibilit di scrivere delle proprie funzioni richiamabili da query, stored procedure e, pi in generale, da T-SQL. Ecco un esempio di user function che esegue la somma di due numeri:
CREATE FUNCTION dbo.MySum
(
@num1 float,
@num2 float
)
RETURNS float
AS
50
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 51
SQL SERVER
ASPETTI
AVANZATI
BEGIN
return @num1 + @num2
END
E allora perch non creare una funzione che incapsuli la chiamata alla nostra PrintName? Detto fatto:
CREATE FUNCTION dbo.GetNumber
(
@numToPrint int,
@prefixToPrint varchar(255)
)
RETURNS varchar(255)
AS
BEGIN
declare @OLEResult int,
@ServerID INT,
@retString varchar(8000),
@errorSource INT,
@errorDescription varchar(220)
51
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 52
Capitolo 2
@ServerID OUT
if @OLEResult<>0
BEGIN
EXEC sp_OAGetErrorInfo @ServerID, @errorSource OUTPUT,
@errorDescription OUTPUT
RETURN -1
END
EXEC @OLEResult = sp_OAMethod @ServerID, 'PrintName',
@retString OUTPUT, @number = @numToPrint, @prefix = @prefixToPrint
IF (@OLEResult <> 0)
BEGIN
EXEC sp_OAGetErrorInfo @ServerID, @errorSource OUTPUT,
@errorDescription OUTPUT
RETURN @errorDescription
END
ELSE
BEGIN
RETURN @retString
END
EXEC @OLEResult = sp_OADestroy @ServerID
IF @OLEResult <> 0
BEGIN
EXEC sp_OAGetErrorInfo @ServerID, @errorSource OUTPUT,
@errorDescription OUTPUT
RETURN @errorDescription
END
RETURN @retString
END
52
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:03
Pagina 53
SQL SERVER
ASPETTI
AVANZATI
Questa funzione cos come tutti gli altri esempi fin qui mostrati, stata creata allinterno di una versione modificata del database Northwind
di SQL Server. Un backup di questa versione accompagna i sorgenti allegati. Pertanto tutte le prove verranno effettuate puntando a questo database. Ed ecco, infatti, unesempio di chiamata alla nostra funzione in
una query:
select OrderID, CustomerID, dbo.GetNumber(OrderID, 's') as CampoCOM
from orders
Beh, tuttaltra cosa rispetto alla complessit vista in precedenza. Naturalmente la chiamata a questa funzione potr essere incapsulata in
unaltra funzione:
CREATE FUNCTION dbo.GetNumberProxy
(
@numToPrint int,
@prefixToPrint varchar(255)
)
RETURNS varchar(255)
AS
BEGIN
RETURN dbo.GetNumber(@numToPrint, 'Proxy ' + @prefixToPrint)
END
O in una stored procedure, colmando finalmente il gap rispetto alle
estendend sp:
CREATE PROCEDURE GetOrdersEx
@MinimunFreight int = 0
AS
select orderid, shipname, freight, dbo.GetNumberProxy(orderid, shipname)
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
53
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 54
Capitolo 2
as CampoFunzione
from orders where freight >= @MinimunFreight
GO
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 55
SQL SERVER
ASPETTI
AVANZATI
o persino del compilato VB6, che produce un codice binario X86 Win32
di buon livello, rispetto al codice eseguito dalla macchina virtuale .NET,
nei fatti si dimostra non vero, sia perch il Jitter (Just In Time Compiler)
di .NET fa un ottimo lavoro e sia perch l'eventuale vantaggio residuale azzerato completamente dall'overhead di accesso a SQL Server all'interno di codice XP C++ e COM rispetto alla modalit meno mediata possibile con .NET.
55
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 56
Capitolo 2
56
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:03
Pagina 57
SQL SERVER
ASPETTI
AVANZATI
57
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 58
Capitolo 2
Facciamo altrettanto con i soggetti inserendone alcuni. Siccome lo scopo dell'esempio e mostrare un esempio d'uso del CLR .NET all'interno
di SQL Server 2005 per verificare e calcolare il codice fiscale, operazione tipicamente effettuata nel codice di backend o di frontend dell'applicazione, ma difficilmente realizzabile direttamente in codice T-SQL, alcuni dei soggetti che inseriremo avranno il codice fiscale errato:
INSERT INTO Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Vessia', 'Vito', N'Bari', 'VSSVSI74M30A662W', N'M', '1974-0830 00:00:00.000')
INSERT INTO Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Rallo', 'Daniela', N'Verona', 'RLLDNL74D49L781Q', N'F', '197404-09 00:00:00.000')
INSERT INTO Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Rossi', 'Mario', N'Bari', 'RSSMRA35A01A662T', N'M', '1935-0101 00:00:00.000')
INSERT INTO Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Esposito', 'Cira', N'Foligno', 'SPSCRI70E59D653J', N'F', '197005-19 00:00:00.000')
INSERT INTO dbo.Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Vessia', 'Antonio', N'Bari', 'VSSNTN79S23A662X', N'M', '197911-23 00:00:00.000')
INSERT INTO dbo.Soggetti
(Cognome, Nome, LocalitaNascita, CodiceFiscale, Sesso, DataNascita)
VALUES ('Armani', 'Federica', N'Verona', 'RMNFRC79S50L781F', N'F',
58
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:03
Pagina 59
SQL SERVER
ASPETTI
AVANZATI
'1979-11-10 00:00:00.000')
GO
L'ultima operazione da effettuare in preparazione all'esempio, proprio l'attivazione del supporto al CLR. Di default i database SQL Server
2005 hanno questo supporto disattivato e quindi va attivato esplicitamente con la seguente sequenza di stored procedure di sistema:
sp_configure "CLR ENABLED", 1
GO
reconfigure
GO
L'eventuale disattivazione si effettua lanciando la stessa sequenza di comandi ma con il parametro CLR ENABLED impostato a 0.
Figura 2.4: Il nuovo progetto CLR SQL Server 2005 da Visual Studio 2005
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
59
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:03
Pagina 60
Capitolo 2
dio 2005 scritto in C#. Dunque procediamo alla sua creazione. Dopo
aver aperto l'IDE, creiamo un nuovo progetto Visual C# > Database
> SQL Server Project (Figura 2.4) che chiameremo CLREsempio.
A questo punto, se non presente nessun'altra connessione al database tra quelle conservate nell'IDE, verr proposta la creazione di una
60
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 61
SQL SERVER
ASPETTI
AVANZATI
61
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
31-10-2007
12:49
Pagina 62
Capitolo 2
CCC contiene tre lettere del cognome, NNN contiene tre lettere del nome, AA l'anno di nascita, M la lettera corrisponde al mese di nascita, secondo una tabella di decodifica specifica dell'algoritmo, DD il
giorno di nascita (per le donne a DD si somma 40 e quindi DD ha la
doppia valenza di indicare il giorno di nascita e il sesso), BBBB il codice belfiore del comune di nascita, una codifica per tutti i comuni italiani e i principali stati mondiali per coloro che non sono nati in Italia, X
rappresenta il codice di controllo e sta ad indicare la corretta consistenza sintattica del codice per segnalare eventuali errori di trascrizione del
codice stesso. Ed proprio su questo codice di controllo che si baser la
62
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 63
SQL SERVER
ASPETTI
AVANZATI
nostra prima funzione SQL Server 2005 CLR scritta in C#. Osserviamone il sorgente:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using System.Collections;
using Microsoft.SqlServer.Server;
namespace CLREsempio
{
public partial class Controllo
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlBoolean ControlloCodiceFiscale(string codiceFiscale)
{
return new
SqlBoolean(PartiComuni.VerificaCodiceFiscale(codiceFiscale));
}
}
}
63
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 64
Capitolo 2
contenuto in una classe costituita da parti di logica comune alle varie estensioni CLR di esempio che introdurremo in questo capitolo. Dunque riportiamo il codice di questa funzione statica:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using Microsoft.SqlServer.Server;
namespace CLREsempio
{
class PartiComuni
{
private static readonly Hashtable pari = new Hashtable();
private static readonly Hashtable dispari = new Hashtable();
private static readonly Hashtable controllo = new Hashtable();
private static readonly Regex reConsonanti =
new Regex("b|c|d|f|g|h|j|k|l|m|n|p|q|r|s|t|v|w|x|y|z",
RegexOptions.IgnoreCase);
private static readonly Regex reVocali =
new Regex("a|e|i|o|u", RegexOptions.IgnoreCase);
private static readonly Regex reLettere =
new Regex("a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z",
RegexOptions.IgnoreCase);
private static readonly Regex reCifre =
new Regex("\\d", RegexOptions.IgnoreCase);
private static readonly char[] mesi =
{ 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' };
64
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 65
SQL SERVER
ASPETTI
AVANZATI
pari.Add('1', 1);
pari.Add('2', 2);
pari.Add('4', 4);
pari.Add('5', 5);
pari.Add('6', 6);
pari.Add('8', 8);
pari.Add('9', 9);
pari.Add('A', 0);
pari.Add('C', 2);
pari.Add('D', 3);
pari.Add('E', 4);
pari.Add('G', 6);
pari.Add('H', 7);
pari.Add('I', 8);
pari.Add('K', 10);
pari.Add('L', 11);
pari.Add('M', 12);
pari.Add('O', 14);
pari.Add('P', 15);
pari.Add('Q', 16);
pari.Add('S', 18);
pari.Add('T', 19);
pari.Add('U', 20);
pari.Add('W', 22);
pari.Add('X', 23);
pari.Add('Y', 24);
pari.Add('3', 3);
pari.Add('7', 7);
pari.Add('B', 1);
pari.Add('F', 5);
pari.Add('J', 9);
pari.Add('N', 13);
pari.Add('R', 17);
pari.Add('V', 21)
pari.Add('Z', 25);
dispari.Add('0', 1);
dispari.Add('1', 0);
5);
dispari.Add('4', 9);
dispari.Add('5', 13);
15);
dispari.Add('8', 19);
dispari.Add('9', 21);
1);
dispari.Add('C', 5);
dispari.Add('D', 7);
9);
dispari.Add('G', 15);
dispari.Add('H', 17);
19);
dispari.Add('2',
dispari.Add('3', 7);
dispari.Add('6',
dispari.Add('7', 17);
dispari.Add('A',
dispari.Add('B', 0);
dispari.Add('E',
dispari.Add('F', 13);
dispari.Add('I',
dispari.Add('J', 21);
65
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 66
Capitolo 2
dispari.Add('K', 2);
dispari.Add('L', 4);
dispari.Add('O', 11);
dispari.Add('P', 3);
dispari.Add('M',
18);
dispari.Add('N', 20);
dispari.Add('Q',
6);
dispari.Add('R', 8);
dispari.Add('S', 12);
dispari.Add('T', 14);
dispari.Add('U',
dispari.Add('W', 22);
dispari.Add('X', 25);
16);
24);
controllo.Add(0, 'A');
controllo.Add(19, 'T');
controllo.Add(21, 'V');
controllo.Add(22, 'W');
controllo.Add(24, 'Y');
controllo.Add(15, 'P');
controllo.Add(17, 'R');
controllo.Add(18, 'S');
controllo.Add(20, 'U');
controllo.Add(11, 'L');
controllo.Add(13, 'N');
controllo.Add(14, 'O');
controllo.Add(16, 'Q');
controllo.Add(7, 'H');
controllo.Add(9, 'J');
controllo.Add(10, 'K');
controllo.Add(12, 'M');
controllo.Add(3, 'D');
controllo.Add(5, 'F');
controllo.Add(6, 'G');
controllo.Add(8, 'I');
dispari.Add('Y',
dispari.Add('Z', 23);
controllo.Add(1, 'B');
controllo.Add(2, 'C');
controllo.Add(4, 'E');
dispari.Add('V', 10);
controllo.Add(23, 'X');
controllo.Add(25, 'Z');
}
internal static bool VerificaCodiceFiscale(string codFiscale)
{
initializeValues();
string codfisc = codFiscale.ToUpper();
int codicecontrollo = 0;
try
{
66
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 67
SQL SERVER
ASPETTI
AVANZATI
if (codfisc.Length == 16)
{
for (int i = 0; i < 15; i++)
{
if ((i + 1) % 2 == 0)
codicecontrollo = codicecontrollo + (int)pari[codfisc[i]];
else
codicecontrollo = codicecontrollo + (int)dispari[codfisc[i]];
}
if ((char)controllo[(codicecontrollo % 26)] != codfisc[15])
return false;
else
return true;
}
else
return false;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}
67
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 68
Capitolo 2
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 69
SQL SERVER
ASPETTI
AVANZATI
La funzione usata per restituire un campo nella clausola SELECT, analogamente a quanto facciamo con le funzioni native. In Figura 2.8
possibile osservare il resultset della query di esempio. Il campo Codice-
FiscaleOK contiene i valori 0 (codice fiscale erratto) o 1 (codice fiscale corretto). In effetti, tra i record inseriti con le INSERT iniziali, vi un soggetto con codice fiscale errato.
69
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 70
Capitolo 2
sciamo queste considerazioni per sottolineare, invece, come il supporto integrato offerto da Visual Studio 2005 Professional allo sviluppo di
oggetti CLR per SQL Server 2005 contempla pure un potente debugger. In pratica SQL Server esegue il vostro codice CLR all'interno della sua
macchina virtuale .NET e voi potete effettuarne il debugging dal vostro
comodo IDE.
Come fare? Il file Test.sql contenuto nella cartella Test Scripts del progetto ci viene incontro. Infatti, riportando al suo interno il codice T-SQL in
grado di richiamare la nostra nuova funzione (ad esempio la query precedente) e semplicemente impostando un breakpoint all'ingresso della funzione C#, con la semplice pressione del tasto F5, che avvia l'esecuzione dei progetti in Visual Studio, l'IDE eseguira il codice T-SQL in
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 71
SQL SERVER
ASPETTI
AVANZATI
" recuperare tutte le righe di Soggetti secondo la condizione di filtro indicata dall'argomento IdSoggetto;
" per ciascun riga eseguire il calcolo del codice fiscale a partire dai
campi Cognome, Nome, Sesso, DataNascita e LocalitaNascita;
" verificare se il codice fiscale ricalcolato corrisponda a quello fisicamente salvato nella riga;
" restituire l'intero resultset costituito dai campi della tabella pi i due
nuovi calcolati.
71
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 72
Capitolo 2
2.5.1 Il codice
Osserviamo il codice. Anche in questo caso il metodo che soggiace alla stored procedure statico ed incorniciato da un attributo Microsoft.SqlServer.Server.SqlProcedure. Esso completamente commentato per una
pi facile comprensione, pertanto verrano evitati ulteriori commendi
fuori dal codice:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace CLREsempio
{
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetSoggettoConCodiceFiscale(int idSoggetto)
{
//viene caricata la cache dei codici belfiore
PartiComuni.CacheCodiciBelfiore();
//si apre una connessione al database per eseguire la query
//la connectionstring semplicemente "context connection=true"
72
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 73
SQL SERVER
ASPETTI
AVANZATI
73
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 74
Capitolo 2
74
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 75
SQL SERVER
ASPETTI
AVANZATI
//ricalcolo
nuovoCodiceFiscale =
PartiComuni.CalcolaCodiceFiscale(cognome, nome, sesso,
localita, dataNascita);
//valorizzazione dei campi della riga corrente; il primo parametro
// delle funzioni Set indica proprio la posizione della riga
nel resultset
record.SetInt32(0, idSogg);
record.SetSqlString(1, cognome);
record.SetSqlString(2, nome);
record.SetSqlString(3, localita);
record.SetSqlString(4, codiceFiscale);
record.SetSqlString(5, sesso);
record.SetSqlDateTime(6, dataNascita);
record.SetSqlString(7, nuovoCodiceFiscale);
record.SetSqlBoolean(8, (nuovoCodiceFiscale == codiceFiscale));
//invio della riga corrente nel resultset
pipe.SendResultsRow(record);
}
//chisura del datareader
reader.Close();
//il flusso del resultset completo e il canale viene chiuso
pipe.SendResultsEnd();
}
}
}
}
Per completezza si riportano le due funzioni statiche CacheCodiciBelfiore e CalcolaCodiceFiscale, entrambe definite nella classe PartiComuni.
Di questa classe abbiamo gi riportato una porzione in precedenza che,
dunque, ometteremo. Osserviamo la prima; essa fa uso di un'istanza di
Dictionary di tipo generic che contiene tutte le coppie chiave valore di
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
75
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 76
Capitolo 2
76
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 77
SQL SERVER
ASPETTI
AVANZATI
while (reader.Read())
{
//riempimento della cache
localita = reader.GetSqlString(0).Value;
codiceBelfiore = reader.GetSqlString(1).Value;
CodiciBelfiore[localita] = codiceBelfiore;
}
reader.Close();
}
}
La successiva funzione, invece, effettua il ricalcolo del codice fiscale a partire da tutti i parametri che lo costituiscono. Verr omessa ogni spiegazione perch non attinente allo scopo del volume:
internal static string CalcolaCodiceFiscale(
string cognomeSoggetto,
string nomeSoggetto,
string sesso,
string localitaNascita,
DateTime dataNascita)
{
initializeValues();
try
{
int cnt = 0;
string cognome = null;
for (int i = 0; i < cognomeSoggetto.Length; i++)
{
if (reConsonanti.Match(cognomeSoggetto[i].ToString()).Success)
{
cognome = cognome + cognomeSoggetto[i].ToString();
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
77
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 78
Capitolo 2
cnt++;
if (cnt == 3)
break;
}
}
if (cnt < 3)
{
for (int i = 0; i < cognomeSoggetto.Length; i++)
{
if (reVocali.Match(cognomeSoggetto[i].ToString()).Success)
{
cognome = cognome + cognomeSoggetto[i].ToString();
cnt++;
if (cnt == 3)
break;
}
}
}
for (int i = cnt; i < 3; i++)
cognome = cognome + "X";
MatchCollection totCons = reConsonanti.Matches(nomeSoggetto);
cnt = 0;
string nome = null;
int consContante = 0;
for (int i = 0; i < nomeSoggetto.Length; i++)
{
if (reConsonanti.Match(nomeSoggetto[i].ToString()).Success)
{
consContante++;
if (totCons.Count > 3 && consContante == 2)
78
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 79
SQL SERVER
ASPETTI
AVANZATI
continue;
else
{
cnt++;
nome = nome + nomeSoggetto[i].ToString();
if (cnt == 3)
break;
}
}
}
if (cnt < 3)
{
for (int i = 0; i < nomeSoggetto.Length; i++)
{
if (reVocali.Match(nomeSoggetto[i].ToString()).Success)
{
cnt++;
nome = nome + nomeSoggetto[i].ToString();
if (cnt == 3)
break;
}
}
}
for (int i = cnt; i < 3; i++)
nome = nome + "X";
string anno = dataNascita.ToString("yy");
string mese = mesi[dataNascita.Month - 1].ToString();
string giorno = sesso == "M" ? dataNascita.ToString("dd") :
(dataNascita.Day + 40).ToString();
string codicebelfiore = PartiComuni.CodiciBelfiore[localitaNascita];
string codfisc = cognome + nome + anno + mese + giorno + codice
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
79
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 80
Capitolo 2
belfiore;
codfisc = codfisc.ToUpper();
int codicecontrollo = 0;
for (int i = 0; i < 15; i++)
{
if ((i + 1) % 2 == 0)
codicecontrollo = codicecontrollo + (int)pari[codfisc[i]];
else
codicecontrollo = codicecontrollo + (int)dispari[codfisc[i]];
}
codfisc = codfisc + controllo[(codicecontrollo % 26)];
if ( VerificaCodiceFiscale(codfisc ) )
return codfisc;
else
return null;
}
catch (Exception ex)
{
//
}
return null;
}
80
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 81
SQL SERVER
ASPETTI
AVANZATI
@idSoggetto [int]
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME
[CLREsempio].[CLREsempio.StoredProcedures].[GetSoggettoConCodiceFisc
ale]
GO
81
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 82
Capitolo 2
di questo argomento mostrando la creazione di un trigger in C#. SQL Server 2005 offre la possibilit di implementare l'intera gamma di trigger
in .NET, ma per l'esempio ci concentreremo su uno molto semplice. Selezionando il progetto corrente dal pannello Solution Explorer di Visual
Studio 2005 e attivando il men contestuale con il tasto destro del mouse, sceglieremo la voce Add > Trigger. Il nostro trigger di esempio si
chiama TriggerCodiceFiscale ed di tipo FOR UPDATE,INSERT sulla tabella Soggetti. Infatti sul solito metodo statico che lo implementa presente l'attributo Microsoft.SqlServer.Server.SqlTrigger che, per, a differenza della stored procedure e della funzione, propone una serie di
parametri aggiuntivi quali il nome (Name = "TriggerCodiceFiscale"), la
tabella su cui agir (Target = "Soggetti") e la tipologia di trigger (Event
= "FOR INSERT,UPDATE").
Il trigger verificher che il codice fiscale della riga in via di inserimento
o modifica sia corretto. In caso affermativo non far nulla, diversamente sollever un'eccezione applicativa intercettabile e gestibile da codice T-SQL. Eccone il codice commentato:
using System;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace CLREsempio
{
public partial class Triggers
{
[Microsoft.SqlServer.Server.SqlTrigger(Name = "TriggerCodiceFiscale",
Target = "Soggetti", Event = "FOR INSERT,UPDATE")]
public static void TriggerCodiceFiscale()
{
82
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 83
SQL SERVER
ASPETTI
AVANZATI
83
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 84
Capitolo 2
}
}
else
{
//gestisci UPDATE
}
break;
}
SqlContext.Pipe.Send("Trigger FIRED");
}
}
}
}
2.6.1 Registrazione,
test e debug del trigger
Ancora una volta Visual Studio 2005 Professional fa tutto il lavoro di
deployment e registrazione del trigger per noi, ma operando manualmente, dopo l'inevitabile compilazione e upload dell'assembly gi viste in precedenza, dovremmo effettuare la registrazione del trigger con il seguente comando:
CREATE TRIGGER [dbo].[TriggerCodiceFiscale] ON [dbo].[Soggetti] AFTER
INSERT, UPDATE AS
EXTERNAL NAME
[CLREsempio].[CLREsempio.Triggers].[TriggerCodiceFiscale]
GO
Il trigger non direttamente invocabile come le stored procedure e le funzioni, ma viene automaticamente invocato dal database engine al verificarsi dell'evento per cui il trigger registrato. Pertanto per il test e il
debugging del nostro esempio dovremmo operare con un inserimento
di una riga nella tabella Soggetti:
84
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 85
SQL SERVER
ASPETTI
AVANZATI
85
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 86
Capitolo 2
mo un aggregato che agisca sul campo CodiceFiscale e che ritorni il codice fiscale corrispondente al Soggetto pi giovane nell'ambito del resultset. Come sappiamo, la data di nascita, nell'ambito del codice fiscale, occupa una porzione centrale del codice stesso pertanto non si pu
utilizzare un normale criterio di ordinamento alfabetico per ordinare il
risultato per data di nascita, ma bisogna procedere con l'interpretazione del codice fiscale stesso. Inoltre sappiamo anche che l'algoritmo, per
distinguere le donne dagli uomini aggiunge 40 alla porzione relativa al
giorno di nascita, aspetto che deve essere tenuto in considerazione
quando si confrontano le date di nascita per determinare il pi giovane.
Una volta messo appunto il nostro aggregato Younger saremo in grado di usarlo nel seguente modo, come mostrato in Figura 2.12:
Ma procediamo con la creazione dell'aggregato: selezionando il progetto corrente dal pannello Solution Explorer di Visual Studio 2005 e attivando il men contestuale con il tasto destro del mouse, sceglieremo la
voce Add > Aggregato. Verr proposto un scheletro di codice di un tipo
strutturato (una struct). Sulla struct presente l'attributo Microsoft.SqlServer.Server.SqlUserDefinedAggregate dotato di un costruttore a cui passeremo
il parametro Format. Questo un enumerativo che pu contenere i valori Native, UserDefined e Unknown. Il suo scopo descrivere la modalit
di persistenza dei valori all'interno dell'aggregato. Per sua stessa natura,
86
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 87
SQL SERVER
ASPETTI
AVANZATI
87
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 88
Capitolo 2
MaxByteSize = 32)]
public struct Younger : IBinarySerialize
{
//la variabile privata che conterr il codice fiscele del soggetto
pi giovane
private SqlString _cfGiovane;
//metodo di inizializzazione dell'aggregato che viene invocata all'inizio
della query in cui
//compare l'aggregato custom; nel caso specifico viene messo a null la
variabile che contiene
//il codice fiscale del pi giovane in modo che il primo elmento restituito
dalla query venga
//reso automaticamente il pi giovane a meno di confronti con i CF dei
record successivi
public void Init()
_cfGiovane = null;
}
// il metodo fondamentale dell'aggregato e viene invocato ad ogni
iterazione passando l'argomento
//passato all'aggregato nella query di esempio.
//
Es.
88
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 89
SQL SERVER
ASPETTI
AVANZATI
}
else
{
//la data di nascita del codice fiscale espressa nella forma YYMDD
// YY = ultime due cifre dell'anno di nascita
// M = lettera corrispondente al mese di nascita nella sequenza
//
{ 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' }
89
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 90
Capitolo 2
attualmente oggetto
//del confronto
if (int.Parse(dataMaschileValue.Substring(9, 2)) > 40)
{
dataMaschileValue = dataMaschileValue.Substring(0, 9) +
(int.Parse(dataMaschileValue.Substring(9, 2)) 40).ToString("00") +
dataMaschileValue.Substring(11, 5);
}
//una volta riportate le porzioni di codice fiscale relative alle date
di nascita
//rapportate al maschile, il confronto tra due date si riconduce ad
un banale confronto tra
//stringhe visto che la parte pi significa l'anno che gi
espressa in forma numerica
//facilmente confrontabile, il carattere del mese ordinato in modo
crescente al crescere del
//mese stesso e la parte del giorno un numero di due cifre non pi
soggetto al problema
//dell'addizionamento di 40 per le donne a seguito
della conversione al maschile
if (dataMaschile.Substring(6,
5).ToUpper().CompareTo(dataMaschileValue.Substring(6, 5).ToUpper()) <
0)
{
//se la data di nascita del soggetto corrente inferiore a quella
dell'attuale soggetto
//pi giovane, il primo diventa il nuovo soggetto pi giovane
_cfGiovane = Value.ToString();
}
}
}
90
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 91
SQL SERVER
ASPETTI
AVANZATI
91
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 92
Capitolo 2
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 93
SQL SERVER
ASPETTI
AVANZATI
te, infatti, si potrebbe quasi affermare che l'uso di un campo Codice Fiscale viola la Prima Forma Normale proprio perch aggrega pi informazioni nello stesso campo, ma naturalmente prendete questa affermazione come una provocazione e continuate a tranquillamente a definire i
campi codice fiscale nelle vostre tabelle... Osserviamo in Figura 2.13
l'uso del nuovo tipo come campo di una tabella.
Selezionando il progetto corrente dal pannello Solution Explorer di Visual Studio 2005 e attivando il men contestuale con il tasto destro del
mouse, sceglieremo la voce Add > Tipo definito dall'utente. Il wizard
produrr un nuovo file di sorgente nel progetto che contiene una struct.
Su di essa verr posto l'attributo Microsoft.SqlServer.Server.SqlUserDefinedType a cui viene passato l'argomento gi visto in precedenza
Format e i parametri MaxByteSize (anch'esso gi incontrato) e IsByteOrdered che esprime la modalit di ordinamento dei campi. Infatti si immagini una query su una tabella che dispone di un campo di tipo CodiceFiscale: l'attributo esprime come si dovr comportare la eventuale
clausola ORDER BY CodiceFiscale.
Nell'esempio specifico, poi, il tipo implementa anche l'interfaccia IBinarySerializer gi incontrata in precedenza nell'aggregato e l'interfaccia INullable che permette di gestire campi con valori di tipo null per il
tipo personalizzato appena introdotto. Ma si osservi il codice commentato per una maggiore comprensione:
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
93
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 94
Capitolo 2
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace CLREsempio
{
//Un UDT implementato come una normale struct di .NET
//va corredato con l'attributo SqlUserDefinedType a cui si passa il tipo
di formato di serializzazione
//come per gli aggregati, la dimensione richiesta dal tipo espressa in byte,
il tipo di ordinamento
//e altre informazioni
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined,
IsByteOrdered=true, MaxByteSize=32)]
//l'UDT pu implementare le interfacce per la gestione del NULL e per la
serializzazione custom
public struct CodiceFiscale : INullable, IBinarySerialize
{
//variabile privata membro che contiene il codice fiscale vero e proprio
//il suo valore sar l'unico salvato e recuperato effettivamente
dal database, tutti gli
//altri valori verranno semplicemente estratti e ricalcolati in base
a questo
//in questo modo si riduce l'occupazione di spazio sul database
portandola alla stessa
//occupazione di un normale campo NVARCHAR(16) in grado
di conservare un codice fiscale
private SqlString m_CF;
94
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 95
SQL SERVER
ASPETTI
AVANZATI
95
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 96
Capitolo 2
96
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 97
SQL SERVER
ASPETTI
AVANZATI
}
//gestione del recupero dell'informazione dallo stream di persistenza del
valore nel database
//l'implementazione di questo metodo necessaria perch si scelto il
formato di seralizzaizone
//Format.UserDefined in abbinamento all'implementazione
dell'interfaccia IBinarySerialize
//in realt, nell'esempio specifico, l'unica informazione da recuperare
proprio la stringa di
//16 caratteri del codice fiscale perch tutti i campi che costituiscono il
codice fiscale vengono
//calcolati ed estratti dinamicamente a partire da questa
public void Read(System.IO.BinaryReader r)
{
m_CF = r.ReadString();
CodiceBelfiore = m_CF.ToString().Substring(11, 4).ToUpper();
Sesso = int.Parse(m_CF.ToString().Substring(9, 2)) > 40 ? "F" : "M";
DataNascita = PartiComuni.CalcolaDataNascita(m_CF.ToString());
}
//gestione della serializzazione dell'informazione in uno stream binario
public void Write(System.IO.BinaryWriter w)
{
w.Write(m_CF.ToString());
}
}
}
Le caratteristiche salienti del tipo utente CodiceFiscale sono la possibilit di gestire una stringa di 16 caratteri corrispondente ad un codice
fiscale e di questa validarne la bont (metodo Parse), di permetterne la
serializzazione e la deserializzazione nella base dati (i metodi Read e
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
97
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 98
Capitolo 2
Write), di gestirne i valori Null (il metodo IsNull e il metodo statico Null
che restituisce proprio un'istanza a valore null del tipo utente) e di restituirne il codice fiscale come stringa (il metodo convenzionale ToString
derivante da System.Object).
Ma probabilmente gli elementi che rendono davvero uniche e rilevanti
le caratteristiche di questo tipo utente e che, pi in generale, mostrano
la potenza e la versatilit del meccanismo dei tipo utente CLR in SQL Server 2005, la possibilit di estrarre ed esporre del codice fiscali informazioni quali il Codice Belfiore (propriet CodiceBelfiore), la data di nascita (propriet DataNascita) e il sesso (propriet Sesso).
Esso sono fruibili con la normale sintassi <nome_campo>.<propriet>,
pertanto, se volessimo conoscere il sesso di un soggetto, dovremmo
usare la sintassi CodiceFiscale.Sesso.
A questo punto siamo pronti ad introdurre una tabella di esempio che usi questo tipo. Si tratta di una versione semplificata di Soggetti usata nel corso del capitolo: essa, infatti, presenta i soli campi Cognome, Nome e CodiceFiscale e omette i campi DataNascita, LocalitaNascita e Sesso che sono tutte ricavabili direttamente del codice fiscale e che sono esposte dal tipo utente CodiceFiscale come propriet richiamabili direttamente da query T-SQL,
98
Capitolo 2 (037-102):007-050
Capitolo 2
29-10-2007
16:04
Pagina 99
SQL SERVER
ASPETTI
AVANZATI
L'inserimento o la modifica di righe nella tabella molto semplice perch il valore del tipo CodiceFiscale viene trattato come un normale
NVARCHAR(16), come si evince dal metodo Merge implemenato nel
codice del tipo utente di esempio. Ed ecco una INSERT esemplificativa:
INSERT INTO dbo.SoggettiEx
(Cognome, Nome, CodiceFiscale)
VALUES ('Vessia', 'Vito', 'VSSVSI74M30A662W')
Nella Merge, inoltre, abbiamo osservato come venga contestualmente fatta una verifica della validit del codice fiscale appena inserito sollevando un'eccezione in caso di codice fiscale non valido, condizione possibile grazie al ricalcolo del codice/carattere di
controllo finale e del confronto con quello presente nel codice fiscale. In caso di INSERT o di UPDATE di codici fiscali non validi, verr
sollevata la seguente eccezione e l'operazione di scrittura non
verr eseguita:
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
99
Capitolo 2 (037-102):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:04
Pagina 100
Capitolo 2
La query che segue mostra tutta la potenza e la versatilit del nostro tipo utente personalizzato: dalla tabella SoggettiEx, oltre ai
campi di tipo convenzionale Nome e Cognome, vengono estratte tutta una serie di informazioni dal campo CodiceFiscale. Per
cominciare il codice fiscale stesso, invocando il metodo ToString()
dell'oggetto che, nell'implementazione che abbiamo fornito per
il tipo CodiceFiscale restituisce proprio il codice fiscale intero.
Inoltre, invocando rispettivamente le propriet DataNascita, CodiceBelfiore e Sesso del tipo, si possono estrarre direttamente le
omonime informazioni rendendo cos non necessaria la presenza
esplicita di questi campi nella tabella che contiene il tipo utente
100
Capitolo 2 (037-102):007-050
29-10-2007
Capitolo 2
16:04
Pagina 101
SQL SERVER
ASPETTI
AVANZATI
CodiceFiscale.
SELECT Cognome, Nome, CodiceFiscale.ToString() AS CodiceFiscale,
CodiceFiscale.DataNascita AS DataNascita,
CodiceFiscale.CodiceBelfiore AS CodiceBelfiore,
CodiceFiscale.Sesso AS Sesso
FROM SoggettiEx
101
Capitolo 2 (037-102):007-050
29-10-2007
16:04
Pagina 102
Capitolo 3 (103-128):007-050
Capitolo 3
29-10-2007
16:05
Pagina 103
SQL SERVER
ASPETTI
AVANZATI
" master;
" model;
" tempdb;
" msdb.
103
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 104
Capitolo 3
ca in esso vengono memorizzate sia le operazioni da compiere per ciascuna schedulazione programmata (dagli script T-SQL da eseguire, ai
comandi agli script) che la loro programmazione temporale di esecuzione. In Figura 3.1 vengono mostrate queste tabelle da SQL Server Management Studio.
Capitolo 3 (103-128):007-050
Capitolo 3
29-10-2007
16:05
Pagina 105
SQL SERVER
ASPETTI
AVANZATI
fiche che fanno le stesse operazioni ma con maggior cognizione di causa e tenendo conto di tutti gli aspetti di interdipendenza.
Di seguito viene riportato l'elenco delle principali tabelle di sistema.
da notare che ciascun oggetto del database identificato da un nome
univoco definito dall'utente al momento della creazione, qualora si tratti di un oggetto utente, e di un identificativo numerico univoco assegnato in maniera progressiva dal sistema al momento della creazione
dell'oggetto, con un normale meccanismo di Identity come accede nelle nostre tabelle utente, a riprova del fatto che i dati nelle tabelle di sistema sono trattati alla stregua dei dati nelle tabelle utente.
COLONNA
id
name
uid
type
crdate
parent_obj
DESCRIZIONE
L'identificativo univoco dell'oggetto all'interno del database
Il nome dell'oggetto del database
L'identificativo del possesso dell'oggetto
Il tipo di oggetto del database. Pu assumere i seguenti
valori:
C = vincolo check;
D = predefinito;
F = chiave esterna;
K = chiave primaria o vincolo di univocit
R = regola
RF = stored procedure di replicazione
S = tabella di sistema
TR = trigger
U = tabella ordinaria definita dall'utente
V = vista
X = stored procedure estesa
Data di creazione dell'oggetto nel database
Identificativo dell'oggetto padre. Non tutti gli oggetti hanno
un padre, ma taluni si, ad esempio una chiave primaria (tipo
K) ha per padre la tabella di cui rappresenta il vincolo. In
questo modo possibile risalire alla gerarchia di tutti gli
oggetti del database.
105
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 106
Capitolo 3
3.1.1 sysobjects
Si tratta della tabella di sistema principale di SQL Server. presente sia
nel database master che in tutti i database creati dall'utente. Contiene
una riga per ciascun oggetto presente nel database e quindi ogni tabella, ogni stored procedure. Ogni vista ed ogni altro oggetto del database sono censiti come righe diquesta tabella. Di seguito se ne riportano
i campi
Come si accennava in precedenza, tutte le tabella di sistema sono gestibili come normali tabelle create dall'utente, pertanto sar possibile effettuare la seguente query di esempio:
SELECT *
FROM sysobjects
WHERE TYPE NOT IN ('S')
106
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
16:05
Pagina 107
SQL SERVER
ASPETTI
AVANZATI
3.1.2 syscolumns
presente sia nel database master che in tutti i database creati
dall'utente. Contiene una riga per ciascuna colonne delle tabelle e delle viste presenti nel database e per ciascun parametro di
stored procedure del database. Di seguito se ne riportano i campi pi importanti:
COLONNA
id
colid
name
DESCRIZIONE
Rappresenta l'identificativo univoco dell'oggetto nel quale
compare la colonna
Rappresenta l'identificativo univoco di colonna dell'oggetto
rappresentato dal campo id
il nome della colonna vera e propria
Si osservi al seguente query che estrae l'elenco delle colonne della tabella Soggetti definita nel database di esempio Libro:
SELECT *
FROM syscolumns
WHERE id = 1973582069 'id della tabella Soggetti
107
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 108
Capitolo 3
3.1.3 sysindexes
presente sia nel database master che in tutti i database creati
dall'utente. Contiene una riga per ciascun indice. La sua interpretazione e il suo uso sono per troppo complessi, pertanto preferibile l'uso della stored procedure di sistema sp_helpindex che
permette di riportare in formato decisamente pi comprensibile
lo stato degli indici delle tabelle del database. La sua sintassi :
sp_helpindex @nome_oggetto
Ecco un'interrogazione di esempio degli indici della tabella Orders
di Northwind:
sp_helpindex 'Orders'
In Figura 3.4 possibile osservare il risultato dell'invocazione.
Figura 3.4: Gli indici della tabella Orders di Northwind via sp_helpindex
3.1.4 sysusers
presente sia nel database master che in tutti i database creati
dall'utente. Contiene una riga per ciascun account di Windows, gruppo
di Windows, login di SQL Server o ruolo di SQL Server. Di seguito
se ne riportano i principali campi: In Figura 3.5 possiamo osservare
una tipica interrogazione su questa tabella.
108
Capitolo 3 (103-128):007-050
Capitolo 3
COLONNA
uid
sid
name
29-10-2007
16:05
Pagina 109
SQL SERVER
ASPETTI
AVANZATI
DESCRIZIONE
L'identificativo numerico dell'utente, univoco nel database
L'identificativo di sistema dell'utente creatore del database
Il nome dell'utente, anche quest'ultimo deve essere univoco
nell'ambito del database
3.1.5 sysdatabases
Questa tabella di sistema appare nel solo database master e contiene
una riga per ciascuno dei database presenti nell'istanza di SQL Server.
COLONNA
dbid
name
sid
crdate
filaname
status
DESCRIZIONE
Identificativo numerico univoco del database nell'ambito
dell'istanza
Nome simbolo univoco del database nell'ambito dell'istanza
di SQL Server
Identificativo di sistema del creatore del database
Data di creazione del database
Nome del file fisico che soggiace al database
Stato del database. Sono possibili diversi stati
contemporaneamente sullo stesso database e pertanto viene
usata la seguente codifica bitwise. Eccone alcuni:
1 = autoclose (ALTER DATABASE)
109
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
COLONNA
status
Pagina 110
Capitolo 3
DESCRIZIONE
4 = select into/bulkcopy (ALTER DATABASE con l'opzione SET
RECOVERY)
8 = trunc. log on chkpt (ALTER DATABASE con l'opzione SET
RECOVERY)
16 = torn page detection (ALTER DATABASE)
32 = loading
64 = pre recovery
128 = recovering
256 = not recovered
512 = offline (ALTER DATABASE)
1024 = read only (ALTER DATABASE)
2048 = dbo use only (ALTER DATABASE con l'opzione SET
RESTRICTED_USER)
4096 = single user (ALTER DATABASE)
16384 = ANSI null default (ALTER DATABASE)
32768 = emergency mode
65536 = concat null yields null (ALTER DATABASE)
4194304 = autoshrink (ALTER DATABASE)
1073741824 = cleanly shutdown
Ancora una volta, con una normale query su questa tabella, possibile
ottenere l'elenco dei database presenti nell'istanza, magari filtrati da una
clausola di WHERE sullo stato usando l'operatore & di bitwise, come mostrato in Figura 3.6, per ottenere l'elenco dei soli database che dispongono dell'opzione concat null yields null.
110
Capitolo 3 (103-128):007-050
Capitolo 3
29-10-2007
16:05
Pagina 111
SQL SERVER
ASPETTI
AVANZATI
A volte pu rendersi necessario conoscere il solo dbid di un certo database, ad esempio per impostare un filtro di profilazione nel SQL Server Profiler o per verificare lo stato dei lock in un certo database.A tal proposito esiste la comoda funzione di sistema db_id() che, a fronte del nome simbolico del database, ne restituisce proprio il dbid come mostrato di seguito nell'esempio:
print db_id('Libro')
'risposta --> 9
3.1.6 sysdepends
presente sia nel database master che in tutti i database creati
dall'utente. Contiene una riga per ciascuna relazione di dipendenza tra tabelle, viste e stored procedure. Di seguito se ne riportano i campi pi importanti:
COLONNA
id
number
depid
depnumber
DESCRIZIONE
Identificativo numerico univoco della relazione di dipendenza
nell'ambito dell'istanza
Il numero della stored procedure
Il numero di identificazione dell'oggetto in relazione di
dipendenza con l'oggetto identificato da id
Il numero della stored procedure in relazione di dipendenza
Procediamo con il solito esempio selettivo di interrogazione sulla tabella di sistema in questione, come mostrato in Figura 3.7.
111
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 112
Capitolo 3
3.1.7 sysconstraints
presente sia nel database master che in tutti i database creati dall'utente. Contiene una riga per ciascun vincolo di integrit definito per un
oggetto del database definito con una CREATE TABLE o con una ALTER TABLE. Di seguito se ne riportano i campi pi importanti:
COLONNA
constid
id
colid
status
DESCRIZIONE
Identificativo numerico univoco del vincolo nell'ambito
dell'istanza
Identificativo univoco della tabella su cui applicato il
vincolo di integrita, come definito nella tabella sysobjects
Identificativo univoco della colonna su cui applicato il
vincolo di integrita, come definito nella tabella syscolumns
Tipologia di vincolo di integrit, come elencato di seguito:
1 = vincolo PRIMARY KEY
2 = vincolo UNIQUE KEY
3 = vincolo FOREIGN KEY
4 = vincolo CHECK
5 = vincolo DEFAULT
6 = vincolo di colonna
7 = vincolo di tabella
112
Capitolo 3 (103-128):007-050
Capitolo 3
29-10-2007
16:05
Pagina 113
SQL SERVER
ASPETTI
AVANZATI
syscolumns
sysusers
VISTA DI CATALOGO
sys.databases
sys.objects (mostra i soli oggetti utente della
tabella sysobjects)
sys.system_objects (mostra i soli oggetti di
sistema della tabella sysobjects)
sys.all_objects (corrisponde esattamente alla
tabella di sistema sysobjects)
sys.columns
sys.users
3.2.1 sys.objects
Contiene una riga per ciascun oggetto presente nel database e quindi
ogni tabella, ogni stored procedure, ogni vista ed ogni altro oggetto del
database sono censiti come righe di questa tabella. Come gi mostrato
COLONNA
name
object_id
schema_id
type
DESCRIZIONE
Nome simbolico univoco dell'oggetto nell'ambito del
database
Identificativo numerico univoco nell'ambito del database
Identificativo dello schema a cui appartiene l'oggetto
Tipo dell'oggetto, rimappato direttamente sul campo type di
sysobjects di cui, evidentemente, le condivide il dizionario di
possibili valori.
113
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 114
Capitolo 3
3.2.2 sys.columns
mappata direttamente sulla tabella di sistema syscolumns e pertanto contiene una riga per ciascuna colonne delle tabelle e delle viste presenti nel database e per ciascun parametro di stored procedure del database. Di seguito se ne riportano i campi pi importanti:
COLONNA
object_id
column_id
name
114
DESCRIZIONE
Rappresenta l'identificativo univoco dell'oggetto nel quale
compare la colonna
Rappresenta l'identificativo univoco di colonna dell'oggetto
rappresentato dal campo id
il nome della colonna vera e propria
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
16:05
Pagina 115
SQL SERVER
ASPETTI
AVANZATI
3.2.3 sys.database_principals
Contiene una riga per ciascuno degli oggetti di sicurezza presenti nel database e quindi per ciascun utente, gruppo e ruolo del database. Di seguito se ne riportano i campi pi importanti:
COLONNA
name
principal_id
type
DESCRIZIONE
il nome univoco dell'oggetto di sicurezza (principal)
Identificativo univoco dell'oggetto di sicurezza nell'ambito
del database
Tipologia di principal. Pu assumere i seguenti valori:
S = SQL user
U = Windows user
G = Windows group
A = Application role
R = Database role
C = User mapped to a certificate
K = User mapped to an asymmetric key
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
115
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 116
Capitolo 3
Osserviamo, in Figura 3.11, un esempio di interrogazione di questa Vista di Catalogo per i soli tipi R.
3.3.1 sp_help
Questa stored procedure visualizza le informazioni di uno o pi oggetti del database. Se invocata senza parametri si limita ad elencare tutti
gli oggetti di sistema, diversamente, se parametrizzata con il nome simbolico dell'oggetto da interrogare, restituir un vero e proprio report
completo di ogni informazione relativa all'oggetto in questione.
116
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
16:05
Pagina 117
SQL SERVER
ASPETTI
AVANZATI
Si osservi la Figura 3.12: essa ci mostra il risultato dell'interrogazione della tabella Soggetti attraverso la stored procedure in questione. Restituisce numerosi resultset:
G " un resultset relativo all'oggetto Soggetti stesso, corrispondente
grosso modo alla riga di sysobjects relativa a questo oggetto (infatti sono visibili il nome, l'owner, la tipologia di tabella e la data di
creazione);
G " un resultset relativo alle colonne della tabella, corrispondente
grosso modo alle righe della tabella syscolumns filtrate per l'id delI libri di ioPROGRAMMO/SQL Server aspetti avanzati
117
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
G
G
G
G
16:05
Pagina 118
Capitolo 3
la tabella Soggetti;
" un resultset relativo alla Identity impostata sul campo IdSoggetto della tabella, sempre che sia presente un identity nell'oggetto;
" un resultset relativo alla eventuale presenza di tipi rowGuid nella tabella (nell'esempio specifico non vi sono colonne di questo tipo);
" un resultset relativo al partizionamento fisico dei dati della tabella, utile qualora la tabella risulti partizionata;
" un resultset relativo agli indici definiti su questa tabella, corrispondente alle informazioni accessibili attraverso la tabella di sistema
sysindexes o alla stored procedure specifica di sistema sp_helpindex;
" un resultset realtivo alle constraint presenti nella tabella (in effetti, nel caso specifico, possibile osservare una constraint di tipo
check sul campo Sesso, che pu assumere isoli valori 'S' o 'N', una
constraint di tipo chiave primaria di tipo CLUSTERED sul campo IdSoggetto e una constraint relativa ad una foreign key del campo LocalitaNascita sul campo chiave Localita della tabella Localita dello
stesso database Libro);
" infine, un resultset che indica se la tabella usate in una o pi viste e le informazioni di dettaglio relative (nessuna informazione di
questo tipo viene restituita nell'esempio perch la tabella non viene adoperata in nessuna vista).
3.3.2 sp_depends
Questa stored procedure molto utile perch permette di verificare le
dipendenze tra tabelle, viste, trigger e stored procedure del database e
pertanto risulta molto utile quando si deve realizzare un'analisi d'impatto di una modifica di un qualsiasi oggetto del database.
Proviamo ad esaminare le dipendenze della tabella Person.Contact del
database di esempio AdventureWorks:
USE AdventureWorks
GO
118
Capitolo 3 (103-128):007-050
Capitolo 3
29-10-2007
16:05
Pagina 119
SQL SERVER
ASPETTI
AVANZATI
sp_depends 'Person.Contact'
GO
SQL Server ci moster l'elenco di tutti gli oggetti del database dipendenti da quello passato come argomento, precisandone, per ciascuno di
essi, il nome e la tipologia (tabella, stored procedure, funzione, ecc...),
come mostrato in Figura 3.13.
3.3.3 sp_helptext
Questa comoda stored procedure si limita a resituire un'informazione molto utile: il corpo testuale degli oggetti definiti interamente in T-SQL e cio stored procedure, viste, trigger e funzioni.
Si passa il nome dell'oggetto da esaminare come argomento e
la stored procedure restituisce un resultset in cui ciascuna riga
contiene una linea del corpo del T-SQL dell'oggetto, suddiviso per
line break.
Si osservi il seguente esempio:
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
119
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 120
Capitolo 3
USE AdventureWorks
GO
sp_helptext 'usp_LookupConversionRate'
GO
Nel caso specifico sar richiesto di esaminare il corpo della stored procedure usp_LookupConversionRate. Il risultato sar molto utile e persino decorativo, come mostrato in Figura 3.14.
Figura 3.14: Come ottenere un output formattato del corpo degli oggetti
definiti in T-SQL puro
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
G
G
G
G
G
16:05
Pagina 121
SQL SERVER
ASPETTI
AVANZATI
Le funzioni scalari di sistema vanno usate esattamente come le funzioni scalari definite dall'utente. Si osservi il seguente uso all'interno di
una banale query sulla tabella Sales.SalesOrderDetail di Northwind:
SELECT
GetDate() AS DataCorrente,
FLOOR(UnitPrice) AS FlooredPrice, UnitPrice,
SUBSTRING(CarrierTrackingNumber, 6, 4) AS PartialTrackingNumber,
CarrierTrackingNumber
FROM Sales.SalesOrderDetail
WHERE SalesOrderId = 43661
Si pu osservare l'uso di diverse funzioni di sistema, con o senza parametri da passare come argomento delle stesse. In particolare sono state
adoperate la funzione GetDate per ottenere la data corrente dal sistema, la funzione FLOOR per ottenere l'arrotondamento per eccesso di un
numero decimale e la funzione SUBSTRING che restituisce una sottostringa di una stringa pi ampia. In Figura 3.15 si pu osservare il resultset generato dalla query di esempio.
121
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 122
Capitolo 3
DESCRIZIONE
Restituisce il valore assoluto (positivo) dell'espressione
numerica specificata.
ACOS(n)
ASIN(n)
ATAN(n)
ATN2(n, m)
CEILING(n)
COS(n)
COT(n)
FLOOR(n)
LOG(n)
122
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
FUNZIONE
LOG10(n)
16:05
Pagina 123
SQL SERVER
ASPETTI
AVANZATI
DESCRIZIONE
Restituisce il logaritmo in base 10 dell'espressione float
specificata.
PI()
ROUND(n,
len, tipo)
SIGN(n)
SIN(n)
SQRT(n)
SQUARE(n)
TAN(n)
123
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 124
Capitolo 3
hh (ora);
mi (minuti);
ss (secondi;
ms (millisecondi).
Di seguito se ne riportano le pi rilevanti:
FUNZIONE
DESCRIZIONE
Funzioni di stringa
Le funzioni di stringa manipolano stringhe effettuandone modifiche,
concatenazioni e trasformazioni e restituendo le stringe risultati. Di seguito se ne riportano le pi rilevanti:
FUNZIONE
DESCRIZIONE
124
Capitolo 3 (103-128):007-050
29-10-2007
Capitolo 3
FUNZIONE
16:05
Pagina 125
SQL SERVER
ASPETTI
AVANZATI
DESCRIZIONE
(expr)
125
Capitolo 3 (103-128):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:05
Pagina 126
FUNZIONE
STUFF(expr,
inizio, fine,
lunghezza,
expr2)
SUBSTRING(
expr, inizio,
fine)
UPPER
Capitolo 3
DESCRIZIONE
Elimina un determinato numero di caratteri nella stringa expr
e inserisce un altro set di caratteri a partire dal punto
specificato nella stringa expr2.
Restituisce parte di un'espressione di tipo carattere, binario,
testo o immagine.
Restituisce un'espressione di caratteri dopo aver convertito i
caratteri maiuscoli in caratteri maiscoli.
DESCRIZIONE
Restituisce la posizione di inizio della prima occorrenza di un
criterio di ricerca in un'espressione specificata, oppure zero se
il criterio di ricerca non viene trovato, in tutti i dati di tipo
carattere e text validi.
Restituisce il valore del puntatore di testo corrispondente a
una colonna di tipo text, ntext o image in formato varbinary.
possibile utilizzare il valore del puntatore di testo
recuperato nelle istruzioni READTEXT, WRITETEXT e
UPDATETEXT.
Capitolo 3 (103-128):007-050
Capitolo 3
FUNZIONE
COLAESCE(e
xpr, n)
COL_LENGT
H ('tabella',
'colonna')
COL_NAME
(idtabella,
idcolonna)
DATALENGT
H(expr)
GETANSINUL
L([
'database' ] )
NULLIF(expr
1, expr2)
29-10-2007
16:05
Pagina 127
SQL SERVER
ASPETTI
AVANZATI
DESCRIZIONE
Restituisce la prima espressione non Null tra i relativi
argomenti.
Restituisce la lunghezza definita di una colonna, espressa in
byte.
Restituisce il nome di una colonna corrispondente al numero
di identificazione di tabella e al numero di identificazione di
colonna specificati.
Restituisce il numero di byte utilizzati per rappresentare
un'espressione.
Restituisce l'impostazione predefinita relativa al supporto dei
valori Null del database per la sessione corrente.
Restituisce un valore Null se le due espressioni specificate
sono uguali.
127
Capitolo 3 (103-128):007-050
29-10-2007
16:05
Pagina 128
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 129
SQL SERVER
ASPETTI
AVANZATI
NUOVE FUNZIONALIT
IN SQL SERVER 2005
Quest'ultimo capitolo non presenta una coerenza e continuit di argomenti, ma si pone quasi come una sorta di raccolta di appendici
che richiamano alcune delle pi interessanti novit di SQL Server
2005 che non stato possibile affrontare direttamente nel corso del
volume. Pertanto possibile leggere il capitolo anche non in modo
lineare, ma concentrandosi solo sui paragrafi di interesse e nell'ordine che si preferisce.
129
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 130
Capitolo 4
PrezzoUnitario FLOAT,
Quantita INT,
TotaleRiga AS PrezzoUnitario * Quantita
)
4.2 M.A.R.S.
(MULTIPLE ACTIVE RESULT SET)
La fruizione dei dati di un database possibile in ADO.NET abbina130
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 131
SQL SERVER
ASPETTI
AVANZATI
to a SQL Server in diversi modi. Tra le tante novit che questa nuova versione di SQL Server introduce, ce n' una estremamente comoda chiamata MARS: Multiple Active Result Set. Essa funziona in abbinamento alla piattaforma .NET 2.0 e in particolare alla libreria di
accesso ai dati ADO.NET 2.0.
Quando abbiamo bisogno di accedere a dei dati in modo veloce e leggero non possiamo che optare per il DataReader che consente di leggere un flusso di dati ricevuto da un database in modalit read-only,
forward-only. Esso impone che la connessione al database venga
mantenuta aperta finch tutti i dati non sono stati letti. Fino alla
versione ADO.NET 1.1 e Microsoft SQL Server 2000 questo model lo
di programmazione introduceva una limitazione: limpossibilit di
eseguire altre operazioni sul database finch non si liberata la connessione, pena il sollevamento dell'eccezione che indicava la preesistenza di un DataReader gi aperto associato alla connessione
corrente. Non era possibile aprire altri DataReader nella stessa connessione prima che questo non venisse chiuso. Osserviamo il seguente esempio: facendo riferimento al solito database di esempio
Northwind, consideriamo la tabella Orders (ordini) e le sue righe Order Details. Scorrendo la lista di tutti gli ordini, per ciascun ordine vogliamo determinare il valore dell'ordinato e cio la somma del valore unitario di tutte le righe moltiplicato per la quantit ordinata per
ciascuna riga.
L'algoritmo pi intuitivo prevede che:
G
G
Per compiere questa operazione dobbiamo necessariamente eseguire due comandi T-SQL distinti sul database, ma sempre sulla stessa connessione. Cosa che, come abbiamo visto, non potremmo fare
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
131
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 132
Capitolo 4
con il DataReader.
Il Multiple Active Resultset Set ci viene in aiuto perch ci permette di
eseguire comandi distinti attraverso la stessa connessione. Osserviamo il seguente esempio in C# (.NET 2.0):
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace MARS {
class Program {
static void Main( string[] args ) {
SqlConnection myConnection = new SqlConnection();
//Va impostato il parametro MultipleActiveResultSets sulla stringa
di connessione
myConnection.ConnectionString = "Data Source=(local);Integrated
Security=SSPI;Persist Security Info=False;Initial
Catalog=Northwind;MultipleActiveResultSets=True";
SqlCommand myCommand = myConnection.CreateCommand();
myCommand.CommandType = CommandType.Text;
myCommand.CommandText = "Select OrderId from Orders";
myConnection.Open();
SqlDataReader myDataReader = myCommand.ExecuteReader();
while ( myDataReader.Read() ) {
SqlCommand mySecondCommand =
myConnection.CreateCommand();
mySecondCommand.CommandType = CommandType.Text;
mySecondCommand.CommandText = "Select cast(Sum(UnitPrice
* Quantity) as float) as Venduto from [Order Details] where OrderId =
@OrderId";
SqlParameter parameter =
132
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 133
SQL SERVER
ASPETTI
AVANZATI
mySecondCommand.CreateParameter();
parameter.ParameterName = "@OrderId";
parameter.Value = (int)myDataReader["OrderId"];
mySecondCommand.Parameters.Add( parameter );
//Eseguo il secondo comando sulla stessa connessione
double Ordinato = (double)mySecondCommand.ExecuteScalar();
Console.WriteLine( "Order Id: {0}\r\nTotale Ordinato: {1}\r\n----\r\n", myDataReader["OrderId"].ToString(), Ordinato.ToString() );
}
myConnection.Close();
}
}
}
Per sfruttare MARS la connectionstring deve essere predisposta impostando il parametro MultipleActiveResultSets = true nella stringa
di connessione. La cosa va fatta solo per le connessioni che realmente necessitano di questa funzionalit, pena un decadimento delle prestazioni.
A questo punto viene prima eseguita una selezione su tutti i record
della tabella Orders il cui risultato un DataReader. Eseguendo un
ciclo su tutti i record, viene recuperato lOrderId che viene passato
alla seconda query che effettua il conteggio dei quantitativi venduti. Impostare a true il parametro MultipleActiveResultSets nella stringa di connessione anche quando non serve, potrebbe avere impatti negativi sul fronte delle prestazione, dunque il suo uso va attentamente ponderato. Tuttavia la sua introduzione, ove serva, fornisce
un'estrema semplificazione del codice di programmazione.
133
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 134
Capitolo 4
la precedente versione 2000 grazie alla clausola FOR XML che consentiva (e consente) di restituire un resultset in formato XML oppure utilizzando OPENXML. SQL Server 2005 estende grandemente il
suo supporto a questo formato introducendo l'implementazione del
nuovo tipo di dati XML. Vi erano in passato per una serie di limitazioni: ad esempio, per memorizzare interamente un file XML nel database, era necessario inserire il contenuto in campi text o nei campi BLOB perdendo le potenzialit del formato XML. Con SQL Server
2005, invece, adesso abbiamo un tipo di dati nativo XML ovvero
possiamo utilizzarlo come campo di una tabella o come variabile di
input/output per stored procedure e funzioni. Dunque, all'interno
del campo XML, si potranno inserire direttamente interi documenti
o anche solo porzioni well-formed. Tale condizione sar direttamente verificata dal parser xml interno di SQL Server 2005 che ne segnaler l'errore analogamente a quanto viene quanto avviene con
gli invalid cast. Quindi se proviamo ad inserire nel database un file
non well-formed sar direttamente SQL Server che ci avviser che l'operazione non possibile e quindi la INSERT o un UPDATE che vadano a modificare non avr esecuzione. Al fine di meglio comprendere questa funzionalit, procediamo con un semplice esempio. Costruiamo una semplice tabella che contiene un campo di tipo XML:
CREATE TABLE TabellaEsempio
(
IdRec int IDENTITY(1,1) NOT NULL,
ValoreXML XML NULL
)
La tabella, a parte la solita chiave primaria identity, contiene un campo ValoreXML del nuovo data type XML. Siamo dunque pronti ad
inserire un record in questa tabella:
INSERT INTO TabellaEsempio
134
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 135
SQL SERVER
ASPETTI
AVANZATI
(ValoreXML)
VALUES('<?xml version="1.0" encoding="utf-8" ?>
<Indice>
<Capitolo>
<Titolo>Cenni sul database relazionale</Titolo>
<Sommario>Introduzione e storia del modello
relazionale</Sommario>
<Pagine>25</Pagine>
</Capitolo>
<Capitolo>
<Titolo>Installazione e amministrazione di SQL Server
2005</Titolo>
<Sommario>La parte piu noiosa da scrivere di tutto i
volume</Sommario>
<Pagine>50</Pagine>
</Capitolo>
</Indice>')
135
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 136
Capitolo 4
<Capitolo>
<Titolo>Funzionalit nuove a avanzate di SQL Server 2005</Titolo>
<Sommario>Il capitolo corrente</Sommario>
<Pagine>25</Pagine>
</Capitolo>
</Indice>
La lettera di Funzionalit accentata e quindi non fa parte dell'alfabeto UTF-8, come descritto nel preambolo del documento. SQL
Server ci segnaler un inequivocabile:
Msg 9420, Level 16, State 1, Line 9
XML parsing: line 10, character 22, illegal xml character
Per eseguire un'interrogazione su tabelle contenenti campi di tipo XML
possibile con la vecchia e rassicurante SELECT:
SELECT IdRec, ValoreXML FROM TabellaEsempio
Oppure utilizzare la vecchia clausola FOR XML per ritornare in forma XML l'intera riga, compreso il valore scalare tradizionale IdRec:
SELECT IdRec, ValoreXML FROM TabellaEsempio FOR XML AUTO
La Figura 4.1 ci mostra propria la resa nella griglia di SQL Server
Management Studio delle due query. Cliccando sul contenuto del
campo ValoreXML, Management Studio apre un nuovo documento,
questa volta di tipo XML, in cui mostra il contenuto XML del campo
con un layout simile a quello usato dai browser per rappresentare i
136
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 137
SQL SERVER
ASPETTI
AVANZATI
137
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 138
Capitolo 4
Abbiamo introdotto tre righe con altrettanti documenti XML (Capitoli, nel nostro esempio) nella tabella. Con la seguente query XPath
vogliamo estrarre i soli capitoli che hanno pi di 40 pagine e cio che
il contenuto del sottonodo Pagine sia maggiore di 40:
SELECT IdRec, ValoreXML.query('/Indice/Capitolo[Pagine>40]')
138
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 139
SQL SERVER
ASPETTI
AVANZATI
FROM TabellaEsempio
139
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 140
Capitolo 4
sempio, ma lo studio della sua sintassi non argomento di questo volume. Lo scopo, invece, era solo mostrare il livello di sofisticazione che ha raggiunto il supporto al formato XML in SQL
Server 2005, tanto da far stare stretta a questo prodotto la definizione di mero RDMS.
XQuery offre anche la possibile di modificare i valori oltre che inserirne di nuovi, ma si rimanda alla documentazione specifica sull'argomento per approfondimenti.
140
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 141
SQL SERVER
ASPETTI
AVANZATI
141
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 142
Capitolo 4
Proprio come i suoi predecessori, SQL Server 2005 supporta sia la modalit di autenticazione mista che quella Windows. In Windows
Authentication Mode, garantito l'accesso basato su un token di
142
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 143
SQL SERVER
ASPETTI
AVANZATI
protezione assegnato durante l'autenticazione effettuata correttamente al dominio o al server locale da parte di un account Windows.
In Active Directory disponibile un ulteriore livello di protezione fornito dal protocollo Kerberos. Sebbene l'autenticazione basata su
Windows sia intrinsecamente pi sicura di quella offerta dalla modalit mista, la sicurezza di SQL Server basato login migliorata attraverso la sua cifratura con certificati.
Tra le altre novit vi la capacit di impostare e gestire alcune regole per la corretta definizione degli account e delle password per le login di SQL Server in modalit nativa. Questo permette di far rispettare tali limiti sulla password quali la complessit, la scadenza della password, e il blocco degli account. Le regole di complessit delle password possono essere riassunte come di seguito:
G la lunghezza della password deve essere di almeno 6 caratteri (in
generale, le password di SQL Server pu avere tra 1 e 128 caratteri);
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
143
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 144
Capitolo 4
caratteri quali lettere maiuscole, lettere minuscole, numeri e caratteri non alfanumerici;
G la password deve differire dalle seguenti stringhe: admin, administrator, password, sa, sysadmin, il nome dell'host di hosting
calcolare l'installazione di SQL Server, e in tutto o in parte e l'impossibilit che la password ricordi da vicino la login dell'utente
correntemente connesso a Windows.
Si osservi che, indipendentemente dalla modalit di autenticazione
e dalle policy, l'installazione guidata non consente password non
vuote sull'utente SA. Inoltre vi sono delle forti restrizioni anche nella definizione della password. Pertanto si pu affermare che le impostazioni di sicurezza possibili al momento dell'installazione sono
persino pi restrittive di quanto SQL Server gi consenta di fare. Lo
scopo chiaro: tutti coloro che installano l'applicazione senza modificare sostanzialmente le impostazioni di default offerte dal setup,
si ritroveranno immediatamente un sistema piuttosto sicuro e meno
esposto a problemi di sicurezza.
possibile utilizzare le clausole CHECK_EXPIRATION e CHECK_POLICY per la creazione di nuove login con la CREATE LOGIN per impostarne o disattivarne la conformit con le policy (con i parametri
ON o OFF) e CHECK_EXPIRATION per le regole di scadenza della
password, mentre CHECK_POLICY determina il livello di complessit
della password.
CREATE LOGIN drevil
WITH
PASSWORD = 'Ch4ngeMe' MUST_CHANGE,
CHECK_EXPIRATION = ON,
CHECK_POLICY = ON
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 145
SQL SERVER
ASPETTI
AVANZATI
so di SQL esistenti. Le stesse informazioni sono disponibili per i singoli account attraverso l'interfaccia grafica di SQL Server Management Studio.
Lo statement ALTER LOGIN supporta la clausola UNLOCK, che ha la
funzione di sbloccare le login di accesso di SQL Server, che sono stati bloccate da ripetuti errori di immissione della password, a seguito della policy che prevede il blocco dell'account dopo un certo nu
Osserviamo un altro tipico script di creazione della login prodotto
/* For security reasons the login is created disabled and with a random
password. */
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
145
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 146
Capitolo 4
di massa
G diskadmin Gestiscono i file sul disco
G dbcreator Creano e modificano database
G processadmin Gestiscono i processi e i job
G serveradmin Configurano il server
G setupadmin Gestiscono la replica
G sysadmin Qualsiasi attivit amministrativa sul database
Ai ruoli amministrativi, che sono trasfersabili perch riferiti al motore di accesso ai dati pi che ai singoli database, si aggiungono
quelli che, invece, si legano a questi ultimi. In realt possibile ap146
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 147
SQL SERVER
ASPETTI
AVANZATI
plicare questi ruoli con granularit molto elevata perch impostabili sui singoli oggetti del database (tabelle, campi, stored procedure,
viste, ecc...).
Eccone i principali:
G db_accessadmin Gestiscono gli utenti del database
G db_backupoperator Possono eseguire le funzioni di backup
G db_datareader Possono esclusivamente visualizzare i dati
G db_datawriter Possono eseguire INSERT e UPDATE
G db_ddladmin Possono modificare gli oggetti o vincolarli
G db_denydatareader Non possono leggere i dati db_denydataw-
147
Capitolo 4 (129-150):007-050
SQL SERVER
ASPETTI
AVANZATI
29-10-2007
16:19
Pagina 148
Capitolo 4
Per creare un indice full-text su una tabella, quest'ultima deve contenere una colonna singola, univoca e non Null. Si consideri ad esempio un indice full-text per la tabella Document in Adventure Works,
in cui DocumentID la colonna chiave primaria. L'indice full-text indica che la parola "instructions" la 24 e la 44 nella colonna DocumentSummary della riga associata al valore DocumentID di 3.
Questa struttura di indice supporta una ricerca efficiente di tutti gli
elementi che includono parole indicizzate e operazioni di ricerca
avanzate, ad esempio ricerche di frasi e di prossimit.
Durante l'elaborazione di una query full-text, il motore di ricerca restituisce a SQL Server i valori delle chiavi delle righe che soddisfano
i criteri di ricerca. Se si desidera utilizzare una query full-text per individuare documenti che contengono la parola "instructions", dall'indice full-text si ottengono i valori DocumentID 3, 4, 6, 7 e 8. SQL
Server utilizza quindi tali chiavi per restituire le righe corrispondenti. SQL Server consente linstallazione del servizio Full Text che quindi dovr essere avviato (possiamo verificare lo stato del servizio tramite lapplicazione SQL Server Configuration Manager presente al-
Capitolo 4 (129-150):007-050
Capitolo 4
29-10-2007
16:19
Pagina 149
SQL SERVER
ASPETTI
AVANZATI
stituito da componenti responsabili dell'accesso ai dati delle tabelle e della relativa applicazione di filtri e un word breaking, meccanismo in grado di suddividere testo in token basandosi su regole lessicali.
lumn)
G attivare l'indice (sp_fulltext_table)
G popolare l'indice (sp_fulltext_catalog)
I libri di ioPROGRAMMO/SQL Server aspetti avanzati
149
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 150
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 151
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 152
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 153
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 154
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 155
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 156
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 157
Capitolo 4 (129-150):007-050
29-10-2007
NOTE
16:19
Pagina 158
Capitolo 4 (129-150):007-050
29-10-2007
16:19
Pagina 159
Tamburino:Tamburino
29-10-2007
16:22
Pagina 1
SQL SERVER
ASPETTI AVANZATI
Autore: Vito Vessia
EDITORE
Edizioni Master S.p.A.
Sede di Milano:Via Ariberto, 24 - 20123 Milano
Sede di Rende: C.da Lecco, zona ind. - 87036 Rende (CS)
Realizzazione grafica:
Cromatika Srl
C.da Lecco, zona ind. - 87036 Rende (CS)
Art Director: Paolo Cristiano
Responsabile grafico di progetto: Salvatore Vuono
Coordinatore tecnico: Giancarlo Sicilia
Illustrazioni: Tonino Intieri
Impaginazione elettronica: Francesco Cospite
Servizio Clienti