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

Comprendre la taille de stockage « temporelle » dans SQL Server

Dans cet article, je regarde la taille de stockage du temps type de données dans SQL Server.

En particulier, je regarde ce qui suit :

  • Documentation Microsoft
  • Données stockées dans une variable
    • Longueur en octets en utilisant DATALENGTH()
    • Longueur en octets en utilisant DATALENGTH() après conversion en varbinary
  • Données stockées dans une base de données
    • Longueur en octets en utilisant COL_LENGTH()
    • Longueur en octets en utilisant DBCC PAGE()

Documentation Microsoft

Documentation officielle de Microsoft sur l'heure Le type de données indique que sa taille de stockage est comprise entre 3 et 5 octets, selon la précision utilisée.

Ce type de données permet une précision définie par l'utilisateur. Vous pouvez utiliser time(n) pour spécifier la précision, où n est une échelle entre 0 et 7.

Voici les données que Microsoft présente pour le temps type de données :

Échelle spécifiée Résultat (précision, échelle) Longueur de la colonne (octets) Précision fractionnaire de seconde
temps (16,7) 5 7
heure(0) (8,0) 3 0-2
temps(1) (10,1) 3 0-2
temps(2) (11,2) 3 0-2
temps(3) (12,3) 4 3-4
temps(4) (13,4) 4 3-4
temps(5) (14,5) 5 5-7
temps(6) (15,6) 5 5-7
temps(7) (16,7) 5 5-7

Pour les besoins de cet article, je m'intéresse principalement à la Longueur de la colonne (octets) colonne. Cela nous indique combien d'octets sont utilisés pour stocker ce type de données dans une base de données.

Du point de vue de l'utilisateur, le temps le type de données fonctionne de la même manière que la partie heure de datetime2 . Il a une précision fractionnaire de seconde définie par l'utilisateur et accepte une échelle de 0 à 7.

Le reste de cet article passe par divers exemples où je renvoie la taille de stockage de time valeurs dans différents contextes.

Données stockées dans une variable

Tout d'abord, je vais stocker un temps valeur dans une variable et vérifier sa taille de stockage. Ensuite, je convertirai cette valeur en varbinary et revérifiez.

Longueur en octets en utilisant DATALENGTH

Voici ce qui se passe si nous utilisons le DATALENGTH() fonction pour renvoyer le nombre d'octets utilisés pendant un time(7) valeur :

DECLARE @t time(7);
SET @t = '10:15:30.1234567';
SELECT 
  @t AS 'Value',
  DATALENGTH(@t) AS 'Length in Bytes';

Résultat

+------------------+-------------------+
| Value            | Length in Bytes   |
|------------------+-------------------|
| 10:15:30.1234567 | 5                 |
+------------------+-------------------+

La valeur dans cet exemple a l'échelle maximale de 7 (parce que je déclare la variable comme time(7) ), et il renvoie une longueur de 5 octets.

Ceci est normal, car il correspond à la taille de stockage indiquée dans le tableau de Microsoft.

Cependant, si nous convertissons la valeur en varbinary nous obtenons un résultat différent.

Longueur en octets après conversion en 'varbinary'

Certains développeurs aiment convertir le temps ou datetime2 variables en varbinary car il est plus représentatif de la façon dont SQL Server le stocke dans la base de données. Bien que cela soit partiellement vrai, les résultats ne sont pas exactement les mêmes que la valeur stockée (plus d'informations ci-dessous).

Voici ce qui se passe si nous convertissons notre temps valeur en varbinary :

DECLARE @t time(7);
SET @t = '10:15:30.1234567';
SELECT 
  CONVERT(VARBINARY(16), @t) AS 'Value',
  DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';

Résultat

+----------------+-------------------+
| Value          | Length in Bytes   |
|----------------+-------------------|
| 0x0787A311FC55 | 6                 |
+----------------+-------------------+

Dans ce cas, nous obtenons 6 octets. Notre valeur utilise maintenant 1 octet de plus que ce qui est indiqué dans la documentation.

C'est parce qu'il a besoin d'un octet supplémentaire pour stocker la précision.

Il s'agit d'une représentation hexadécimale de l'heure valeur. La valeur de temps réelle (et sa précision) est tout ce qui suit le 0x . Chaque paire de caractères hexadécimaux est un octet. Il y a 6 paires, donc 6 octets. Ceci est confirmé lorsque nous utilisons DATALENGTH() pour renvoyer la longueur en octets.

Dans cet exemple, nous pouvons voir que le premier octet est 07 . Cela représente la précision (j'ai utilisé une échelle de 7 et c'est donc ce qui est affiché ici).

Si je change l'échelle, on peut voir que le premier octet change pour correspondre à l'échelle :

DECLARE @t time(3);
SET @t = '10:15:30.1234567';
SELECT 
  CONVERT(VARBINARY(16), @t) AS 'Value',
  DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';

Résultat

+--------------+-------------------+
| Value        | Length in Bytes   |
|--------------+-------------------|
| 0x034B823302 | 5                 |
+--------------+-------------------+

Nous pouvons également voir que la longueur est réduite en conséquence. Mais encore une fois, c'est un octet de plus que ce que la documentation dit qu'il devrait utiliser.

Bien que la documentation de Microsoft pour le temps ne le mentionne pas explicitement, la documentation pour datetime2 indique ce qui suit :

Le premier octet d'un datetime2 value stocke la précision de la valeur, c'est-à-dire le stockage réel requis pour un datetime2 value est la taille de stockage indiquée dans le tableau ci-dessus plus 1 octet supplémentaire pour stocker la précision. Cela rend la taille maximale d'un datetime2 valeur 9 octets - 1 octet stocke la précision plus 8 octets pour le stockage des données avec une précision maximale.

Et le datetime2 Le type de données fonctionne exactement de la même manière par rapport aux exemples ci-dessus. En d'autres termes, il signale uniquement l'octet supplémentaire lorsqu'il est converti en varbinary .

Ainsi, l'octet supplémentaire mentionné dans la documentation Microsoft semble également s'appliquer à time .

Cependant, la taille de stockage réelle de votre temps les valeurs seront sur l'endroit où les données sont stockées.

Données stockées dans une base de données

Lorsqu'une colonne de base de données a un type de heure , sa précision est spécifiée au niveau de la colonne – non au niveau des données. En d'autres termes, il est spécifié une fois pour toute la colonne. Cela a du sens, car lorsque vous définissez une colonne comme time(7) , vous savez que toutes les lignes seront time(7) . Pas besoin d'utiliser de précieux octets pour répéter ce fait sur chaque ligne.

Lorsque vous examinez un temps valeur telle qu'elle est stockée dans SQL Server, vous verrez qu'elle est identique à la valeur varbinary résultat, mais sans la précision.

Vous trouverez ci-dessous des exemples qui montrent comment le temps les valeurs sont stockées dans SQL Server.

Dans ces exemples, je crée une base de données avec différents time(n) colonnes, puis utilisez COL_LENGTH() pour renvoyer la longueur de chaque colonne, en octets. J'insère ensuite des valeurs dans ces colonnes, avant d'utiliser DBCC PAGE pour vérifier la taille de stockage que chaque fois la valeur occupe le fichier de la page.

Créer une base de données :

CREATE DATABASE Test;

Créer un tableau :

USE Test;

CREATE TABLE TimeTest (
    t0 time(0),
    t1 time(1),
    t2 time(2),
    t3 time(3),
    t4 time(4),
    t5 time(5),
    t6 time(6),
    t7 time(7)
    );

Dans ce cas, je crée huit colonnes - une pour chaque échelle définie par l'utilisateur que nous pouvons utiliser avec time(n) .

Nous pouvons maintenant vérifier la taille de stockage de chaque colonne.

Longueur en octets en utilisant COL_LENGTH()

Utilisez COL_LENGTH() pour vérifier la longueur (en octets) de chaque colonne :

SELECT 
  COL_LENGTH ( 'TimeTest' , 't0' ) AS 't0',
  COL_LENGTH ( 'TimeTest' , 't1' ) AS 't1',
  COL_LENGTH ( 'TimeTest' , 't2' ) AS 't2',
  COL_LENGTH ( 'TimeTest' , 't3' ) AS 't3',
  COL_LENGTH ( 'TimeTest' , 't4' ) AS 't4',
  COL_LENGTH ( 'TimeTest' , 't5' ) AS 't5',
  COL_LENGTH ( 'TimeTest' , 't6' ) AS 't6',
  COL_LENGTH ( 'TimeTest' , 't7' ) AS 't7';  

Résultat :

+------+------+------+------+------+------+------+------+
| t0   | t1   | t2   | t3   | t4   | t5   | t6   | t7   |
|------+------+------+------+------+------+------+------|
| 3    | 3    | 3    | 4    | 4    | 5    | 5    | 5    |
+------+------+------+------+------+------+------+------+

Donc, encore une fois, nous obtenons le même résultat que la documentation indique que nous obtiendrons. C'est normal, car la documentation indique explicitement "Longueur de colonne (octets)", ce qui correspond exactement à ce que nous mesurons ici.

Rappelez-vous, c'est avant nous insérons toutes les données. Les colonnes elles-mêmes déterminent la précision (et donc la taille de stockage) de toutes les données insérées, et non l'inverse.

Utilisez DBCC PAGE pour vérifier les données stockées

Insérons maintenant des données, puis utilisons DBCC PAGE pour trouver la taille de stockage réelle des données que nous stockons dans chaque colonne.

Insérer des données :

DECLARE @t time(7) = '10:15:30.1234567';
INSERT INTO TimeTest ( t0, t1, t2, t3, t4, t5, t6, t7 )
SELECT @t, @t, @t, @t, @t, @t, @t, @t;

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

SELECT * FROM TimeTest;

Résultat (en utilisant la sortie verticale) :

t0 | 10:15:30
t1 | 10:15:30.1000000
t2 | 10:15:30.1200000
t3 | 10:15:30.1230000
t4 | 10:15:30.1235000
t5 | 10:15:30.1234600
t6 | 10:15:30.1234570
t7 | 10:15:30.1234567

Comme prévu, les valeurs utilisent la précision précédemment spécifiée au niveau de la colonne.

Notez que mon système affiche des zéros à la fin. Le vôtre peut le faire ou non. Quoi qu'il en soit, cela n'affecte pas la précision ou l'exactitude réelle.

Maintenant, avant d'utiliser DBCC PAGE() , nous devons savoir quel PagePID lui transmettre. Nous pouvons utiliser DBCC IND() pour le trouver.

Trouver le PagePID :

DBCC IND('Test', 'dbo.TimeTest', 0);

Résultat (en utilisant la sortie verticale) :

-[ RECORD 1 ]-------------------------
PageFID         | 1
PagePID         | 308
IAMFID          | NULL
IAMPID          | NULL
ObjectID        | 1541580530
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043236352
iam_chain_type  | In-row data
PageType        | 10
IndexLevel      | NULL
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0
-[ RECORD 2 ]-------------------------
PageFID         | 1
PagePID         | 384
IAMFID          | 1
IAMPID          | 308
ObjectID        | 1541580530
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043236352
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 384 .

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

DBCC TRACEON(3604, -1);
DBCC PAGE(Test, 1, 384, 3);

Pour l'instant, nous nous intéressons principalement à la partie suivante :

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

t0 = 10:15:30                       

Slot 0 Column 2 Offset 0x7 Length 3 Length (physical) 3

t1 = 10:15:30.1                     

Slot 0 Column 3 Offset 0xa Length 3 Length (physical) 3

t2 = 10:15:30.12                    

Slot 0 Column 4 Offset 0xd Length 4 Length (physical) 4

t3 = 10:15:30.123       

Slot 0 Column 5 Offset 0x11 Length 4 Length (physical) 4

t4 = 10:15:30.1235                  

Slot 0 Column 6 Offset 0x15 Length 5 Length (physical) 5

t5 = 10:15:30.12346                 

Slot 0 Column 7 Offset 0x1a Length 5 Length (physical) 5

t6 = 10:15:30.123457                

Slot 0 Column 8 Offset 0x1f Length 5 Length (physical) 5

t7 = 10:15:30.1234567                                                                      

Nous obtenons donc le même résultat que l'indique la documentation. Cela suggérerait que la précision n'est pas stockée avec les valeurs.

Nous pouvons le confirmer en examinant les données réelles.

Les valeurs de temps réelles sont stockées dans cette partie du fichier de page :

Memory Dump @0x0000000423ADA060

0000000000000000:   10002400 42900095 a205d459 384b8233 02f31603  ..$.B..•¢.ÔY8K‚3.ó..
0000000000000014:   167ae51e dc00c1f6 34990887 a311fc55 080000    .zå.Ü.Áö4..‡£.üU...

Nous pouvons extraire les valeurs de temps réelles en supprimant quelques éléments. Une fois supprimé, ce qui suit restera :

42900095 a205d459 384b8233 02f31603
167ae51e dc00c1f6 34990887 a311fc55

Ces chiffres hexadécimaux contiennent toutes nos données temporelles, mais pas la précision . Cependant, ils sont organisés en blocs de 4 octets, nous devons donc réorganiser les espaces pour obtenir les valeurs individuelles.

Voici le résultat final. J'ai placé chaque valeur de date/heure sur une nouvelle ligne pour une meilleure lisibilité.

429000
95a205
d45938
4b823302
f3160316
7ae51edc00
c1f6349908
87a311fc55

Ce sont les valeurs hexadécimales réelles (moins la précision ) que nous obtiendrions si nous convertissions l'heure valeur en varbinary . Comme ceci :

SELECT 
  CONVERT(VARBINARY(16), t0) AS 't0',
  CONVERT(VARBINARY(16), t1) AS 't1',
  CONVERT(VARBINARY(16), t2) AS 't2',
  CONVERT(VARBINARY(16), t3) AS 't3',
  CONVERT(VARBINARY(16), t4) AS 't4',
  CONVERT(VARBINARY(16), t5) AS 't5',
  CONVERT(VARBINARY(16), t6) AS 't6',
  CONVERT(VARBINARY(16), t7) AS 't7'
FROM TimeTest;

Résultat (en utilisant la sortie verticale) :

t0 | 0x00429000
t1 | 0x0195A205
t2 | 0x02D45938
t3 | 0x034B823302
t4 | 0x04F3160316
t5 | 0x057AE51EDC00
t6 | 0x06C1F6349908
t7 | 0x0787A311FC55

Cette requête produit le même résultat, sauf que chaque valeur a été précédée de la précision.

Voici un tableau qui compare les données réelles du fichier de page aux résultats de la CONVERT() opération.

Données de fichier de page CONVERT() Données
429000 00429000
95a205 0195A205
d45938 02D45938
4b823302 034B823302
f3160316 04F3160316
7ae51edc00 057AE51EDC00
c1f6349908 06C1F6349908
87a311fc55 0787A311FC55

Nous pouvons donc voir que le fichier de page ne stocke pas la précision, mais le résultat converti le fait.

J'ai mis en surbrillance la date et l'heure réelles en rouge. J'ai également supprimé le 0x préfixe des résultats convertis, de sorte que seules les données de date/heure réelles soient affichées (avec la précision).

Notez également que l'hexadécimal n'est pas sensible à la casse. Ainsi, le fait que l'un utilise des minuscules et l'autre des majuscules n'est pas un problème.

Conclusion

Lors de la conversion d'une heure valeur en varbinary , il a besoin d'un octet supplémentaire pour stocker la précision. Il a besoin de la précision pour interpréter la partie temporelle (car celle-ci est stockée sous la forme d'un intervalle de temps, dont la valeur exacte dépendra de la précision).

Lorsqu'elle est stockée dans une base de données, la précision est spécifiée une fois au niveau de la colonne. Cela semble logique, car il n'est pas nécessaire d'ajouter la précision à chaque ligne lorsque toutes les lignes ont de toute façon la même précision. Cela nécessiterait un octet supplémentaire pour chaque ligne, ce qui augmenterait inutilement les besoins en stockage.