SQL Server Γ¨ un sistema di gestione di database relazionali sviluppato da Microsoft, progettato per archiviare, recuperare e gestire efficacemente grandi quantitΓ  di dati utilizzando il linguaggio SQL. Vantaggi:

  • Robustezza
  • Performance
  • MaturitΓ 
  • Presenza sul mercato
  • Integrazione con sistemi terzi
  • FacilitΓ  di impiego
  • Bassi costi di manutenzione
  • Economico
  • Ambia divulgazione e documentazione
  • Costante crescita

Componenti

  1. Database Engine
    • Il componente principale di SQL Server che gestisce l’archiviazione, la gestione e l’elaborazione dei dati.
  2. Server Agent
    • Strumento di automazione che consente di programmare ed eseguire operazioni di manutenzione, backup e monitoraggio in SQL Server. Assomiglia al cron di linux.
  3. Reporting Services
    • Componente che permette di sviluppare report basati sui dati presenti in SQL Server.
    • Oggi Γ¨ considerato quasi obsoleto, poichΓ© altre soluzioni di reportistica e visualizzazione dati sono diventate piΓΉ avanzate e diffuse.
  4. Analysis Services
    • Strumento per creare e gestire strutture multidimensionali (come cubi o ipercubi) che contengono dati giΓ  aggregati provenienti da altre origini.
    • Un tempo molto utile per velocizzare le query analitiche, ma oggi il suo utilizzo Γ¨ diminuito, grazie a hardware sempre piΓΉ potente che permette l’elaborazione diretta dei dati senza necessitΓ  di pre-aggregazioni.

Edizioni

  • Enterprise
    • L’edizione piΓΉ completa di SQL Server, con funzionalitΓ  avanzate.
    • Raramente utilizzata, poichΓ© spesso eccede le necessitΓ  della maggior parte delle applicazioni aziendali.
  • Standard
    • Rappresenta circa il 99% degli utilizzi, offrendo un buon equilibrio tra funzionalitΓ  e costo.
    • Limiti di risorse: supporta fino a 25 core e 256 GB di RAM.
    • Disponibile con due opzioni di licenza:
      • Licenza per core: costa circa 1.500€ per core, con un minimo di 2 core.
      • Licenza per utente: costa 250€ per utente, a cui bisogna aggiungere 1.000€ per il motore SQL.
  • Developer
    • Edizione gratuita destinata agli sviluppatori per creare e testare applicazioni.
    • Molto simile alla Standard in termini di funzionalitΓ , ma non puΓ² essere utilizzata in ambienti di produzione.
  • Express
    • Edizione gratuita, pensata per piccoli progetti e applicazioni di base.
    • Limiti di risorse:
      • Massimo di 10 GB di storage.
      • Non supporta le query in parallelo.
    • Sconsigliata per ambienti di produzione con grandi quantitΓ  di dati, a causa delle limitazioni di performance.

Istanze

In SQL Server, Γ¨ possibile avere una o piΓΉ istanze del database server installate sulla stessa macchina. Questo offre una grande flessibilitΓ , soprattutto in ambienti in cui diverse applicazioni richiedono versioni differenti o configurazioni specifiche di SQL Server.

Istanza predefinita vs. Named Instance

  1. Istanza predefinita (Default Instance):
    • Quando si installa SQL Server, si puΓ² scegliere di installare un’istanza predefinita. Quest’istanza Γ¨ accessibile semplicemente usando l’indirizzo IP della macchina o il suo nome di rete, senza dover specificare ulteriori dettagli.
    • Di solito, l’istanza predefinita utilizza la porta 1433 per le connessioni TCP/IP, che Γ¨ anche la porta standard per SQL Server. Se non viene modificata, qualsiasi connessione in arrivo verso questa porta verrΓ  automaticamente instradata verso l’istanza predefinita.
    • Un’istanza predefinita Γ¨ utile quando si desidera un accesso rapido e semplice, senza la necessitΓ  di specificare ulteriori informazioni nel nome del server.
  2. Named Instances (Istanze nominate):
    • Quando si desidera installare piΓΉ di una versione o configurazione di SQL Server sulla stessa macchina, Γ¨ necessario ricorrere alle istanze nominate. Ogni istanza nominata deve avere un nome univoco, scelto dall’utente durante l’installazione (ad esempio ServerName\InstanceName).
    • Per accedere a un’istanza nominata, Γ¨ necessario specificare sia il nome della macchina sia il nome dell’istanza (ad esempio 192.168.1.10\NomeIstanza).
    • Le istanze nominate generalmente utilizzano una porta diversa dalla porta 1433 per evitare conflitti con l’istanza predefinita.

File di dati e log

Microsoft SQL Server utilizza un’architettura in cui i dati e i log delle transazioni sono separati in file distinti per garantire l’integritΓ  e la sicurezza del database. In pratica, il database SQL Server memorizza i dati in file con estensione .MDF (Master Data File) e .NDF (Secondary Data File), mentre le operazioni di log vengono salvate in un file con estensione .LDF (Log Data File).

Funzionamento

  1. Scrittura nel Log di Transazione (LDF)
    • Quando un’applicazione scrive o modifica dati nel database, la modifica viene prima registrata nel log delle transazioni (file .LDF).
    • Il log delle transazioni registra ogni operazione di modifica (ad esempio, INSERT, UPDATE, DELETE) sotto forma di eventi di transazione.
    • In questo modo, ogni modifica al database Γ¨ tracciata e sequenzialmente memorizzata nel log prima di essere applicata al file di dati (file .MDF).
  2. Conferma della Transazione e Persistenza nei Dati (MDF)
    • Dopo che la modifica Γ¨ stata registrata nel log delle transazioni, SQL Server conferma all’applicazione che l’operazione Γ¨ stata completata. Questo processo Γ¨ detto commit della transazione.
    • Solo in un secondo momento, in modo asincrono, SQL Server applica effettivamente le modifiche ai file di dati .MDF tramite un’operazione di scrittura chiamata checkpoint.
    • Durante un checkpoint, il sistema sincronizza il contenuto dei file di dati con il log delle transazioni, assicurandosi che tutte le modifiche registrate nel log siano state applicate anche ai dati.
  3. Recupero in Caso di Interruzioni (Recovery)
    • In caso di interruzioni del servizio, come un arresto improvviso del server o un’interruzione di corrente, SQL Server puΓ² utilizzare il log delle transazioni per mantenere l’integritΓ  del database.
    • Quando il server si riavvia, SQL Server esamina il log delle transazioni e applica qualsiasi modifica che era stata registrata nel log ma non ancora scritta nel file di dati. Questo processo Γ¨ chiamato recovery.
    • PoichΓ© tutte le modifiche sono prima registrate nel log, SQL Server Γ¨ in grado di ripristinare il database allo stato esatto in cui si trovava prima dell’interruzione, riducendo il rischio di corruzione dei dati.

Per visualizzare il file di log posso usare la query:

SELECT * FROM fn_dialog(NULL, NULL)

Tipologie di database

SQL Server ha due grosse categorie di database:

  • Database di sistema
    • master: database di sistema che deve necessariamente esistere che include varie informazioni di sistema, tra le quali: gli account di accesso, i server collegati e le impostazioni di configurazione di sistema. Vengono anche registrare le posizioni di tutti gli altri db collegati e le informazioni di inizializzazione di SQL Server.
    • tempdb: database temporaneo utilizzato non solo quando finisce la RAM ma anche per caching o operazioni particolari. Viene ricreato ad ogni riavvio di SQL Server.
    • model: database modello che viene utilizzato quando creo un nuovo database.
    • msdb: sono contenuti tutti i Jobs, alert e procedure automatiche avviate dall’agent.
  • Database utente

Tipi di dati

Dati carattere

Il piΓΉ usato Γ¨ nvarchar, che ha sia una lunghezza variabile che la gestione di caratteri Unicode.

TipoDescrizioneRange di lunghezzaArchiviazione
charStringa a lunghezza fissa1 - 8,000 caratteriFissa, 1 byte per carattere
varcharStringa a lunghezza variabile1 - 8,000 caratteri o varchar(max)*Variabile, 1 byte per carattere
ncharStringa a lunghezza fissa (Unicode)1 - 4,000 caratteriFissa, 2 byte per carattere
nvarcharStringa a lunghezza variabile (Unicode)1 - 4,000 caratteri o nvarchar(max)*Variabile, 2 byte per carattere

Dati ora e data

I piΓΉ usati sono smalldatetime o datetime se voglio una precisione al millisecondo.

TipoRangePrecisioneArchiviazione
date0001-01-01 to 9999-12-31Giorno3 byte
datetime1753-01-01 00:00:00.000 to 9999-12-31 23:59:59.9973.33 millisecondi8 byte
datetime20001-01-01 00:00:00.0000000 to 9999-12-31 23:59:59.9999999100 nanosecondiDa 6 a 8 byte (dipende dalla precisione)
smalldatetime1900-01-01 00:00 to 2079-06-06 23:591 minuto4 byte
time00:00:00.0000000 to 23:59:59.9999999100 nanosecondiDa 3 a 5 byte (dipende dalla precisione)
datetimeoffset0001-01-01 00:00:00.0000000 to 9999-12-31 23:59:59.9999999 (con offset di fuso orario)100 nanosecondiDa 8 a 10 byte (dipende dalla precisione)

Dati numerici

I dati piΓΉ utilizzati sono int per gli interi e decimal per i numeri reali.

TipoRangeIntervalloArchiviazione
bigint-2^63 to 2^63 - 164 bit8 byte
int-2^31 to 2^31 - 132 bit4 byte
smallint-2^15 to 2^15 - 116 bit2 byte
tinyint0 to 2558 bit1 byte
bit0 or 11 bit1 bit
decimal-10^38 +1 to 10^38 -1Fino a 38 cifre decimali (18,6 piΓΉ usato).Da 5 a 17 byte (dipende dalla precisione). PiΓΉ usato 9.

Comandi comodi

  • Limita il numero di righe restituite da una query in SQL Server.
  • Sintassi: SET ROWCOUNT number
  • Per ripristinare tutte le righe: SET ROWCOUNT 0.
  • Differenza con TOP: SET ROWCOUNT influisce su tutte le query successive finchΓ© non viene reimpostato.
  • SET NOCOUNT ON
    • Disattiva il conteggio automatico delle righe modificate da ogni istruzione SQL.
    • Utile nelle stored procedure per migliorare le prestazioni.
    • Sintassi: SET NOCOUNT ON
  • NULLIF

    • Restituisce NULL se due espressioni sono uguali, altrimenti restituisce la prima espressione.
    • Sintassi: NULLIF(expression1, expression2)
  • HAVING

    • Permette di applicare filtri su aggregazioni (GROUP BY), non possibili con WHERE.
    • Esempio:
      SELECT IDTipoMovimento, SUM(PrezzoTotale)
      FROM contrattirighe
      GROUP BY IDTipoMovimento
      HAVING SUM(PrezzoTotale) > 100000.00;
  • OUTPUT

    • Ritorna informazioni sulle righe modificate da istruzioni INSERT, UPDATE, DELETE o MERGE.
    • Utile per tracciare modifiche o archiviare cambiamenti.
  • CHOOSE

    • Restituisce un valore corrispondente a un indice in una lista.
    • Esempio:
      SELECT CHOOSE(3, 'Manager', 'Director', 'Developer', 'Tester')
  • UNION

    • Combina i risultati di piΓΉ query in un unico set, eliminando i duplicati (per includere i duplicati usa UNION ALL).
    • Le query devono avere lo stesso numero e tipo di colonne.
    • Sintassi:
      query1
      UNION
      query2
  • SELECT INTO

    • Crea una nuova tabella con i risultati di una query giΓ  tipizzata con i nomi dei campi.
    • Sintassi:
      SELECT column1, column2
      INTO NewTable
      FROM SourceTable;
  • INSERT + SELECT e SELECT INTO

    • INSERT + SELECT: inserisce risultati di una query in una tabella esistente.
      INSERT INTO MyTable (column1, column2)
      SELECT column1, column2 FROM SourceTable;
    • SELECT INTO: crea automaticamente una nuova tabella con i risultati della query, utile per tabelle temporanee.
      SELECT column1, column2
      INTO #TempTable
      FROM SourceTable;

Importare DB

Esistono due principali modalitΓ  per importare un database in SQL Server:

  1. Importazione tramite script SQL
  2. Restore tramite file binario compresso

1. Importazione tramite script SQL

Per importare un database utilizzando uno script SQL:

  • Apri il file dello script SQL in SQL Server Management Studio (SSMS).
  • Premi il tasto F5 per eseguire lo script e importare il database.

2. Restore tramite file binario (es. file .bak)

La seconda opzione consiste nel ripristinare un database utilizzando un file binario compresso, solitamente un file di backup con estensione .bak. Questo file contiene i dati del database, incluse le parti principali:

  • File .mdf (contiene i dati principali)
  • File .ldf (contiene i log delle transazioni)

Procedura di restore in SQL Server:

  1. In SQL Server Management Studio, fai clic destro su Database.
  2. Seleziona Restore Database.
  3. Nella finestra di ripristino, scegli Device e quindi File.
  4. Naviga per selezionare il file .bak da importare.

Nota sulle cartelle visibili: Le cartelle visibili nel selettore dipendono dai permessi dell’utente con cui si Γ¨ effettuato l’accesso:

  • Se hai effettuato l’accesso con autenticazione SQL Server, vedrai solo alcune cartelle specifiche, come la cartella di SQL Server in Program Files.
  • Non saranno visibili altre cartelle utente (ad esempio, C:\Users\paolo...) o cartelle di rete, a meno che l’utente con cui sei loggato non abbia i permessi su quei percorsi.

Alias e sinonimi

  • Sinonimi: Un sinonimo Γ¨ un nome alternativo per un oggetto di database (come una tabella, una vista o una stored procedure) che si trova nello stesso database o in un altro database. I sinonimi facilitano l’accesso a oggetti esterni o a oggetti con nomi complessi, permettendo di fare riferimento a un oggetto con un nome piΓΉ semplice o locale senza modificare la struttura delle query. Supponiamo di avere una tabella Products in un database remoto chiamato InventoryDB. Creiamo un sinonimo per questa tabella nel nostro database corrente: CREATE SYNONYM MyProducts FOR InventoryDB.dbo.Products;
  • Alias: Un alias Γ¨ un nome temporaneo assegnato a una colonna o a una tabella all’interno di una singola query per migliorarne la leggibilitΓ  o per evitare conflitti di nomi. Gli alias sono specifici della query e non esistono al di fuori di essa. Per esempio, in una SELECT puoi creare un alias per una tabella o per una colonna (AS) per facilitare la scrittura della query. N.b. : non funzionano gli alias di colonna nella clausola WHERE.

Viste

Le viste in SQL Server sono query salvate che agiscono come tabelle virtuali. Una vista rappresenta un set di risultati derivati da una query su una o piΓΉ tabelle o altre viste. Le viste possono semplificare l’accesso ai dati, migliorare la sicurezza (limitando l’accesso a determinate colonne) e permettere una gestione piΓΉ semplice delle query complesse.

Buona norma: evitare di creare β€œviste di viste” (cioΓ¨, viste basate su altre viste), poichΓ© ciΓ² puΓ² ridurre le prestazioni e aumentare la complessitΓ , rendendo piΓΉ difficile il debug e la manutenzione del database.

Limiti

  • Non Γ¨ possibile usare la clausola ORDER BY senza in TOP
  • Non Γ¨ possibile usare una SELECT INTO in una view
  • Non Γ¨ possibile usare una clausola COMPUTE o COMPUTE BY
  • Non Γ¨ possibile fare riferimento a tabelle temporanee o variabili tabella
  • Non Γ¨ possibile nascondere la definizione di una vista

Linked Server

Un linked server in SQL Server Γ¨ una funzionalitΓ  che consente di collegare il server SQL a un altro server di database (che puΓ² essere un altro SQL Server, PostgreSQL, Oracle, MySQL, ecc.) e accedervi come se fosse una parte dello stesso server. Con un linked server, puoi eseguire query distribuite che combinano dati provenienti da piΓΉ database server. Vantaggi:

  • Accesso remoto ai dati: Consente di accedere a dati di un altro server senza la necessitΓ  di trasferirli manualmente.
  • FacilitΓ  di query: Puoi utilizzare T-SQL per interrogare dati su server remoti come se fossero locali.
  • Integrazione: Ideale per sistemi che richiedono l’integrazione tra piΓΉ database.

Installazione

ODBC

  1. Installare un driver ODBC per PostgreSQL:
    • Installa il driver PostgreSQL ODBC (es. psqlODBC).
    • Configura un DSN (Data Source Name) sul sistema con le credenziali e il database PostgreSQL.: apri ODBC Data Source Administrator (x64) β†’ System DSN β†’ PostgreSQL ANSI (x64)
      • Data Source: Postgres
      • Database: mhiradb
      • Server: localhost
      • User Name: postgres
      • Password: (campo riempito, non visibile il valore esatto)
      • SSL Mode: disable
      • Port: (campo vuoto)
  2. Abilitare la funzionalitΓ  Linked Server in SQL Server:
    • Assicurati che la funzionalitΓ  sia abilitata e che SQL Server abbia i permessi per utilizzare provider esterni.

Creare il Linked Server tramite SSMS

  1. Apri SSMS e connettiti al tuo server SQL Server.
  2. Espandi la cartella Server Objects.
  3. Fai clic destro su Linked Servers e scegli New Linked Server.
  4. Compila i campi come segue:
    • Linked server: Un nome rappresentativo (es. PostgreSQL_LinkedServer).
    • Provider: Seleziona Microsoft OLE DB Provider for ODBC Drivers.
    • Data source: Inserisci il DSN configurato per PostgreSQL.
Step 2: Configurare la Sicurezza
  1. Nella scheda Security, seleziona β€œBe made using this security context”.
  2. Fornisci il nome utente e la password dell’utente che verrΓ  utilizzato da Sql Server per fare le query.

Utilizzo del Linked Server

Posso fare sia quelle query come se fossero in locale (alcuni ODBC convertono il dialetto, per esempio in PostgreSql il TOP 1 viene convertito in un LIMIT 1 automaticamente) oppure scrivere le query direttamente come stringhe usando la sintassi OPENQUERY

-- Query diretta tramite OPENQUERY se ODBC
SELECT * FROM OPENQUERY(PostgreSQL_LinkedServer, 'SELECT * FROM public.users');
 
-- Query distribuita
SELECT * FROM PostgreSQL_LinkedServer.public.users;

Sinonimi

I sinonimi in SQL Server sono un alias per un oggetto database, come una tabella, una vista, una procedura memorizzata o una funzione. Quando utilizzati con i linked server, i sinonimi offrono numerosi vantaggi in termini di leggibilitΓ , manutenzione e flessibilitΓ  del codice. Vantaggi:

  1. Maggiore leggibilitΓ  e semplicitΓ  del codice: Quando si lavora con i linked server, il nome completo di un oggetto puΓ² diventare lungo e complesso, poichΓ© include il nome del server, del database, dello schema e della tabella:
SELECT * FROM [LinkedServer].[Database].[Schema].[Table];

Utilizzando un sinonimo, puoi semplificare il riferimento all’oggetto remoto:

SELECT * FROM SynonymForRemoteTable;
  1. FacilitΓ  di manutenzione: se il nome del linked server cambia (ad esempio, perchΓ© l’ambiente di sviluppo, test o produzione utilizza server diversi), puoi aggiornare il sinonimo senza dover modificare ogni query o procedura memorizzata che fa riferimento al linked server.
    • Esempio: Cambi il sinonimo che punta a un nuovo linked server, e tutte le query che lo utilizzano continuano a funzionare senza modifiche.
  2. Astrazione: I sinonimi nascondono la complessitΓ  del server remoto. Gli sviluppatori possono scrivere query come se stessero lavorando con oggetti locali senza preoccuparsi della struttura del linked server.
CREATE SYNONYM SynonymForRemoteUsers
FOR [PostgreSQL_LinkedServer].[DatabaseName].[SchemaName].[users];

Dopo aver creato il sinonimo, puoi usarlo nelle query senza specificare il linked server:

-- Invece di:
SELECT * FROM [PostgreSQL_LinkedServer].[DatabaseName].[SchemaName].[users];
 
-- Usa:
SELECT * FROM SynonymForRemoteUsers;

Utilizzo

Un esempio di utilizzo Γ¨ avere uno script che di notte va a prendere dati da vari linked server di varia natura e porta tali dati all’interno di tabelle di Sql Server. Da queste tabelle che creo poi posso creare delle visualizzazioni ad hoc usando, per esempio, PowerBI. In questo modo non vado a toccare tutti i server originali e le rispettive tabelle ma le utilizzo solo in interrogazione per popolare il mio database con cui poter fare tutte le viste che voglio.

Importare e Sincronizzare un Database

Importare un database da un altro DBMS

  1. Creare il DBMS come Linked Server
    • Configurare l’altro database come un linked server in SQL Server utilizzando SQL Server Management Studio (SSMS). Questo permette a SQL Server di comunicare con il database esterno come se fosse locale.
  2. Creare le tabelle nel database SQL Server
    • Nel database SQL Server, crea le tabelle che rispecchiano la struttura (schema) del database esterno. Puoi farlo manualmente in SSMS oppure automatizzarlo se hai script SQL che descrivono le tabelle.
  3. Importare i dati con una query
    • Utilizza una query SQL per copiare i dati dal database esterno al database SQL Server. Ad esempio:
      SELECT * INTO NomeTabella
      FROM OPENQUERY(LinkedServerName, 'SELECT * FROM NomeTabellaRemota');

Sincronizzare i dati con nuovi aggiornamenti

  1. Identificare le righe nuove
    • Una volta che la tabella Γ¨ stata creata in SQL Server, puoi sincronizzarla con i nuovi dati generati nel database remoto. Per fare ciΓ², utilizza un identificatore univoco, come un ID incrementale o un timestamp, per sapere fino a dove sono stati importati i dati.
  2. Creare una stored procedure per l’aggiornamento
    • Scrivi una stored procedure che esegua una query sul database remoto per identificare e importare solo le righe nuove. Ad esempio:
      CREATE PROCEDURE SincronizzaDati
      AS
      BEGIN
          DECLARE @UltimoID INT;
       
          -- Trova l'ultimo ID sincronizzato nel database locale
          SELECT @UltimoID = MAX(ID) FROM NomeTabella;
       
          -- Importa solo le righe con ID maggiori di @UltimoID
          INSERT INTO NomeTabella (Colonna1, Colonna2, Colonna3)
          SELECT Colonna1, Colonna2, Colonna3
          FROM OPENQUERY(LinkedServerName, 
              'SELECT * FROM NomeTabellaRemota WHERE ID > ' + CAST(@UltimoID AS NVARCHAR(10)) + '');
      END;
  3. Automatizzare l’esecuzione della stored procedure
    • Utilizza SQL Server Agent per pianificare l’esecuzione della stored procedure a intervalli regolari (ad esempio ogni notte). In questo modo, il database SQL Server sarΓ  costantemente aggiornato con i nuovi dati provenienti dal database remoto.

Stored Procedure

Le stored procedure sono blocchi di codice SQL salvati nel database che possono essere eseguiti come un singolo comando. Sono usate per eseguire operazioni complesse, come inserimenti, aggiornamenti, cancellazioni e selezioni di dati, con la possibilitΓ  di includere logica condizionale (come IF e WHILE) e parametri di input/output. Sono utilizzate su database importanti in quanto sono estremamente piΓΉ veloci di scrivere codice utilizzando magari un Orm per accedere al DB. Quindi non sono il male assoluto come puΓ² sembrare, ma devono essere utilizzate con ragionevolezza nei casi in cui siano indispensabili per avere ottime performance su enormi basi di dati. Il loro svantaggio Γ¨ che sono difficilmente versionabili e mantenibili ma se utilizzate con razionalitΓ  permettono di ottenere delle performance incredibili, soprattutto su basi di dati importanti. Esempi:

  • Script che girano di notte che sistemano dati e mandano mail nel caso di errori;
  • Script che fanno polling su tabelle temp e in base a quello che leggono fanno cose;

Utilizzare i valori di ritorno

Di default una stored procedure fornisce come codice di uscita 0, se invece si verifica un errore il valore sarΓ  diverso da 0 ovviamente. Anche noi possiamo assegnare dei valori all’istruzione RETURN, ad esempio RETURN(-100) esce dalla procedura con codice di errore uguale a -100. Spesso vi Γ¨ la necessitΓ , in una Stored Procedure, di lavorare con dei valori di ritorno forniti da una altra Stored. Per ottenere ciΓ² esistono tre possibili modalitΓ .

Return Value

All’interno della Stored Procedure posso utilizzare la parola chiave RETURN, per esempio:

CREATE PROCEDURE GetMyInt( @Param int)
AS
DECLARE @ReturnValue int
SELECT @ReturnValue=MyIntField FROM MyTable WHERE MyPrimaryKeyField = @Param
RETURN @ReturnValue
END

In questo modo posso chiamare la procedura come:

DECLARE @SelectedValue int
EXEC @SelectedValue = GetMyInt 999

Questo metodo perΓ² ha due importanti limitazioni:

  • Il RETURN puΓ² fornire solo valori interi, NULL Γ¨ convertito a 0 e le stringhe forniscono un errore di Conversion failed when converting the varchar value to data type int.
  • Il metodo puΓ² fornire un solo valore di ritorno

Output Parameter

In fase di dichiarazione di una procedura posso dichiararne i parametri di OUTPUT, come nel seguente esempio:

CREATE PROCEDURE GetMyInt( @Param int, @OutValue int OUTPUT)
AS
SELECT @OutValue=MyIntField FROM MyTable WHERE MyPrimaryKeyField = @Param
END

La chiamata avviene nel seguente modo:

DECLARE @SelectedValue int
EXEC GetMyInt 999, @SelectedValue OUTPUT

La differenza fondamentale con il metodo indicato sopra Γ¨ il valore puΓ² essere di qualsiasi tipo, anche se puΓ² essere comunque un solo valore.

Result Set

Questo metodo Γ¨ particolarmente utile quando voglio fornire un rowset come output di una procedura e non un solo valore. Abbiamo la dichiarazione della procedura nel seguente modo:

CREATE PROCEDURE GetMyInt(@Param int)
AS
SELECT MyIntField FROM MyTable WHERE MyPrimaryKeyField = @Param
END

E’ importante notare di come questa sia la procedura piΓΉ β€œspoglia”, non presenta ne un RETURN ne un OUTPUT, semplicemente una generica SELECT. Per chiamarla scrivo, per esempio:

DECLARE @ResultSet table (SelectedValue int)
INSERT INTO @ResultSet (SelectedValue)
EXEC GetMyInt @Param
SELECT * FROM @ResultSet

Questo metodo risolve entrambe le limitazioni dei metodi precedenti in quanto:

  • Posso ritornare un qualsiasi numero di righe
  • I valori possono essere di qualsiasi tipo

Questo approccio necessitΓ  della creazione della tabella temporanea anche se viene ritornato un solo record.

Variabili

Dichiarazione

Ogni variabile utilizzata all’interno di una stored procedure (o di un batch) deve essere dichiarata assieme al suo datatype attraverso una istruzione DECLARE ed in seguito possiamo assegnarle un valore attraverso una istruzione SET o SELECT. Le variabili possono essere dichiare su riga singola:

DECLARE @VARIABILE_TESTO AS VARCHAR(300)

oppure su piΓΉ linee:

DECLARE 
@VARIABILE_NUMERO AS INTEGER,
@VARIABILE_TESTO AS CHAR(300),
@ VARIABILE_TESTOUNICODE NVARCHAR(500)

In T-SQL le variabili debbono interdirsi come variabili locali, cioΓ¨ hanno significato solo all’interno del batch (gruppo o sequenza di istruzioni T-SQL) o della stored procedure nella quale vengono dichiarate , fuori da questo contesto perdono significato. E’ possibile passare variabili da una procedura ad un’altra.

Assegnamento

Per assegnare un valore ad una variabile dopo averla dichiarata dobbiamo usare una istruzione SET o SELECT, la cui sintassi Γ¨ la seguente:

SELECT { @ variabile_locale = espressione } [ ,...n ]

L’espressione puΓ² essere una qualsiasi altra variabile un numero , una stringa, una data oppure il valore risultante dalla chiamata di altre funzioni valide per SQL Server. Posso anche fare degli pseudo cicli all’interno del select, nel caso in cui questo ritorni piΓΉ righe.

SELECT @au_lname = @au_lname + β€˜,’ + au_lname FROM authors 

N.B: SQL server quando recupera righe vuote da una SELECT assegna alla variabile il valore che aveva prima dell’istruzione SELECT usata per l’assegnazione, in questo caso il mio nome.

La differenza tra SET e SELECT Γ¨ che SET puΓ² assegnare solo una variabile alla volta mentre la SELECT anche piΓΉ variabili.

Batch SQL e l’istruzione GO

Un batch SQL Γ¨ un insieme di comandi SQL inviati come gruppo al server SQL per l’esecuzione. Ogni batch comporta:

  • Sincronizzazione tra client e server.
  • Parsing, esecuzione dei comandi e invio di un codice di stato di successo o errore al client.

Il comando GO non Γ¨ un’istruzione T-SQL, ma un segnale per il client che separa i comandi in batch distinti, da inviare uno per volta al server.

Nota: GO funziona singolarmente, dividendo i comandi in batch. Ad esempio:

DELETE FROM a
DELETE FROM b
DELETE FROM c

invio una singola query con tre comandi al server. Invece:

DELETE FROM a
GO
DELETE FROM b
GO
DELETE FROM c

invio tre batch distinti, uno per ogni comando DELETE.

Alcuni comandi richiedono un batch dedicato per funzionare correttamente. Ad esempio:

  • DROP TABLE e CREATE TABLE (se usati sullo stesso nome).
  • Comandi come CREATE/ALTER PROCEDURE o ADD COLUMN devono essere i primi nel loro batch.

Profiler

Esiste un profiler che permette di visualizzare tutte le query che vengono effettuate su un determinato database. Si accede tramite Tools -> Sql Server Profiler. Per debuggare i tempi di esecuzione di una query posso fare Query -> Include Execution Plan che aggiunge una terza tab dopo l’esecuzione di una query che mostra ogni operazione della query quanto tempo ci mette.

Trick

Concatenare variabili a stringhe

@sql = 'foo' + CAST(@variable AS NVARCHAR(MAX)) + 'bar';

Eseguire una query se esiste un valore in una tabella

Letteralmente significa β€œRitorna 1 dalla tabella” e viene spesso utilizzato in combinazione con WHERE EXISTS in modo da verificare (se la select 1 fornisce un risultato) che la tabella esista.

SELECT * FROM TABLE1 T1 WHERE EXISTS (
SELECT 1 FROM TABLE2 T2 WHERE T1.ID= T2.ID
);

Oppure posso usarlo, genericamente, quando voglio verificare che una tabella esista.

Verificare la presenza di un parametro in ingresso in una Stored

IF (ISNULL(@fooInt, 0) = 0)
BEGIN
RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0)
RETURN
END

Verificare se un sp_executesql ha dato risultati

Usare il @@ROWCOUNT che indica il numero di righe ritornate dall’ultima query eseguita

EXEC sp_executesql @sql;
IF @@ROWCOUNT = 0
BEGIN
RETURN;
END

Trasformare il risultato di una select in una stringa

Talvolta puΓ² capitare l’esigenza di fornire come risultato di una query non una lista di campi, ma un solo campo formato da una lista concatenata, che sono i valori risultanti della query in questione.

Assumiamo di avere una SELECT che mi ritorna una colonna di valori, per esempio

SELECT Field FROM Table

Che fornisce:

Field
aaa
bbb
ccc
ddd

Ora voglio concatenare i risultati di questa query in una stringa separata da virgola:

aaa,bbb,ccc,ddd

Per ottenere questo risultato Γ¨ necessario seguire il workaround spiegato in seguito.

Per prima cosa utilizzo la funzione for XML path modificando la query nel seguente modo:

SELECT f
FROM ( SELECT Field FROM TABLE ) AS fields
FOR XML PATH('')
, root('ROOTNODE')
, type

In questo modo l’output ora Γ¨ un file XML:

<ROOTNODE>
<f>aaa</f>
<f>bbb</f>
<f>ccc</f>
<f>ddd</f>
</ROOTNODE>

Ora devo eliminare il tag <f>, per far ciΓ² modifico la query nel seguente modo:

SELECT ',' + f
FROM ( SELECT Field FROM TABLE ) AS fields
FOR XML PATH('')
, root('ROOTNODE')
, type

Ottenendo così:

<ROOTNODE>,aaa,bbb,ccc,ddd</ROOTNODE>

Ora per eliminare anche il <ROOTNODE> uso la funzione value nel seguente modo:

SELECT (
SELECT ',' + f
FROM (
SELECT Field
FROM TABLE
) AS fields
FOR XML PATH('')
, root('ROOTNODE')
, type
).value('/ROOTNODE[1]', 'nvarchar(max)')
 

Che mi permette di ottenere:

,aaa,bbb,ccc,ddd

L’ultimo punto Γ¨ eliminare la prima virgola, per far ciΓ² utilizzo la funzione STUFF che consente di inserire una stringa in un’altra stringa eliminando un numero di caratteri specificato nella posizione iniziale della prima stringa e inserendo la seconda stringa in tale posizione.

SELECT STUFF((
SELECT ',' + f
FROM (
SELECT Field
FROM TABLE
) AS fields
FOR XML PATH('')
, root('ROOTNODE')
, type
).value('/ROOTNODE[1]', 'nvarchar(max)'), 1, 1, '')

Ottenendo finalmente

aaa,bbb,ccc,ddd

che Γ¨ il risultato atteso.

Usare SplitFn per ottenere una tabella a partire da una stringa

Nel caso in cui io debba gestire dei campi formati da ID (per esempio) separati da virgola, devo avere un modo per trasformare questi in una tabella in modo da poterla poi usare in delle altre query. Per questa esigenza viene in soccorso la funzione Selfcare.SplitFn che prende in ingresso una stringa e un separatore fornendo una tabella con i risultati separati.

SELECT * FROM Selfcare.SplitFn('ciao, come, va', ',')
items
ciao
come
va

Concatenare una lista (anche vuota) ad una query

Con il controlla ISNULL appendo il selettore IN solo se la lista non Γ¨ vuota

DECLARE @sql NVARCHAR(MAX)
SET @sql = 'DELETE FROM dbo.ReclamiSedi WHERE IDReclamo = ' + CONVERT(VARCHAR(10), @IDReclamo)
IF ISNULL(@ListaSedi, '') <> ''
SET @sql = @sql + ' AND IDSede NOT IN (' + @ListaSedi + ')'
EXEC sp_executesql @sql

Fornire una tabella custom come output con una sola SELECT

Il trucco Γ¨ sfruttare il comando AS del SELECT: Se io ho la seguente query:

SELECT foo FROM bar

Il risultato sarΓ  una tabella monocolonna con n righe, in cui il titolo della colonna sarΓ  foo e come contenuto dei record ci saranno quelli del campo foo della tabella bar. Ora se io invece metto nel SELECT delle costanti, cosa succede?

SELECT 1, foo FROM bar

In questo caso avrΓ² due colonne: la prima con valore costante 1, la seconda invece come prima. Non avendo utilizzato il comando AS la prima colonna non avreΓ  nessun nome. Se invece faccio

SELECT 1 AS costante_uno, foo FROM bar

Ottengo che il nome della prima colonna sarΓ  β€œcostante_uno”. Ora assumiamo di avere n query che restituiscono 0 o 1 in base al fatto che esista un record, voglio ritornare questi risultati in una nuova tabella con delle colonne nominate come voglio: la soluzione Γ¨ usare una SELECT nel seguente modo:

SELECT 0 AS col_1
, CASE 
WHEN EXISTS (
SELECT TOP 1 1
...
)
THEN 1
ELSE 0
END AS col_2

FerrΓ  ritornata una tabella con la prima colonna chiamata col_1 e la seconda col_2. Allo stesso modo posso ritornare, in una SELECT, i risultati di una altra SELECT.

SELECT 0 AS col_1
, (SELECT TOP 1 foo from bar) AS col_2

Inserire un valore in una colonna identitΓ 

Disattivo temporaneamente i controlli sull’autoincrement con il comando SET IDENTITY_INSERT table_name ON ed eseguo l’INSERT. Nota bene che devo obbligatoriamente elencare le colonne dopo il nome della tabella e deve essere un INSERT SELECT e non un INSERT VALUES. In questo esempio viene anche eseguito un controllo sul

SET IDENTITY_INSERT table_name ON
INSERT INTO table_name (col_1, col_2)
SELECT T.*
FROM (
SELECT 999 AS dummy_name, 'bar' AS dummy_name_2
) T
WHERE NOT EXISTS (
SELECT 1
FROM table_name 
WHERE col_1 = 999
);
SET IDENTITY_INSERT table_name  OFF

Eseguire un UNION con ordinamento

Quando eseguo una UNION SQL non vuole che le due SELECT da unire presentino un ordinamento. Per risolvere il problema utilizzo il seguente trucco:

SELECT *
FROM (
SELECT TOP 1 foo AS col_name
FROM table_name
ORDER BY col ASC
) DummyAlias1
 
UNION ALL
 
SELECT *
FROM (
SELECT TOP 100 PERCENT bar AS col_name
FROM table_name_2
ORDER BY col DESC
) DummyAlias2

UPDATE se esiste, altrimenti INSERT

Metodo 1 (da eseguire in una transazione):

UPDATE foo
SET Name = @Name
WHERE UnitMeasureCode = @UnitMeasureCode
IF (@@ROWCOUNT = 0 )
BEGIN
INSERT INTO foo (UnitMeasureCode, Name)
VALUES (@UnitMeasureCode, @Name)
END

Metodo 2 (consigliato):

-- =============================================
-- MERGE: dichiara la tabella da considerare
-- =============================================
MERGE foo
-- =============================================
-- USING: dichiara le variabili da utilizzare e le associa a source.var_1, socurce.var_2
-- =============================================
USING (
SELECT @UnitMeasureCode, @Name
) AS source(UnitMeasureCode, NAME)
-- =============================================
-- ON: clausola WHERE nella tabella foo
-- =============================================
ON (foo.UnitMeasureCode = source.UnitMeasureCode)
-- =============================================
-- WHEN MATCHED: se la clausola ON da TRUE (IF EXISTS SELECT TOP 1 1 FROM foo WHERE foo.UnitMeasureCode = source.UnitMeasureCode)
-- =============================================
WHEN MATCHED
-- =============================================
-- Aggiorna il nome solo nelle colonne considerate
-- =============================================
THEN
UPDATE
SET NAME = source.NAME
-- =============================================
-- Se invece non trova nessun record esegue il normale inserimento
-- =============================================
WHEN NOT MATCHED
THEN
INSERT (UnitMeasureCode, NAME)
VALUES (source.UnitMeasureCode, source.NAME)

Cercare tutti gli oggetti modificati meno n giorni fa da me

SELECT o.modify_date
, o.NAME
FROM sys.objects AS o
INNER JOIN sys.sql_modules AS m ON o.[object_id] = m.[object_id]
WHERE o.modify_date >= DATEADD(day, - 20, GETDATE())
AND m.DEFINITION LIKE '%myName%'
ORDER BY o.modify_date DESC

Cercare tutti gli oggetti modificati meno n giorni fa

Come da titolo, questa query permette di avere informazioni su tutti gli oggetti (quindi viste, stored procedure…) che sono stati modificati meno di un numero arbitrario di giorni fa, nell’esempio seguente 20:

SELECT o.modify_date, o.name, m.definition
FROM sys.objects AS o
INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id]
INNER JOIN sys.sql_modules AS m ON o.[object_id] = m.[object_id]
WHERE o.modify_date >= DATEADD(day,-20, GETDATE())

Cercare tutti gli oggetti che contengono una stringa

Quante volte puΓ² capitare di dover ricercare nel DB tutti gli oggetti che utilizzano una determinata stored, oppure gli oggetti che contengono un commento TODO o TOFIX. Esistono numerosi tool per eseguire tali ricerce ma spesso la soluzione piΓΉ veloce e semplice Γ¨ la conoscenza delle tabelle di sistema.

SELECT o.NAME AS Object_Name
, o.type_desc
, o.modify_date
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id = o.object_id
WHERE m.DEFINITION LIKE '%usp_Config_GetByID%'
ORDER BY o.modify_date DESC

Cercare tra i nomi di tutte le colonne

Analogamente a quanto descritto sopra, spesso non ho la necessitΓ  di sapere se esiste una colonna con un tal nome all’interno del mio DB (sopratutto se questo Γ¨ di dimensioni molto grandi e difficilmente consultabile). Per ottenere questa informazione utilizziamo le viste Information Schema, che meriteranno un approfondimento a parte.

SELECT COLUMN_NAME, TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%column_name%'

Cercare tutti i processi lanciati da me in questo momento

Lavorando con query complesse spesso nasce la necessitΓ  di voler avere sottocontrollo i processi aperti e quante risorse questi stanno utilizzando sul server. Per poter fare ciΓ² sono dipsonibili delle stored procedure di sistema, come sp_who, sp_who2 e sp_who4. Il codice seguente inserisce i valori forniti dalla stored sp_who2 in una tabella, in modo che questi possano essere facilmente interrogati con una query.

DECLARE @ResultSet TABLE (SPID INT, STATUS VARCHAR(100), LOGIN VARCHAR(100), HostName VARCHAR(100), BlkBy VARCHAR(100), DBName VARCHAR(100), Command VARCHAR(100), CPUTime INT, DiskIO INT, LastBatch VARCHAR(100), ProgramName VARCHAR(100), SPID2 INT, REQUESTID INT)
INSERT INTO @ResultSet EXEC sp_who2
SELECT * FROM @ResultSet AS rs WHERE rs.HostName = 'NBMYNAME' AND rs.ProgramName = 'Microsoft SQL Server Management Studio - Query'

Confrontare le colonne della stessa tabella tra due differenti database

Talvolta puΓ² capitare di dover verificare eventuali disallineamenti di colonne per la stessa tabella tra due database diversi (solitamente tra il database di staging e il database di produzione). Per trovare le differenze a livello di colonne tra due diversi database viene in aiuto la seguente query.

DECLARE @table_name varchar(50);
SET @table_name = 'calendarioFatturazione';
SELECT 'BillingTLP.dbo.' + c2.table_name AS 'Tabella di riferimento'
, c2.COLUMN_NAME, CAST(c2.data_type AS varchar(30)) + '(' + CAST(c2.character_maximum_length AS varchar(20)) + ')' AS Column_type
FROM BillingTLP.INFORMATION_SCHEMA.COLUMNS c2
WHERE table_name = @table_name
AND c2.COLUMN_NAME NOT IN (
SELECT column_name
FROM BillingTLP_QTYbis.INFORMATION_SCHEMA.COLUMNS
WHERE table_name = @table_name
)
UNION
SELECT 'BillingTLP_QTYbis.dbo.' + c2.table_name AS 'Tabella di riferimento'
, c2.COLUMN_NAME, CAST(c2.data_type AS varchar(30)) + '(' + CAST(c2.character_maximum_length AS varchar(20)) + ')' AS Column_type
FROM BillingTLP_QTYbis.INFORMATION_SCHEMA.COLUMNS c2
WHERE table_name = @table_name
AND c2.COLUMN_NAME NOT IN (
SELECT column_name
FROM BillingTLP.INFORMATION_SCHEMA.COLUMNS
WHERE table_name = @table_name
)

Trasformare i risultati di una query in coppia chiave/valore

In questo brevissimo articolo spiegherΓ² come trasformare i risultati di una qualsiasi query in una coppia chiave/valore, dove la chiave Γ¨ il nome della colonna e il valore il valore effettivo. Questa puΓ² essere utile nel caso abbia una tabella che abbia valorizzato dei campi con nomi di colonne di un’altra tabella, e abbia la nece3ssitΓ  di andare in JOIN su queste. Prendiamo ora la seguente query, molto semplice:

SELECT Foo FROM Bar

Che fornisce

Foo
100
200
300
400
500

Per trasformare questo risultato in una coppia chiave valore, utilizzo la funzione UNPIVOT (che eventualmente spiegherΓ² in un altro post).

SELECT Key, Value
FROM Bar
UNPIVOT(Valore FOR Chiave IN (Foo))
ChiaveValore
Foo100
Foo200
Foo300
Foo400
Foo500

Script onnicomprensivi T-SQL

SELECT

SELECT
-- =============================================
-- selezione di colonna standard
-- =============================================
foo
-- =============================================
-- Selezione di colonna cambiando il nome della colonna ritornata
-- =============================================
, foo_2 AS changed_name_col
-- =============================================
-- colonna impostata sempre a 'const'
-- =============================================
, 'const' AS const_col
-- =============================================
-- colonna impostata sempre a NULL
-- =============================================
, NULL AS Nome
-- =============================================
-- concatenare campi string con campi interi
-- =============================================
, string_field + CAST(int_field AS VARCHAR(20)) + '$' + CAST(int_field_2 AS VARCHAR(20)) + '$'
-- =============================================
-- colonna booleana che dipende dall'esistenza o meno di un valore in una altra tabella
-- =============================================
, CASE 
WHEN EXISTS (
SELECT TOP 1 1
FROM bar
WHERE foo = 1
)
THEN 1
ELSE 0
END AS boolean_col_exists
-- =============================================
-- valore booleano nel caso in cui una colonna non sia null e una seconda colonna sia LIKE una variabile
-- =============================================
, CASE 
WHEN field_name IS NOT NULL
AND (field_name_2 LIKE '%' + @var + '%')
THEN 1
ELSE 0
END AS boolean_col_like
-- =============================================
-- colonna che viene popolata tramite un SELECT da una altra tabella
-- =============================================
, (
SELECT TOP 1 foo
FROM bar
) AS select_col
-- =============================================
-- il valore della colonna Γ¨ una concatenazione di valori di colonna con costanti
-- =============================================
, field + CASE another_field
WHEN 2
THEN ' [chiuso]'
WHEN 3
THEN ' [riaperto]'
ELSE ''
END AS concatenated_col
-- =============================================
-- conversione da NULL/stringa a booleani 0/1
-- =============================================
, CASE 
WHEN field_that_could_be_null IS NULL
THEN 0
ELSE 1
END AS boolean_col_if_null
-- =============================================
-- cambio il valore della colonna in base al valore della sua prima lettera
-- =============================================
, CASE 
WHEN LEFT(field_name, 1) = '!'
THEN SUBSTRING(field_name, 2, LEN(field_name) - 1)
ELSE field_name
END AS first_letter_col
-- =============================================
-- colonna stringa booleana (M/F) in base al valore di una sottostringa di un campo iniziale
-- =============================================
, CASE 
WHEN CAST(SUBSTRING(field_name, 10, 2) AS INT) > 40
THEN 'F'
ELSE 'M'
END AS
-- =============================================
-- metto i valori di un campo o di un altro in base all'uguaglianza o meno di due variabili
-- =============================================
, CASE 
WHEN (@var_1 = @var_2)
THEN field_name_1
ELSE field_name_2
END AS col_that_depends_on_var_values
-- =============================================
-- Solo quando l'app name Γ¨ un determinato valore (Connection string: Application Name=fo;)
-- =============================================
, CASE 
WHEN app_name() = 'fo'
THEN foo
ELSE NULL
END AS Utente
-- =============================================
-- il valore della colonna Γ¨ la concatenzazione dei valori da una altra tabella
-- =============================================
, (
SELECT STUFF((
SELECT ',''' + CAST(field_to_concatenate AS VARCHAR) + ''''
FROM table_with_field_that_have_to_be_concatenated
FOR XML PATH('')
, root('ROOTNODE')
, type
).value('/ROOTNODE[1]', 'nvarchar(max)'), 1, 1, '')
) AS concatenated_col
FROM bar
-- =============================================
-- confronto il valor di un campo in base al valore di una variabile
-- =============================================
WHERE bar_2 = CASE 
WHEN @local_var = 10
THEN 1
ELSE 0
END
-- =============================================
-- il valore del campo non deve esistere in una altra tabella
-- =============================================
AND (
field_name NOT IN (
SELECT foo
FROM bar
)
)
-- =============================================
-- confronto date validitΓ  con la data attuale
-- =============================================
AND GetDate() BETWEEN ValidoDal
AND ValidoAl
-- =============================================
-- Ordinamento custom: Quelle che si chiamano 'Aperto' vanno per prime, e così via
-- =============================================
ORDER BY CASE col
WHEN 'Aperto'
THEN 1
WHEN 'Chiuso'
THEN 2
ELSE 3
END

IF

-- =============================================
-- se esiste alemno un record in una tabella
-- =============================================
IF EXISTS(SELECT TOP 1 1 FROM foo WHERE bar = @var)
-- =============================================
-- Se una variabile Γ¨ uguale al risultato di una SELECT
-- ============================================= 
IF @var_2 = ( SELECT TOP 1 foo FROM bar WHERE field_= @var )
-- =============================================
-- controllo l'esistenza di variabili
-- =============================================
IF ISNULL(@string_var, '') = ''
IF ISNULL(@int_var, 0) = 0
-- =============================================
-- controlla se i risultati di un SELECT sono un sottoinsieme dei risultati di una seconda query
-- =============================================
IF (SELECT foo FROM bar WHERE field_name = 'string') IN (SELECT DISTINCT field_1 From bar_1 where id = @var)
-- =============================================
-- Se una variabile non Γ¨ settata la setto a 1 se esiste una config corrispondente, altrimenti 0
-- =============================================
IF ISNULL(@bool_var, 0) = 0
BEGIN
SET @bool_var = CASE WHEN EXISTS (SELECT TOP 1 1 FROM dbo.Config WHERE IDConfig = 'OENext_BancheMultiple' AND VConfig = '1') THEN 0 ELSE 1 END;
END
-- =============================================
-- Se il numero di record forniti da una query Γ¨ > di k
-- =============================================
IF ( SELECT COUNT(*) FROM foo WHERE bar=@var ) > 10
-- =============================================
-- Se il valore di ritorno di una SELECT Γ¨ null
-- =============================================
IF ( SELECT TOP 1 ISNULL(field_that_could_be_null, '') FROM bar ) = ''
-- =============================================
-- Confronto una parte di una stringa con un intero
-- =============================================
IF(CAST(SUBSTRING(@CFisc, 10, 2) AS INT) > 40)

Extra