Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

datetime2 vs smalldatetime in SQL Server:qual è la differenza?

Questo articolo esplora le principali differenze tra datetime2 e smalldatetime tipi di dati in SQL Server.

Entrambi i tipi di dati vengono utilizzati per memorizzare i valori di data e ora, tuttavia esistono alcune differenze importanti tra i due. Nella maggior parte dei casi è meglio usare datetime2 (Microsoft consiglia anche questo), tuttavia potrebbero esserci alcuni scenari in cui è necessario utilizzare smalldatetime .

Ecco una tabella che delinea le differenze principali tra questi due tipi.

Funzione smalldatetime datetime2
Conforme a SQL (ANSI e ISO 8601) No
Intervallo di date 01-01-1900 fino a 06-06-2079 0001-01-01 al 31-12-9999
Intervallo di tempo dalle 00:00:00 alle 23:59:59 00:00:00 alle 23:59:59.9999999
Lunghezza del personaggio 19 posizioni al massimo 19 posizioni minimo
27 massimo
Dimensioni di archiviazione 4 byte, fissi Da 6 a 8 byte, a seconda della precisione*

* Più 1 byte per memorizzare la precisione

Precisione Un minuto 100 nanosecondi
Precisione frazionaria dei secondi No
Precisione al secondo frazionario definita dall'utente No
Offset fuso orario Nessuno Nessuno
Consapevolezza e conservazione della differenza di fuso orario No No
Attenzione all'ora legale No No

Vantaggi di "datetime2"

Come si vede nella tabella sopra, il datetime2 type ha molti vantaggi rispetto a smalldatetime , tra cui:

  • intervallo di date più ampio
  • Precisione di secondi frazionari
  • precisione specificata dall'utente opzionale
  • maggiore precisione
  • si allinea con gli standard SQL (ANSI e ISO 8601)

* In alcuni casi un datetime2 value utilizza un byte aggiuntivo per memorizzare la precisione, tuttavia, quando viene archiviata in un database, la precisione è inclusa nella definizione della colonna, quindi il valore effettivo memorizzato non richiede il byte aggiuntivo.

Devo usare "datetime" o "smalldatetime"?

Microsoft consiglia datetime2 per nuovi lavori (e per gli stessi motivi sopra elencati).

Pertanto, dovresti utilizzare datetime2 , a meno che tu non abbia un motivo specifico per non farlo (come lavorare con un sistema legacy).

Esempio 1 – Confronto di base

Ecco un rapido esempio per dimostrare la differenza fondamentale tra datetime2 e smalldatetime .

DECLARE 
  @thedatetime2 datetime2(7), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.5555555';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Risultato:

+-----------------------------+---------------------+
| datetime2                   | smalldatetime       |
|-----------------------------+---------------------|
| 2025-05-21 10:15:30.5555555 | 2025-05-21 10:16:00 |
+-----------------------------+---------------------+

Qui ho impostato un smalldatetime variabile allo stesso valore di datetime2 variabile. Questo fa sì che il valore venga convertito in smalldatetime e possiamo quindi utilizzare un SELECT istruzione per vedere il valore di ciascuna variabile.

In questo caso, il datetime2 variabile usa una scala di 7, il che significa che ha 7 cifre decimali. Il smalldatetime valore d'altra parte, non ne ha nessuno decimali. Inoltre, i suoi secondi vengono impostati su zero e i minuti vengono arrotondati per eccesso.

Questo è prevedibile, perché la documentazione ufficiale di Microsoft afferma che smalldatetime L'ora di 's è basata su un giorno di 24 ore, con secondi sempre zero (:00) e senza secondi frazionari .

Quindi possiamo vedere che il datetime2 type fornisce un valore di data/ora molto più preciso e accurato.

Certo, potresti non aver bisogno di tutti quei secondi frazionari. Uno degli aspetti positivi di datetime2 è che puoi specificare quanti (se presenti) secondi frazionari vuoi.

Esempio 2:utilizzo di posizioni con meno decimali

In questo esempio riduco datetime2 scala a 0:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.5555555';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Risultato:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:31 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

In questo caso, il datetime2 il valore non include più una parte frazionaria. Entrambi i tipi ora condividono la stessa lunghezza di caratteri (19 posizioni).

Ma ci sono ancora differenze.

La dataora2 value rispetta il valore dei secondi, anche se in questo caso i suoi secondi sono stati arrotondati per eccesso. Come accennato, il smalldatetime la componente dei secondi del valore è sempre impostata su zero e, in questo caso, i suoi minuti sono stati arrotondati per eccesso.

Il motivo per datetime2 il componente dei secondi è arrotondato per eccesso perché la parte frazionaria è 5 o superiore. Se riduciamo la parte frazionaria, non viene eseguito alcun arrotondamento:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.4444444';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Risultato:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

Tuttavia, il smalldatetime i minuti di valore continuano ad essere arrotondati per eccesso.

Esempio 3 – Impostazione di valori da stringhe letterali

Negli esempi precedenti, il smalldatetime il valore è stato assegnato impostandolo sullo stesso valore di datetime2 valore. Quando lo facciamo, SQL Server esegue una conversione implicita in modo che i dati "si adattino" al nuovo tipo di dati.

Tuttavia, se proviamo ad assegnare la stessa stringa letterale a smalldatetime variabile, otteniamo un errore:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime
SET @thedatetime2 = '2025-05-21 10:15:30.4444444'
SET @thesmalldatetime = '2025-05-21 10:15:30.4444444'
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Risultato:

Msg 295, Level 16, State 3, Line 5
Conversion failed when converting character string to smalldatetime data type.

Questo perché smalldatetime accetta solo stringhe letterali che hanno 3 o meno secondi frazionari.

Potresti aspettarti che non accetti stringhe letterali con qualsiasi secondi frazionari, visto che non include secondi frazionari, ma non è così. Accetta felicemente 3 secondi frazionari, ma non di più.

Quindi, per ovviare a questo problema, dobbiamo ridurre la parte frazionaria a sole 3 (o meno) cifre decimali.

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.4444444';
SET @thesmalldatetime = '2025-05-21 10:15:30.444';
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Risultato:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

La dataora2 type non ha questa limitazione, anche quando si utilizza una scala di 0.

Esempio 4 – Dimensioni di archiviazione

Il smalldatetime il tipo di dati ha una dimensione di archiviazione fissa di 4 byte. Questo è uno dei pochi vantaggi smalldatetime ha più di datetime2 .

La dataora2 può essere 6, 7 o 8 byte, a seconda della sua precisione. Quindi un datetime2 value utilizzerà sempre almeno 2 byte di spazio di archiviazione in più rispetto a smalldatetime valore.

Microsoft afferma che datetime2 type utilizza anche 1 byte in più per memorizzare la sua precisione, nel qual caso utilizzerebbe almeno 3 byte in più rispetto a smalldatetime .

Tuttavia, questo probabilmente dipende dal fatto che lo stiamo memorizzando in una tabella o in una variabile e se lo stiamo convertendo o meno in una costante binaria.

Ecco cosa succede se utilizziamo DATALENGTH() funzione per restituire il numero di byte utilizzati per ciascuno dei nostri valori:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  DATALENGTH(@thedatetime2) AS 'datetime2',
  DATALENGTH(@thesmalldatetime) AS 'smalldatetime';

Risultato

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 6           | 4               |
+-------------+-----------------+

Ma se li convertiamo in varbinary , otteniamo quanto segue:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  DATALENGTH(CAST(@thedatetime2 AS varbinary(10))) AS 'datetime2',
  DATALENGTH(CAST(@thesmalldatetime AS varbinary(10))) AS 'smalldatetime';

Risultato

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 7           | 4               |
+-------------+-----------------+

Quindi dataora2 utilizza un byte in più quando viene convertito in varbinary . Molti sviluppatori presumono che la conversione in varbinary è rappresentativo del modo in cui SQL Server archivia effettivamente i valori di data e ora.

Questo è vero solo in parte però. Sebbene sia vero che SQL Server archivia i valori di data e ora in formato esadecimale, tale valore esadecimale in realtà non include la precisione. Questo perché la precisione è inclusa nella definizione della colonna. Ma quando ci convertiamo in varbinary come abbiamo fatto nell'esempio precedente, la precisione è anteposta e questo aggiunge un byte in più.

L'esempio seguente lo dimostra. Mostra che quando i dati sono archiviati in una colonna del database, otteniamo una lunghezza di 6 byte per datetime2 rispetto a 4 byte per smalldatetime .

Esempio 5 – Dimensioni di archiviazione per i dati archiviati

In questo esempio creo un database e utilizzo COL_LENGTH per restituire la lunghezza di ciascuna colonna, in byte. Quindi inserisco un datetime2 e smalldatetime valore in esso e utilizzare DBCC PAGE() per trovare la lunghezza dei dati effettivi nel file di paging. Questo ci mostra lo spazio di archiviazione utilizzato da ciascun tipo di dati quando viene archiviato in un database.

Crea un database:

CREATE DATABASE CompareTypes;

Crea una tabella:

USE CompareTypes;

CREATE TABLE Datetime2vsSmalldatetime (
    TheDateTime2 datetime2(0),
    TheSmallDateTime smalldatetime
    );

In questo caso creo due colonne:una è un datetime2(0) colonna e l'altro è un smalldatetime colonna.

Controlla la lunghezza della colonna

Controlla la lunghezza (in byte) di ogni colonna:

SELECT 
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheDateTime2' ) AS 'datetime2',
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheSmallDateTime' ) AS 'smalldatetime';  

Risultato:

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 6           | 4               |
+-------------+-----------------+

Quindi vediamo che datetime2(0) colonna ha una lunghezza di 6 byte, rispetto a smalldatetime è di 4 byte.

Inserisci dati

Ora esaminiamo le dimensioni di archiviazione dei valori di data e ora effettivi quando sono archiviati in SQL Server. Possiamo usare DBCC PAGE() per controllare la pagina effettiva nel file di dati.

Ma prima dobbiamo inserire i dati nelle nostre colonne.

Inserisci dati:

DECLARE @thedatetime2 datetime2 = '2025-05-21 10:15:30';
INSERT INTO Datetime2vsSmalldatetime ( TheSmallDateTime, TheDateTime2 )
SELECT @thedatetime2, @thedatetime2;

Seleziona i dati (solo per verificarli):

SELECT * FROM Datetime2vsSmalldatetime;

Risultato:

+---------------------+---------------------+
| TheDateTime2        | TheSmallDateTime    |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

Utilizzo di DBCC PAGE()

Qui è dove usiamo DBCC PAGE() per controllare la pagina effettiva nel file di dati.

Per prima cosa, utilizzeremo DBCC IND() per trovare il PagePID:

DBCC IND('CompareTypes', 'dbo.Datetime2vsSmalldatetime', 0);

Risultato (usando l'output verticale):

-[ RECORD 1 ]-------------------------
PageFID         | 1
PagePID         | 308
IAMFID          | NULL
IAMPID          | NULL
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
iam_chain_type  | In-row data
PageType        | 10
IndexLevel      | NULL
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0
-[ RECORD 2 ]-------------------------
PageFID         | 1
PagePID         | 344
IAMFID          | 1
IAMPID          | 308
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
iam_chain_type  | In-row data
PageType        | 1
IndexLevel      | 0
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0

Questo restituisce due record. Siamo interessati al PageType di 1 (il 2° record). Vogliamo il PagePID da quel record. In questo caso il PagePID è 344 .

Ora possiamo prendere quel PagePID e usarlo nel modo seguente:

DBCC TRACEON(3604, -1);
DBCC PAGE(CompareTypes, 1, 344, 3);

Questo produce molti dati, ma siamo principalmente interessati alla parte seguente:

Slot 0 Column 1 Offset 0x4 Length 6 Length (physical) 6

TheDateTime2 = 2025-05-21 10:15:30  

Slot 0 Column 2 Offset 0xa Length 4 Length (physical) 4

TheSmallDateTime = 2025-05-21 10:16:00.000                                          

Questo mostra che smalldatetime ha una lunghezza di 4 byte e datetime2(0) ha 6 byte quando è archiviato in un database.

Quindi, in questo caso, c'è solo una differenza di 2 byte, ma datetime2(0) è più preciso e aderisce agli standard ANSI e ISO 8601.