Share via


MSDN Tips & Tricks

I consigli degli esperti italiani per sfruttare al meglio gli strumenti di sviluppo e semplificare l’attività quotidiana.

In questa pagina

Risoluzione veloce dei tipi in Visual Studio 2005 e Valutazione di espressioni a run-time Risoluzione veloce dei tipi in Visual Studio 2005 e Valutazione di espressioni a run-time
Passare un array di dati ad una stored procedure Passare un array di dati ad una stored procedure
 Fare DISTINCT con COLLATE   Fare DISTINCT con COLLATE
 SQL Server Management Studio   SQL Server Management Studio

Risoluzione veloce dei tipi in Visual Studio 2005 e Valutazione di espressioni a run-time

Di Corrado Cavalli

Visual C# 2005 include una caratteristica molto interessante: la possibilità di risolvere i tipi automaticamente inserendo automaticamente il relativo statement using oppure qualificando il tipo con il relativo namespace. Per far questo basta scrivere il nome del tipo, selezionarlo col tasto destro e selezionare l’opzione Resolve.

*

Purtroppo Visual Basic 2005 non offre direttamente la stessa funzionalità, sfruttando però la funzionalità di auto correzione del codice è possibile far risolvere i namespace direttamente a Visual Basic.

*

Come si può notare dalla figura sopra riportata, semplicemente indicando il nome del tipo da utilizzare la funzione di auto correzione (indicata dal punto esclamativo), una volta selezionata, ci permette di cambiare la definizione del tipo includendo il relativo namespace. Ovviamente questa funzionalità va oltre quanto indicato offrendo una serie di possibili soluzioni ogni volta che il compilatore identifica un errore sintattico. E’ possibile gestire l’attivazione dell’ auto correzione in Visual Basic 2005 accendendo alle opzioni di Visual Studio 2005 (menu Tools->Options) nella sezione Text Editor (TextEditor->Basic->VB Specific).

Valutatore di espressioni
Volendo valutare delle espressioni dinamicamente possiamo sfruttare la capacità dei compilatori VB.NET e C# di generare ed eseguire codice a run-time. Immaginiamo di avere una stringa che contiene l’espressione da valutare.
Esempio:

string s="3+2*((5*3)-(20/10))";

Usando la funzione sotto riportata possiamo valutarla dinamicamente.

using System.CodeDom.Compiler;
using System.Reflection;

     public object Evaluate(string syntax)
      {
         //Creo il compilatore C# e relativi parametri
         CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
         CompilerParameters param = new System.CodeDom.Compiler.CompilerParameters();
         param.GenerateInMemory = true;
         param.GenerateExecutable = false;
         param.ReferencedAssemblies.Add("system.dll");

         //Creo una classe temporanea
         string src = "using System;" +
         "class Evaluator" +
         "{" +
         "public object Eval(){return " + syntax + ";}" +
         "}";

         //Compilo dinamicamente la classe
         CompilerResults results = provider.CompileAssemblyFromSource(param, src);
         if (results.Errors.Count > 0)
         {
            throw new ArgumentException("Parametro non valido.", "syntax");
         }
         else
         {
            //Creo istanza della classe Evaluator e invoco metodo Eval()
            object o = results.CompiledAssembly.CreateInstance("Evaluator");
            MethodInfo method = o.GetType().GetMethod("Eval");
            return method.Invoke(o, null);
         } 
      }

Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports System.Reflection

Public Function Evaluate(ByVal syntax As String) As Object
  'Creo il compilatore VB e relativi paramentri
  Dim provider As New Microsoft.VisualBasic.VBCodeProvider()
  Dim param As New CompilerParameters()
  param.GenerateInMemory = True
  param.GenerateExecutable = False
  param.ReferencedAssemblies.Add("System.dll")

  'Creo una classe temporanea
  Dim src As String = "Imports System" + ControlChars.Cr + _
  "Class Evaluator" + ControlChars.Cr + _
  "Public Function Eval() As Object" + ControlChars.Cr + _
  "Return " + syntax + ControlChars.Cr + _
  "End Function" + ControlChars.Cr + _
  "End Class"

  'Compilo dinamicamente la classe
  Dim results As CompilerResults = provider.CompileAssemblyFromSource(param, src)
  If results.Errors.Count > 0 Then
    Throw New ArgumentException("Parametro non valido.", "syntax")
  Else
    'Creo istanza della classe Evaluator e invoco metodo Eval()
    Dim o As Object = results.CompiledAssembly.CreateInstance("Evaluator")
    Dim method As MethodInfo = o.GetType().GetMethod("Eval")
    Return method.Invoke(o, Nothing)
  End If
End Function

Usando:

string s="3+2*((5*3)-(20/10))";
try
{
 int value = (int)Evaluate(s);
 MessageBox.Show(value.ToString());
}
catch (Exception ex)
{
 MessageBox.Show(ex.Message);
}

Dim s As String = "3+2*((5*3)-(20/10))"
Try
  Dim value As Integer = CInt(Evaluate(s))
Catch ex As Exception
  MessageBox.Show(ex.Message)
End Try

La funzione Evaluate crea dinamicamente un istanza della classe Evaluator e invoca il metodo Eval il quale valuta e ritorna il risultato dell’espressione.

Un esempio molto semplice ma che racchiude tutta la potenzialità che la generazione dinamica di codice presente in .NET mette a disposizione.

 

Passare un array di dati ad una stored procedure

Di Davide Mauri

Una delle domande più frequenti nei newsgroup dedicati a SQL Server è quella relativa alla possibilità di utilizzare array come parametri di una stored procedure.

Tecnicamente non è possibile, ma già da SQL Server 2000 esistevano alcuni workaround. Il più conosciuto ed utilizzato consiste nell’utilizzare una stringa delimitata di cui ne viene fatto lo “split” tramite una UDF. Un esempio di tale UDF è il seguente:

create function fn_ParseDelimitedString
(
@string varchar(8000),
@delimiterchar(1)
)
returns @parsed_rows table (parsed_value varchar(8000))
as
begin
declare @start int
declare @end int

select @start = 1
while @start <= len(@string)
begin
set @end = charindex(@delimiter, @string, @start)
if (@end = 0) set @end = len(@string) + 1

insert into@parsed_rows select rtrim(ltrim(substring(@string, @start, @end - @start)))
set @start = @end + 1
end
return
end
go

Che può essere utilizzato nel seguente modo:

select 
parsed_value, 
len(parsed_value) 
from 
dbo.fn_ParseDelimitedString('Davide, Andrea, Gianluca, Lorenzo, Luca, Marcello', ',')
go

Il risultato è :

parsed_value 
------------ -----------
Davide       6
Andrea       6
Gianluca     8
Lorenzo      7
Luca         4
Marcello     8

L’utilizzo di una stringa comporta diversi limiti che con SQL 2005 possono essere brillantemente superati grazie all’uso di XML.

Usando un XML di questo tipo

<?xml version="1.0"?>
<a>
<v>Davide</v>
<v>Andrea</v>
<v>Gianluca</v>
<v>Lorenzo</v>
<v>Luca</v>
<v>Marcello</v>
</a>

È possibile usufruire del supporto nativo ad XML, sfruttando il metodo nodes:

select 
parsed_value = a.v.value('text()[1]', 'varchar(max)')
from
@xml_sample.nodes('/a/v') a(v)

Utilizzando lo seguente query è possibile ottenere lo stesso risultato ottenuto con la versione basata sull’uso delle stringhe delimitate:

declare @xml_sample xml;
set @xml_sample = 
'<?xml version="1.0"?>
<a>
<v>Davide</v>
<v>Andrea</v>
<v>Gianluca</v>
<v>Lorenzo</v>
<v>Luca</v>
<v>Marcello</v>
</a>
';

with parsed_values as
(
select 
parsed_value = a.v.value('text()[1]', 'varchar(10)')
from
@xml_sample.nodes('/a/v') a(v)
)
select
parsed_value,
len(parsed_value)
from
parsed_values

Ed ecco il risultato:

parsed_value 
------------ -----------
Davide       6
Andrea       6
Gianluca     8
Lorenzo      7
Luca         4
Marcello     8

I “pro” dell’utilizzo di XML, oltre al supporto nativo, sta nella possibilità di poter utilizzare il tipo di dato XML come parametro di una stored procedure, senza aver più il limite degli 8000 caratteri. Oltre a questo è possibile (e consigliabile) creare ed associare uno XML Schema al parametro cosi da ottenere una sorta di array tipizzato.di La tipologia dell’array, inoltre, può essere molto più complessa che il semplice passaggio di una lista valori. E’ infatti possibile modificare l’elemento v in cosi da avere più attributi per gestire, ad esempio, un dictionary. Semplicemente modificando l’XML dell’esempio è possibile ottenere questo risultato :

declare @xml_sample xml;
set @xml_sample = 
'<?xml version="1.0"?>
<a>
<v i="1" n="Davide"/>
<v i="2" n="Andrea"/>
<v i="3" n="Gianluca"/>
<v i="4" n="Lorenzo"/>
<v i="5" n="Luca"/>
<v i="6" n="Marcello"/>
</a>
';

select 
parsed_value_index = a.v.value('@i', 'int'),
parsed_value_name = a.v.value('@n', 'varchar(10)')
from
@xml_sample.nodes('/a/v') a(v)

Che produce il seguente resultset:

parsed_value_index parsed_value_name
------------------ -----------------
1                  Davide
2                  Andrea
3                  Gianluca
4                  Lorenzo
5                  Luca
6                  Marcello

 

Fare DISTINCT con COLLATE

Di Andrea Benedetti

Nell’installazione dei nostri server viene usata spesso la COLLATION di default: Latin1_General_CI_AS.

Impostare la collation ci consente di specificare l’insieme delle regole basate su lingue ed impostazioni internazionali specifiche che verranno utilizzate per ordinare e confrontare stringhe.

Questa di default indica a SQL Server di utilizzare il dizionario Latin1 General, di non fare distinzione tra maiuscole e minuscole (CI = Case Insensitive) e di farla tra caratteri accentati (Accent Sensitive).

Nella definizione di un database possiamo lasciare che venga applicata la collation di default impostata a livello di istanza oppure modificarla, così come possiamo modificarla per ogni singola colonna (purché sia di tipo: char, varchar, text, nchar, nvarchar o ntext) di ogni singola tabella.

Possiamo visualizzare la COLLATION impostata di default sui nostri database ad esempio tramite la funzione DATABASEPROPERTYEX, come:

              select DB_NAME() as databaseName, 
	DATABASEPROPERTYEX (DB_NAME(), 'collation') as
  defaultCollation

Così come posso visualizzare le impostazioni applicate su ogni singola colonna delle tabelle del nostro database interrogando la vista di sistema sys.columns, ad esempio con la seguente select:

select object_name(object_id) as tableName, [name], collation_name 
from sys.columns
order by tableName, [name]

La clausola DISTINCT, che rimuove valori duplicati da un resultset, si comporta di default con le stesse caratteristiche della colonna su cui la vogliamo applicare.

Questo significa che se la nostra colonna dovesse avere una collation case insensitive, ovvero utilizzare delle regole di confronto che non effettuano distinzione tra caratteri minuscoli e maiuscoli, anche la funzione si comporterà di conseguenza, quindi senza fare alcuna distinzione.

Quindi se avessimo una operazione di lettura come “select distinct colonnaCheVoglioLeggere…”, e all’interno della tabella avessimo valori come "aaBB" e "AAbb", SQL Server tornerebbe, reputandoli identici, soltanto uno solo di essi.

La necessità potrebbe essere invece quella di trattare le due informazioni come differenti, ma lasciare la collation impostata come case insensitive.

Per fare questo SQL Server ci mette a disposizione la clausola COLLATE, ovvero la possibilità di definire delle regole di confronto differenti, anche senza modificare la struttura di database e/o tabelle.

Come posso specificare un set di regole di confronto in fase di creazione tabella, ovvero:

create table anagrafica
(
idRecord int primary key,
cognome varchar(35) collate latin1_general_CS_AS,
...
)

Allo stesso modo posso applicare questa clausola direttamente in operazioni di lettura.

Vediamo un esempio completo:

          USE Tempdb
          go

          -- creo una tabella di test
          create table tableTest
          (
          idRecord int primary key identity(1,1),
          descrizione varchar(35) collate latin1_general_ci_as
          )
          go

          -- inserisco valori in tabella
          insert tableTest values ('aabb')
          insert tableTest values ('AABB')
          insert tableTest values ('aAbB')
          insert tableTest values ('ccDD')
          insert tableTest values ('CcDd')

          -- caso di default
          select distinct descrizione
          from tableTest
        

All’esecuzione di queste istruzioni il risultato sarà uguale a:

          descrizione
          -----------------------------------
          aabb
          ccDD
        

In uscita avremmo quindi soltanto due righe. Utilizziamo allora la clausola COLLATE per specificare con quali regole trattare la colonna che andremo a leggere, ovvero:

          select distinct descrizione COLLATE latin1_general_cs_as
          from tableTest
        

Questa volta il risultato sarà composto da 5 righe, ovvero SQL Server tratterà come valori univoci i valori presenti in tabella, ovvero:

          descrizione
          -----------------------------------
          AABB
          aAbB
          aabb
          CcDd
          ccDD
        

 

SQL Server Management Studio

Di Davide Mauri

IDE Table Columns Drag’n’Drop
Una delle regole auree nella scrittura di buone query è quella di evitare a tutti i costi l’utilizzo della wildcard “*”. E’ infatti bene evitare di scrivere

          select
          from
          dbo.Articles

        

ed invece scrivere la select con la lista delle colonne esplicitata:

          select
          IdArticle,
          IdResourceType,
          Title,
          Abstract,
          Keywords,
          CreationDate,
          LastChangeDate,
          Visible,
          Original,
          TextOnly,
          Processed
          from
          dbo.Articles

        

in molti casi la scelta dell’uso del carattere “*” è semplicemente dovuto alla necessità di scrivere la query velocemente e senza perdere tempo a scrivere le colonne una ad una. Per prendere i classici due piccioni con un fava sia il Management Studio di SQL Server 2005 che il Query Analyzer di SQL Server 2000 ci vengono in aiuto; facendo il drag’n’drop della voce “Columns” sotto la tabella interessata verso la finestra di editing della query, è possibile far scrivere i nomi delle colonne in automatico dall’IDE:

*

Ed ecco il risultato:

*

Provare per credere!

Esportazione in Excel o .csv
Una feature molte utile quando è necessario fare delle analisi “al volo” (e su un limitato set di righe) è la possibilità di esportare il risultato di una query in excel oppure in formato comma-delimited.

Per esportare i dati su Excel è sufficiente posizionarsi sul rettangolo posto in altro ed a sinistra della griglia che contiene il risultato della query e quindi selezionare tutti i dati cliccandoci sopra. Una volta selezionati i dati è possibile fare un semplice copia-e-incolla su Excel per avere i dati copiati all’interno del foglio di lavoro in uso!

Ecco l’esempio: dal management studio

*

Ad Excel:

*

Se invece si preferisce salvare il risultato in un file .csv è sufficiente selezionare la voce “Save Result As” che appare cliccando con il pulsante destro del mouse nel riquando iniziale della griglia che contiene il risultato della query:

*

A questo punto apparirà un finestra di dialogo dove sarà sufficiente specificare il nome del file di destinazione.

Tab Groups & Window Split
Avendo a disposizione uno schermo ampio (oggi quasi tutti i portatili sono ormai a 16:9 e in sistemi desktop non è raro trovare LCD a 19’’) è possibile utilizzare alcune funzionalità piuttosto comode messe dispodizioni dal Management Studio. La prima funzionalità è offerta dai “tab group”. In pratica è possibile dividere a metà la finestra dove normalmente si scrive il codice delle query in modo tale da avere due connessioni visibili conteporaneamente. Per farlo basta cliccare con il tasto destro sul tab che vi interessa “isolare”:

*

E scegliere se dividerlo in orizzontale o in verticale. Visto la conformazioni di uno schermo a 16:9 l’ideale è scegliere lo split verticale. Il risultato è il seguente:

*

Avendo cosi la possibilità di controllare con un solo colpo d’occhio due connessioni (e quindi due query).

Un’altra importante funzionalità è quella del window split. Per accedere in questo caso è necessario passare per il menu Window -> Split. Selezionata questa voce la finestra attiva verrà divisa in due, ed i due contenuti saranno sincronizzati: quello che scriverete in una verrà scritto anche nell’altra (in realtà, infatti, è sempre lo stesso codice ma visto da due finestre diverse). L’utilità enorme di questa funzionalità è che invece lo scroll delle due finestre è indipendente.

*

Quando avete da sviluppare del codice SQL piuttosto lungo è frequente la necessità - ad un certo punto della scrittura o dell’analisi - di voler andare a vedere cosa fa il codice “qualche riga più in su”. Grazie allo split potete evitare di continuare a scrollare in alto ed in basso alla ricerca della riga di codice che desiderate, ma dividendo semplicemente la finestra a metà potete tenere una parte fissa sul codice sulla quale state facendo un controllo (magari per avere sott’occhio i nomi dei campi di una tabella) ed una parte fissata invece sulla porzione di codice che state sviluppando o modifcando (magari la clausola “on” di una join).