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

datetime2 vs smalldatetime en SQL Server:¿Cuál es la diferencia?

Este artículo explora las principales diferencias entre datetime2 y pequeña fecha y hora tipos de datos en SQL Server.

Ambos tipos de datos se utilizan para almacenar valores de fecha y hora; sin embargo, existen algunas diferencias importantes entre los dos. En la mayoría de los casos, es mejor usar datetime2 (Microsoft también recomienda esto), sin embargo, puede haber algunos escenarios en los que necesite usar smalldatetime .

Aquí hay una tabla que describe las diferencias clave entre estos dos tipos.

Característica pequeña fecha y hora fechahora2
Cumple con SQL (ANSI e ISO 8601) No
Intervalo de fechas 1900-01-01 a 2079-06-06 0001-01-01 a 9999-12-31
Intervalo de tiempo 00:00:00 a 23:59:59 00:00:00 a 23:59:59.9999999
Longitud de caracteres 19 posiciones como máximo 19 posiciones mínimo
27 máximo
Tamaño de almacenamiento 4 bytes, fijo 6 a 8 bytes, dependiendo de la precisión*

* Más 1 byte para almacenar la precisión

Precisión Un minuto 100 nanosegundos
Precisión de fracciones de segundo No
Precisión fraccionaria de segundo definida por el usuario No
Compensación de zona horaria Ninguno Ninguno
Reconocimiento y preservación de la compensación de zona horaria No No
Consciente del horario de verano No No

Ventajas de 'datetime2'

Como se ve en la tabla anterior, el datetime2 type tiene muchas ventajas sobre smalldatetime , incluyendo:

  • intervalo de fechas más amplio
  • precisión de fracciones de segundo
  • precisión opcional especificada por el usuario
  • mayor precisión
  • se alinea con los estándares SQL (ANSI e ISO 8601)

* En algunos casos, un datetime2 El valor utiliza un byte adicional para almacenar la precisión; sin embargo, cuando se almacena en una base de datos, la precisión se incluye en la definición de la columna, por lo que el valor real almacenado no requiere el byte adicional.

¿Debería usar 'datetime' o 'smalldatetime'?

Microsoft recomienda datetime2 para un nuevo trabajo (y por las mismas razones enumeradas anteriormente).

Por lo tanto, debe usar datetime2 , a menos que tenga una razón específica para no hacerlo (como trabajar con un sistema heredado).

Ejemplo 1:comparación básica

Aquí hay un ejemplo rápido para demostrar la diferencia básica entre datetime2 y pequeña fecha y hora .

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

Resultado:

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

Aquí, establecí un smalldatetime variable al mismo valor que datetime2 variable. Esto hace que el valor se convierta a smalldatetime y luego podemos usar un SELECT instrucción para ver el valor de cada variable.

En este caso, el datetime2 variable utiliza una escala de 7, lo que significa que tiene 7 decimales. La pequeña fecha y hora valor por otro lado, no tiene ninguna lugares decimales. Además, sus segundos se ponen a cero y sus minutos se redondean.

Esto es de esperar, porque la documentación oficial de Microsoft establece que smalldatetime El tiempo de está basado en un día de 24 horas, con segundos siempre cero (:00) y sin fracciones de segundo .

Entonces podemos ver que el datetime2 type proporciona un valor de fecha/hora mucho más preciso y exacto.

Por supuesto, es posible que no necesite todas esas fracciones de segundo. Una de las cosas buenas de datetime2 es que puedes especificar cuántos (si los hay) fracciones de segundo quieres.

Ejemplo 2:usar menos lugares decimales

En este ejemplo reduzco el datetime2 escala 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';

Resultado:

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

En este caso, el datetime2 el valor ya no incluye una parte fraccionaria. Ambos tipos ahora comparten la misma longitud de caracteres (19 posiciones).

Pero todavía hay diferencias.

La fechahora2 value respeta el valor de los segundos, aunque en este caso sus segundos se han redondeado. Como se mencionó, el smalldatetime el componente de segundos del valor siempre se establece en cero y, en este caso, sus minutos se han redondeado.

La razón por la datetime2 el componente de segundos se redondea hacia arriba porque la parte fraccionaria es 5 o más. Si reducimos la parte fraccionaria, no se realiza el redondeo:

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

Resultado:

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

Sin embargo, el smalldatetime los minutos del valor continúan redondeándose hacia arriba.

Ejemplo 3:configuración de valores a partir de literales de cadena

En los ejemplos anteriores, el smalldateime el valor se asignó estableciéndolo en el mismo valor que datetime2 valor. Cuando hacemos eso, SQL Server realiza una conversión implícita para que los datos "encajen" en el nuevo tipo de datos.

Sin embargo, si intentamos asignar ese mismo literal de cadena a smalldatetime variable, obtenemos un error:

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

Resultado:

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

Eso es porque smalldatetime solo acepta cadenas literales que tienen 3 o menos fracciones de segundo.

Es de esperar que no acepte cadenas literales con cualquiera segundos fraccionarios, ya que no incluye segundos fraccionarios, pero ese no es el caso. Acepta felizmente 3 fracciones de segundo, pero no más.

Entonces, para superar este problema, necesitamos reducir la parte fraccionaria a solo 3 (o menos) lugares decimales.

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

Resultado:

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

La fechahora2 type no tiene esta limitación, incluso cuando se usa una escala de 0.

Ejemplo 4:tamaño de almacenamiento

La pequeña fecha y hora tipo de datos tiene un tamaño de almacenamiento fijo de 4 bytes. Este es uno de los pocos beneficios smalldatetime tiene más de datetime2 .

La fechahora2 puede tener 6, 7 u 8 bytes, según su precisión. Así que un datetime2 el valor siempre usará al menos 2 bytes más de almacenamiento que un smalldatetime valor.

Microsoft afirma que datetime2 type también usa 1 byte adicional para almacenar su precisión, en cuyo caso usaría al menos 3 bytes más que smalldatetime .

Sin embargo, esto probablemente depende de si lo almacenamos en una tabla o en una variable, y si lo convertimos o no en una constante binaria.

Esto es lo que sucede si usamos DATALENGTH() función para devolver el número de bytes utilizados para cada uno de nuestros valores:

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

Resultado

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

Pero si los convertimos a varbinary , obtenemos lo siguiente:

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

Resultado

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

Entonces datetime2 usa un byte adicional cuando se convierte a varbinary . Muchos desarrolladores asumen que convertir a varbinary es representativo de cómo SQL Server realmente almacena los valores de fecha y hora.

Sin embargo, esto es solo parcialmente cierto. Si bien es cierto que SQL Server almacena sus valores de fecha y hora en hexadecimal, ese valor hexadecimal en realidad no incluye la precisión. Esto se debe a que la precisión está incluida en la definición de la columna. Pero cuando convertimos a varbinary como hicimos en el ejemplo anterior, la precisión se antepone y esto agrega un byte adicional.

El siguiente ejemplo demuestra esto. Muestra que cuando los datos se almacenan en una columna de la base de datos, obtenemos una longitud de 6 bytes para datetime2 vs 4 bytes para smalldatetime .

Ejemplo 5:tamaño de almacenamiento para datos almacenados

En este ejemplo, creo una base de datos y uso COL_LENGTH para devolver la longitud de cada columna, en bytes. Luego inserto un datetime2 y pequeña fecha y hora valor en él y use DBCC PAGE() para encontrar la longitud de los datos reales en el archivo de página. Esto nos muestra el espacio de almacenamiento que utiliza cada tipo de datos cuando se almacena en una base de datos.

Crear una base de datos:

CREATE DATABASE CompareTypes;

Crear una tabla:

USE CompareTypes;

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

En este caso, creo dos columnas:una es datetime2(0) columna y la otra es un smalldatetime columna.

Compruebe la longitud de la columna

Compruebe la longitud (en bytes) de cada columna:

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

Resultado:

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

Entonces vemos que datetime2(0) la columna tiene una longitud de 6 bytes, en comparación con smalldatetime longitud de 4 bytes.

Insertar datos

Ahora veamos el tamaño de almacenamiento de los valores reales de fecha y hora cuando se almacenan en SQL Server. Podemos usar DBCC PAGE() para inspeccionar la página real en el archivo de datos.

Pero primero, necesitamos insertar datos en nuestras columnas.

Insertar datos:

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

Selecciona los datos (solo para comprobarlo):

SELECT * FROM Datetime2vsSmalldatetime;

Resultado:

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

Uso de la PÁGINA DBCC()

Aquí es donde usamos DBCC PAGE() para inspeccionar la página real en el archivo de datos.

Primero, usaremos DBCC IND() para encontrar el PagePID:

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

Resultado (usando salida vertical):

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

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 344 .

Ahora podemos tomar ese PagePID y usarlo en lo siguiente:

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

Esto produce una gran cantidad de datos, pero estamos interesados ​​principalmente en la siguiente parte:

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                                          

Esto muestra que smalldatetime tiene una longitud de 4 bytes y datetime2(0) tiene 6 bytes cuando se almacena en una base de datos.

Entonces, en este caso, solo hay una diferencia de 2 bytes, pero datetime2(0) es más preciso y cumple con los estándares ANSI e ISO 8601.