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
- Longueur en octets en utilisant
- Données stockées dans une base de données
- Longueur en octets en utilisant
COL_LENGTH()
- Longueur en octets en utilisant
DBCC PAGE()
- Longueur en octets en utilisant
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..¢.ÔY8K3.ó.. 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.