Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

datetime2 vs smalldatetime dans SQL Server :quelle est la différence ?

Cet article explore les principales différences entre le datetime2 et smalldatetime types de données dans SQL Server.

Les deux types de données sont utilisés pour stocker les valeurs de date et d'heure, cependant, il existe des différences importantes entre les deux. Dans la plupart des cas, il vaut mieux utiliser datetime2 (Microsoft le recommande également), mais il peut y avoir des scénarios où vous devez utiliser smalldatetime .

Voici un tableau qui décrit les principales différences entre ces deux types.

Fonctionnalité smalldatetime datetime2
Conformité SQL (ANSI et ISO 8601) Non Oui
Plage de dates 1900-01-01 à 2079-06-06 0001-01-01 à 9999-12-31
Plage de temps 00:00:00 à 23:59:59 00:00:00 à 23:59:59.9999999
Longueur des caractères 19 postes maximum 19 postes minimum
27 maximum
Taille de stockage 4 octets, fixe 6 à 8 octets, selon la précision*

* Plus 1 octet pour stocker la précision

Précision Une minute 100 nanosecondes
Précision fractionnaire de seconde Non Oui
Précision fractionnaire de seconde définie par l'utilisateur Non Oui
Décalage du fuseau horaire Aucun Aucun
Conscience et préservation du décalage horaire Non Non
Respect de l'heure d'été Non Non

Avantages de "datetime2"

Comme indiqué dans le tableau ci-dessus, le datetime2 type présente de nombreux avantages par rapport à smalldatetime , y compris :

  • plage de dates plus large
  • précision fractionnaire de seconde
  • précision facultative spécifiée par l'utilisateur
  • plus grande précision
  • conforme aux normes SQL (ANSI et ISO 8601)

* Dans certains cas, un datetime2 La valeur utilise un octet supplémentaire pour stocker la précision, mais lorsqu'elle est stockée dans une base de données, la précision est incluse dans la définition de la colonne, de sorte que la valeur stockée réelle ne nécessite pas l'octet supplémentaire.

Dois-je utiliser "datetime" ou "smalldatetime" ?

Microsoft recommande datetime2 pour un nouveau travail (et pour les mêmes raisons énumérées ci-dessus).

Par conséquent, vous devez utiliser datetime2 , sauf si vous avez une raison spécifique de ne pas le faire (par exemple, si vous travaillez avec un ancien système).

Exemple 1 - Comparaison de base

Voici un exemple rapide pour démontrer la différence fondamentale entre datetime2 et 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';

Résultat :

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

Ici, j'ai mis un smalldatetime variable à la même valeur que datetime2 variable. Cela entraîne la conversion de la valeur en smalldatetime et nous pouvons alors utiliser un SELECT instruction pour voir la valeur de chaque variable.

Dans ce cas, la datetime2 variable utilise une échelle de 7, ce qui signifie qu'elle a 7 décimales. Le petitedateheure valeur d'autre part, n'en a pas aucune décimales. De plus, ses secondes sont mises à zéro et ses minutes sont arrondies.

Il faut s'y attendre, car la documentation officielle de Microsoft indique que smalldatetime L'heure est basée sur une journée de 24 heures, avec des secondes toujours à zéro (:00) et sans fraction de seconde .

Nous pouvons donc voir que le datetime2 type fournit une valeur de date/heure beaucoup plus précise et exacte.

Bien sûr, vous n'aurez peut-être pas besoin de toutes ces fractions de secondes. L'un des avantages de datetime2 est que vous pouvez spécifier combien de secondes fractionnaires (le cas échéant) vous voulez.

Exemple 2 - Utiliser moins de décimales

Dans cet exemple, je réduis le datetime2 échelle à 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';

Résultat :

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

Dans ce cas, la datetime2 la valeur n'inclut plus de partie fractionnaire. Les deux types partagent désormais la même longueur de caractères (19 positions).

Mais il y a encore des différences.

Le datetime2 respecte la valeur des secondes, bien que dans ce cas ses secondes aient été arrondies. Comme mentionné, le smalldatetime le composant secondes de la valeur est toujours défini sur zéro, et dans ce cas, ses minutes ont été arrondies.

La raison pour laquelle datetime2 seconde est arrondie parce que la partie fractionnaire est égale ou supérieure à 5. Si nous réduisons la partie fractionnaire, aucun arrondi n'est effectué :

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';

Résultat :

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

Cependant, le smalldatetime les minutes de valeur continuent d'être arrondies.

Exemple 3 - Définition de valeurs à partir de littéraux de chaîne

Dans les exemples précédents, le smalldateime la valeur a été attribuée en la définissant sur la même valeur que datetime2 valeur. Lorsque nous faisons cela, SQL Server effectue une conversion implicite afin que les données « correspondent » au nouveau type de données.

Cependant, si nous essayons d'attribuer ce même littéral de chaîne au smalldatetime variable, nous obtenons une erreur :

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';

Résultat :

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

C'est parce que smalldatetime accepte uniquement les littéraux de chaîne qui ont 3 secondes fractionnaires ou moins.

Vous pourriez vous attendre à ce qu'il n'accepte pas les littéraux de chaîne avec any fractions de seconde, vu qu'il n'inclut pas les fractions de seconde, mais ce n'est pas le cas. Il accepte volontiers 3 fractions de seconde, mais pas plus.

Donc, pour surmonter ce problème, nous devons réduire la partie fractionnaire à seulement 3 décimales (ou moins).

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';

Résultat :

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

Le datetime2 type n'a pas cette limitation, même en utilisant une échelle de 0.

Exemple 4 - Taille de stockage

Le petitedateheure Le type de données a une taille de stockage fixe de 4 octets. C'est l'un des rares avantages smalldatetime a plus de datetime2 .

Le datetime2 peut être de 6, 7 ou 8 octets, selon sa précision. Donc un datetime2 la valeur utilisera toujours au moins 2 octets de stockage de plus qu'un smalldatetime valeur.

Microsoft déclare que le datetime2 type utilise également 1 octet supplémentaire afin de stocker sa précision, auquel cas il utiliserait au moins 3 octets de plus que smalldatetime .

Cependant, cela dépend probablement si nous le stockons dans une table ou dans une variable, et si nous le convertissons ou non en une constante binaire.

Voici ce qui se passe si nous utilisons le DATALENGTH() fonction pour retourner le nombre d'octets utilisés pour chacune de nos valeurs :

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';

Résultat

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

Mais si nous les convertissons en varbinary , nous obtenons ceci :

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';

Résultat

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

Donc datetime2 utilise un octet supplémentaire lors de la conversion en varbinary . De nombreux développeurs supposent que la conversion en varbinary est représentatif de la manière dont SQL Server stocke réellement les valeurs de date et d'heure.

Ce n'est cependant que partiellement vrai. S'il est vrai que SQL Server stocke ses valeurs de date et d'heure au format hexadécimal, cette valeur hexadécimale n'inclut pas réellement la précision. En effet, la précision est incluse dans la définition de la colonne. Mais lorsque nous convertissons en varbinary comme nous l'avons fait dans l'exemple précédent, la précision est ajoutée, ce qui ajoute un octet supplémentaire.

L'exemple suivant le démontre. Il montre que lorsque les données sont stockées dans une colonne de base de données, nous obtenons une longueur de 6 octets pour datetime2 vs 4 octets pour smalldatetime .

Exemple 5 - Taille de stockage pour les données stockées

Dans cet exemple, je crée une base de données et j'utilise COL_LENGTH pour renvoyer la longueur de chaque colonne, en octets. J'insère ensuite un datetime2 et smalldatetime valeur et utilisez DBCC PAGE() pour trouver la longueur des données réelles dans le fichier d'échange. Cela nous montre l'espace de stockage que chaque type de données utilise lorsqu'il est stocké dans une base de données.

Créer une base de données :

CREATE DATABASE CompareTypes;

Créer un tableau :

USE CompareTypes;

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

Dans ce cas, je crée deux colonnes - l'une est une datetime2(0) colonne et l'autre est un smalldatetime colonne.

Vérifiez la longueur de la colonne

Vérifiez la longueur (en octets) de chaque colonne :

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

Résultat :

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

Nous voyons donc que le datetime2(0) la colonne a une longueur de 6 octets, par rapport à smalldatetime est de 4 octets.

Insérer des données

Examinons maintenant la taille de stockage des valeurs de date et d'heure réelles lorsqu'elles sont stockées dans SQL Server. Nous pouvons utiliser DBCC PAGE() pour inspecter la page réelle dans le fichier de données.

Mais d'abord, nous devons insérer des données dans nos colonnes.

Insérer des données :

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

Sélectionnez les données (juste pour les vérifier) :

SELECT * FROM Datetime2vsSmalldatetime;

Résultat :

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

Utilisation de DBCC PAGE()

Voici où nous utilisons DBCC PAGE() pour inspecter la page réelle dans le fichier de données.

Tout d'abord, nous allons utiliser DBCC IND() pour trouver le PagePID :

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

Résultat (en utilisant la sortie 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

Cela renvoie deux enregistrements. Nous sommes intéressés par le PageType de 1 (le 2e enregistrement). Nous voulons le PagePID de cet enregistrement. Dans ce cas, le PagePID est 344 .

Nous pouvons maintenant prendre ce PagePID et l'utiliser dans ce qui suit :

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

Cela produit beaucoup de données, mais nous nous intéressons principalement à la partie suivante :

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                                          

Cela montre que smalldatetime a une longueur de 4 octets et datetime2(0) a 6 octets lorsqu'il est stocké dans une base de données.

Donc dans ce cas, il n'y a qu'une différence de 2 octets, mais datetime2(0) est plus précis et respecte les normes ANSI et ISO 8601.