Sino alla versione 2005, SQL Server forniva unicamente il tipo datetime
per memorizzare date e orari: anche se volevamo salvare solo una data (ad esempio la data di nascita di una persona), si doveva comunque inserire un orario non significativo, con un conseguente spreco di spazio di archiviazione e la necessità di ricorrere a particolari "accorgimenti" per effettuare ricerche in campi di questo tipo (come vedremo meglio nel seguito).
Nota: gli esempi commentati in questo articolo sono disponibili nel file zip allegato.
Analogamente, anche quando ci interessava esclusivamente un orario, tale informazione doveva essere accompagnata una data fittizia (a patto, naturalmente, di non salvare le date e gli orari in campi di tipo VARCHAR).
SQL Server 2008 risolve questi problemi introducendo nuovi tipi di dati per la gestione di date e orari, ovvero date
, time
, datetime2
e datetimeoffset
. Essi sono pienamente conformi allo standard SQL, quindi garantiscono la massima portabilità. Come i loro nomi lasciano intuire, i primi due consentono di memorizzare separatamente una data oppure un orario, mentre il datetime2
è una sorta di estensione del datetime
tradizionale, di cui aumenta l'intervallo di valori consentiti e la precisione. Il datetimeoffset
, infine, aggiunge al datetime2
la possibilità di indicare il fuso di orario di appartenenza.
Questi nuovi tipi di dati sono supportati da tutte le versioni di SQL Server 2008, compresa l'edizione Express. Per utilizzarli con i linguaggi di programmazione .NET, è necessario utilizzare Visual Studio 2008 aggiornato al Service Pack 1 o versione successiva.
Caratteristiche dei nuovi tipi di dati
Le due tabelle seguenti, prese direttamente dalla documentazione in linea, mostrano le caratteristiche principali dei nuovi tipi di dati, a confronto con quelle dei tipi esistenti:
Tipo di dati | Formato | Intervallo |
---|---|---|
time | hh:mm:ss[.nnnnnnn] | da 00:00:00.0000000 a 23:59:59.9999999 |
date | YYYY-MM-DD | da 0001-01-01 a 9999-12-31 |
smalldatetime | YYYY-MM-DD hh:mm:ss | da 1900-01-01 a 2079-06-06 |
datetime | YYYY-MM-DD hh:mm:ss[.nnn] | da 1753-01-01 a 9999-12-31 |
datetime2 | YYYY-MM-DD hh:mm:ss[.nnnnnnn] | da 0001-01-01 00:00:00.0000000 a 9999-12-31 23:59:59.9999999 |
datetimeoffset | YYYY-MM-DD hh:mm:ss[.nnnnnnn] [+|-]hh:mm | da 0001-01-01 00:00:00.0000000 a 9999-12-31 23:59:59.9999999 (in UTC) |
Tipo di dati | Accuratezza | Dimensioni (in byte) | Altro |
---|---|---|---|
time | 100 nanosecondi | da 3 a 5 | |
date | 1 giorno | 3 | |
smalldatetime | 1 minuto | 4 | |
datetime | 0,00333 secondi | 8 | |
datetime2 | 100 nanosecondi | da 6 a 8 | |
datetimeoffset | 100 nanosecondi | da 8 a 10 | Gestisce i fusi orari |
Da notare l'aumento di precisione del tipo datetime2
rispetto al datetime
: da 0,00333 secondi si passa a soli 100 nanosecondi.
Nella pagina successiva vedremo gli esempi pratici per recuperare la data da un set di dati.
Recuperare data e ora
I seguenti esempi mostrano come recuperare solo la data oppure l'ora corrente a partire dal risultato della funzione GETDATE()
:
DECLARE @date as DATE, @time AS TIME
SET @date = GETDATE()
SET @time = GETDATE()
SELECT @date AS [Date], @time AS [Time]
Risultato:
Date Time 2010-05-17 22:13:14.1630000
Inoltre, la funzione GETDATE()
è stata affiancata da una nuova versione, chiamata SYSTDATETIME()
: anche quest'ultima restituisce la data e l'ora del computer in cui è in esecuzione l'istanza di SQL Server, ma lo fa in formato datetime2
, quindi con una precisione maggiore rispetto al valore ottenuto con GETDATE()
.
La funzione SYSTEDATETIMEOFFSET()
, invece, è una novità di SQL Server 2008 e restituisce la data e l'ora corrente includendo il fuso orario. Possiamo verificare tali comportamenti con un semplice esempio:
SELECT GETDATE() As OldDateFormat, SYSDATETIME() As NewDateFormat, SYSDATETIMEOFFSET() As [DateTimeOffset]
Questa query produrrà un risultato simile al seguente:
OldDateFormat NewDateFormat DateTimeOffset 2010-04-16 20:51:31.013 2010-04-16 20:51:31.0156250 2010-04-16 20:51:31.0156250 +02:00
Nella pagina successiva vedremo come, lavorando con i nuovi tipi di dato, sia possibile aggirare le limitazioni del vecchio SQL Server.
Lavorare con i nuovi tipi di dati
Il problema
L'introduzione di tipi separati per trattare date e orari in SQL Server 2008 risolve uno dei problemi "storici" di questo database engine, ovvero la necessità di usare"trucchetti" per effettuare ricerche basate su una data o su intervalli di date.
Supponiamo di avere la seguente tabella, in cui la colonna Consegna è di tipo datetime:
ID Prodotto Consegna 1 Gomma 2010-05-22 00:00:00.000 2 Matita 2010-05-22 15:35:00.000 3 Penna 2010-05-21 00:00:00.000 4 Quaderno 2010-05-21 00:00:00.000 5 Compasso 2010-05-21 10:21:00.000 6 Righello 2010-05-22 9:50:45.000
Supponiamo di voler cercare tutti i prodotti consegnati il giorno 21 maggio 2010. Potremmo pensare di scrivere qualcosa del tipo:
SELECT * FROM DateTimeExample WHERE Consegna = '2010-05-21'
Tale interrogazione, tuttavia, restituisce solo due risultati, ovvero Penna e Quaderno, ma non il Compasso. Questo avviene perché, facendo una ricerca in un campo datetime
, SQL Server tenta innanzi tutto di convertire la stringa di ricerca ('2010-05-21' nell'esempio) in un valore datetime
, aggiungendovi automaticamente il valore 00:00:00.000, ovvero mezzanotte, se non specificato altrimenti.
Nel caso di Penna e Quaderno, l'orario di consegna è proprio la mezzanotte (e dunque rientra nella condizione indicata nella clausola WHERE
), mentre il Compasso ha un'ora diversa, per cui non appare tra i risultati dell'interrogazione.
Proviamo quindi ad indicare esplicitamente anche gli orari che ci interessano:
SELECT * FROM DateTimeExample WHERE Consegna >= '2010-05-21 00:00:00.000' AND Consegna <= '2010-05-21 23:59:59.999'
Ora stiamo cercando tutti i prodotti consegnati dalla mezzanotte alle 23:59:59.999 del giorno 21 maggio 2010. Anche questa interrogazione, però, non produce il risultato desiderato: oltre a Penna, Quaderno e Compasso, tra di essi compare anche Gomma, che ha data di consegna uguale a '2010-05-22 00:00:00.000'.
Tale comportamento è dovuto alla precisione del tipo datetime
, che è di 0,00333 secondi, con la conseguenza che il valore '2010-05-21 23:59:59.999' viene "arrotondato" a '2010-05-22 00:00:00.000'.
In generale, se vogliamo solo i prodotti consegnati in un certo giorno, o, più in generale, solo in un intervallo di date, lavorando con una colonna di tipo datetime
dobbiamo partire dalla mezzanotte della data minore fino all'ultimo "tick" della data maggiore (23:59:59.997). Nel nostro esempio:
SELECT * FROM DateTimeExample WHERE Consegna >= '2010-05-21 00:00:00.000' AND Consegna <= '2010-05-21 23:59:59.997'
In alternativa a questo "accorgimento", è possibile utilizzare funzioni di conversione all'interno della clausola WHERE
per estrarre solo la porzione di data da un campo datetime. Questa soluzione, però, dovendo applicare una trasformazione a tutti i record elaborati, risulta inefficiente dal punto di vista delle prestazioni.
Nella pagina successiva vedremo la soluzione a questo noioso problema.
La soluzione
Tutti questi problemi si risolvono utilizzando il nuovo campo date
. Supponendo di aggiungere una colonna con nome DataConsegna e tipo date, per ottenere tutti i prodotti consegnati il giorno 21 maggio 2010, ci basterà scrivere:
SELECT * FROM DateTimeExample WHERE DataConsegna = '2010-05-21'
Per ottenere finalmente Penna, Quaderno e Compasso, senza dover utilizzare accorgimenti particolari o funzioni di conversione che penalizzano l'efficienza.
Le considerazioni fatte finora sono valide anche quando dobbiamo gestire solo informazioni sugli orari: con SQL Server 2005, se utilizziamo un campo datetime
, anche se non siamo interessati all'indicazione del giorno, di fatto siamo obbligati a specificare comunque una data non significativa. Ancora una volta, lavorando con SQL Server 2008 abbiamo la possibilità di utilizzare un campo time
, al cui interno inseriremo solo le informazioni che effettivamente ci servono.
Come sappiamo, SQL Server mette a disposizione una serie di funzioni per lavorare con date e orari. Tali funzioni sono state aggiornate per supportare i nuovi tipi di dati introdotti dalla versione 2008. Questo significa che è possibile usare i classici metodi DATEADD
, DATEDIFF
, DATEPART
, YEAR
, MONTH
, DAY
, ecc. anche con date
, datetime2
e datetimeoffset
.
Diamo un'occhiata al seguente esempio e al corrispondente risultato:
SELECT GETDATE() As OldTodayFormat,
DATEADD(d, 1, GETDATE()) As OldTomorrowFormat,
SYSDATETIME() As NewTodayFormat,
DATEADD(d, 1, SYSDATETIME()) AS NewTomorrowFormat
Risultato:
OldTodayFormat OldTomorrowFormat NewTodayFormat NewTomorroFormat 2010-05-14 14:09:42.830 2010-05-15 14:09:42.830 2010-05-14 14:09:42.8318750 2010-05-15 14:09:42.8318750
Abbiamo utilizzato la funzione DATEADD
per aggiungere un giorno alla data corrente: quest'ultima viene recuperata sia in formato datetime
(OldTodayFormat) sia datetime2
(NewTodayFormat). In base a questo formato, la funzione DATEADD
restituirà anch'essa un valore sotto forma di datetime
(OldTomorrowFormat) oppure datetime2
(NewTomorrowFormat).
Tutte le altre funzioni si comportano nel solito modo a cui siamo abituati con SQL Server 2005, l'unica differenza è nella precisione dei valori restituiti.
Tutti gli esempi presentati nell'articolo sono disponibili nel file allegato.
Conclusioni
In questo articolo abbiamo presentato i nuovi tipi di dati introdotti da SQL Server 2008 per trattare le date e gli orari. Essi colmano una della "lacune" storiche di questo DBMS e consentono, da una parte, di facilitare la gestione di questo tipo di informazioni , dall'altra di ottenere un'efficienza maggiore e ottimizzare lo spazio di archiviazione, poiché consentono di specificare con maggiore precisione quali dati interessa mantenere.
Anche la documentazione ufficiale di MSDN suggerisce di utilizzare i campi time
, date
, datetime2
e datetimeoffset
per lo sviluppo di nuove applicazioni, proprio in virtù dei vantaggi che portano.