sql >> Base de Datos >  >> RDS >> Sqlserver

Comprender el tamaño de almacenamiento de 'tiempo' en SQL Server

En este artículo analizo el tamaño de almacenamiento del tiempo tipo de datos en SQL Server.

En particular, miro lo siguiente:

  • Documentación de Microsoft
  • Datos almacenados en una variable
    • Longitud en bytes usando DATALENGTH()
    • Longitud en bytes usando DATALENGTH() después de convertir a varbinary
  • Datos almacenados en una base de datos
    • Longitud en bytes usando COL_LENGTH()
    • Longitud en bytes usando DBCC PAGE()

Documentación de Microsoft

Documentación oficial de Microsoft sobre la hora tipo de datos indica que su tamaño de almacenamiento es de entre 3 y 5 bytes, dependiendo de la precisión que se utilice.

Este tipo de datos permite una precisión definida por el usuario. Puedes usar tiempo(n) para especificar la precisión, donde n es una escala entre 0 y 7.

Estos son los datos que presenta Microsoft para el tiempo tipo de datos:

Escala especificada Resultado (precisión, escala) Longitud de columna (bytes) Precisión de fracciones de segundo
tiempo (16,7) 5 7
hora(0) (8,0) 3 0-2
hora(1) (10,1) 3 0-2
hora(2) (11,2) 3 0-2
hora(3) (12,3) 4 3-4
hora(4) (13,4) 4 3-4
hora(5) (14,5) 5 5-7
hora(6) (15,6) 5 5-7
hora(7) (16,7) 5 5-7

A los efectos de este artículo, estoy principalmente interesado en la Longitud de columna (bytes) columna. Esto nos dice cuántos bytes se utilizan para almacenar este tipo de datos en una base de datos.

Desde la perspectiva de un usuario, el tiempo el tipo de datos funciona de la misma manera que la parte de la hora de datetime2 . Tiene una precisión de fracciones de segundo definida por el usuario y acepta una escala de 0 a 7.

El resto de este artículo recorre varios ejemplos en los que devuelvo el tamaño de almacenamiento de tiempo valores en diferentes contextos.

Datos almacenados en una variable

Primero, almacenaré una hora valor en una variable y comprobar su tamaño de almacenamiento. Luego convertiré ese valor a varbinary y compruébalo de nuevo.

Longitud en Bytes usando DATALENGTH

Esto es lo que sucede si usamos DATALENGTH() función para devolver el número de bytes utilizados durante un tiempo(7) valor:

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

Resultado

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

El valor en este ejemplo tiene la escala máxima de 7 (porque declaro la variable como time(7) ), y devuelve una longitud de 5 bytes.

Esto es de esperar, ya que coincide con el tamaño de almacenamiento descrito en la tabla de Microsoft.

Sin embargo, si convertimos el valor a varbinary obtenemos un resultado diferente.

Longitud en bytes después de convertir a 'varbinary'

A algunos desarrolladores les gusta convertir tiempo o fechahora2 variables a varbinary porque es más representativo de cómo SQL Server lo almacena en la base de datos. Si bien esto es parcialmente cierto, los resultados no son exactamente iguales al valor almacenado (más información a continuación).

Esto es lo que sucede si convertimos nuestro tiempo valor a 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';

Resultado

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

En este caso obtenemos 6 bytes. Nuestro valor ahora usa 1 byte más de lo que se indica en la documentación.

Eso es porque necesita un byte adicional para almacenar la precisión.

Esta es una representación hexadecimal del tiempo valor. El valor de tiempo real (y su precisión) es todo después de 0x . Cada par de caracteres hexadecimales es un byte. Hay 6 pares, y por lo tanto 6 bytes. Esto se confirma cuando usamos DATALENGTH() para devolver la longitud en bytes.

En este ejemplo podemos ver que el primer byte es 07 . Esto representa la precisión (utilicé una escala de 7 y eso es lo que se muestra aquí).

Si cambio la escala, podemos ver que el primer byte cambia para coincidir con la escala:

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

Resultado

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

También podemos ver que la longitud se reduce en consecuencia. Pero nuevamente, es un byte más de lo que la documentación dice que debería usar.

Aunque la documentación de Microsoft para tiempo no menciona esto explícitamente, la documentación para datetime2 establece lo siguiente:

El primer byte de un datetime2 value almacena la precisión del valor, lo que significa el almacenamiento real requerido para un datetime2 El valor es el tamaño de almacenamiento indicado en la tabla anterior más 1 byte adicional para almacenar la precisión. Esto hace que el tamaño máximo de un datetime2 valor 9 bytes:1 byte almacena precisión más 8 bytes para el almacenamiento de datos con la máxima precisión.

Y el datetime2 El tipo de datos funciona exactamente de la misma manera con respecto a los ejemplos anteriores. En otras palabras, solo informa el byte adicional cuando se convierte a varbinary .

Por lo tanto, el byte adicional mencionado en la documentación de Microsoft también parece aplicarse al tiempo. .

Sin embargo, el tamaño de almacenamiento real de su tiempo los valores dependerán de dónde se almacenen los datos.

Datos almacenados en una base de datos

Cuando una columna de base de datos tiene un tipo de hora , su precisión se especifica en el nivel de columna:no a nivel de datos. En otras palabras, se especifica una vez para toda la columna. Esto tiene sentido, porque cuando define una columna como time(7) , sabe que todas las filas serán time(7) . No hay necesidad de gastar valiosos bytes reafirmando ese hecho en cada fila.

Cuando examinas un tiempo valor como está almacenado en SQL Server, verá que es el mismo que el varbinary resultado, pero sin la precisión.

A continuación se muestran ejemplos que muestran cómo tiempo Los valores se almacenan en SQL Server.

En estos ejemplos, creo una base de datos con varios time(n) columnas, y luego use COL_LENGTH() para devolver la longitud de cada columna, en bytes. Luego inserto valores en esas columnas, antes de usar DBCC PAGE para comprobar el tamaño de almacenamiento que cada vez el valor ocupa el archivo de la página.

Crear una base de datos:

CREATE DATABASE Test;

Crear una tabla:

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

En este caso, creo ocho columnas, una para cada escala definida por el usuario que podemos usar con time(n) .

Ahora podemos verificar el tamaño de almacenamiento de cada columna.

Longitud en Bytes usando COL_LENGTH()

Utilice COL_LENGTH() para verificar la longitud (en bytes) de cada columna:

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

Resultado:

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

Entonces, una vez más, obtenemos el mismo resultado que la documentación indica que obtendremos. Esto es de esperar, porque la documentación establece explícitamente "Longitud de columna (bytes)", que es exactamente lo que estamos midiendo aquí.

Recuerda, esto es antes insertamos cualquier dato. Las propias columnas determinan la precisión (y, por lo tanto, el tamaño de almacenamiento) de cualquier dato que se inserte, y no al revés.

Utilice la PÁGINA DBCC para comprobar los datos almacenados

Ahora insertemos datos, luego usemos DBCC PAGE para encontrar el tamaño de almacenamiento real de los datos que almacenamos en cada columna.

Insertar datos:

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;

Ahora selecciona los datos (solo para comprobarlo):

SELECT * FROM TimeTest;

Resultado (usando salida vertical):

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

Como era de esperar, los valores usan la precisión que se especificó previamente en el nivel de columna.

Tenga en cuenta que mi sistema muestra ceros finales. El tuyo puede o no hacerlo. Independientemente, esto no afecta la precisión o exactitud real.

Ahora, antes de usar DBCC PAGE() , necesitamos saber qué PagePID pasarle. Podemos usar DBCC IND() para encontrar eso.

Encuentra el PID de la página:

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

Resultado (usando salida vertical):

-[ 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

Esto devuelve dos registros. Estamos interesados ​​en el PageType de 1 (el segundo registro). Queremos el PagePID de ese registro. En este caso, el PagePID es 384 .

Ahora podemos tomar ese PagePID y usarlo en lo siguiente:

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

Ahora mismo estamos interesados ​​principalmente en la siguiente parte:

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                                                                      

Entonces obtenemos el mismo resultado que los estados de documentación. Esto sugeriría que la precisión no se almacena con los valores.

Podemos confirmarlo examinando los datos reales.

Los valores de tiempo reales se almacenan en esta parte del archivo de página:

Memory Dump @0x0000000423ADA060

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

Podemos extraer los valores de tiempo reales eliminando algunas cosas. Una vez eliminado, permanecerá lo siguiente:

42900095 a205d459 384b8233 02f31603
167ae51e dc00c1f6 34990887 a311fc55

Estos dígitos hexadecimales contienen todos nuestros datos de tiempo, pero no la precisión . Sin embargo, están organizados en fragmentos de 4 bytes, por lo que debemos reorganizar los espacios para obtener los valores individuales.

Aquí está el resultado final. Coloqué cada valor de fecha/hora en una nueva línea para una mejor legibilidad.

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

Esos son los valores hexadecimales reales (menos la precisión ) que obtendríamos si convertimos el tiempo valor a varbinary . Así:

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;

Resultado (usando salida vertical):

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

Esa consulta produce el mismo resultado, excepto que cada valor se ha antepuesto con la precisión.

Aquí hay una tabla que compara los datos reales del archivo de página con los resultados de CONVERT() operación.

Datos del archivo de página CONVERTIR() datos
429000 00429000
95a205 0195A205
d45938 02D45938
4b823302 034B823302
f3160316 04F3160316
7ae51edc00 057AE51EDC00
c1f6349908 06C1F6349908
87a311fc55 0787A311FC55

Entonces podemos ver que el archivo de la página no almacena la precisión, pero el resultado convertido sí lo hace.

Resalté las partes de fecha y hora reales en rojo. También eliminé el 0x prefijo de los resultados convertidos, de modo que solo se muestren los datos de fecha/hora reales (junto con la precisión).

También tenga en cuenta que el hexadecimal no distingue entre mayúsculas y minúsculas, por lo que el hecho de que uno use minúsculas y el otro use mayúsculas no es un problema.

Conclusión

Al convertir una hora valor a varbinary , necesita un byte adicional para almacenar la precisión. Necesita la precisión para interpretar la porción de tiempo (porque esto se almacena como un intervalo de tiempo, cuyo valor exacto dependerá de la precisión).

Cuando se almacena en una base de datos, la precisión se especifica una vez en el nivel de columna. Esto parece lógico, ya que no es necesario agregar la precisión a cada fila cuando todas las filas tienen la misma precisión de todos modos. Eso requeriría un byte adicional para cada fila, lo que aumentaría innecesariamente los requisitos de almacenamiento.