Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

datetime2 vs smalldatetime no SQL Server:Qual é a diferença?


Este artigo explora as principais diferenças entre o datetime2 e pequena data e hora tipos de dados no SQL Server.

Ambos os tipos de dados são usados ​​para armazenar valores de data e hora, no entanto, existem algumas diferenças importantes entre os dois. Na maioria dos casos, é melhor usar datetime2 (A Microsoft também recomenda isso), no entanto, pode haver alguns cenários em que você precise usar smalldatetime .



Aqui está uma tabela que descreve as principais diferenças entre esses dois tipos.
Recurso smalldatetime datahora2
Compatível com SQL (ANSI e ISO 8601) Não Sim
Período 1900-01-01 a 2079-06-06 0001-01-01 a 9999-12-31
Intervalo de tempo 00:00:00 até 23:59:59 00:00:00 até 23:59:59.9999999
Comprimento do caractere máximo de 19 posições 19 posições no mínimo
27 no máximo
Tamanho do armazenamento 4 bytes, fixo 6 a 8 bytes, dependendo da precisão*

* Mais 1 byte para armazenar a precisão
Precisão Um minuto 100 nanossegundos
Precisão de segundo fracionário Não Sim
Precisão de segundo fracionário definida pelo usuário Não Sim
Deslocamento de fuso horário Nenhum Nenhum
Reconhecimento e preservação de deslocamento de fuso horário Não Não
Conhece o horário de verão Não Não

Vantagens de 'datetime2'


Conforme visto na tabela acima, o datetime2 type tem muitas vantagens sobre smalldatetime , Incluindo:
  • maior intervalo de datas
  • precisão de segundos fracionários
  • precisão opcional especificada pelo usuário
  • maior precisão
  • se alinha com os padrões SQL (ANSI e ISO 8601)

* Em alguns casos, um datetime2 value usa um byte extra para armazenar a precisão, no entanto, quando armazenada em um banco de dados, a precisão é incluída na definição da coluna, portanto, o valor armazenado real não requer o byte extra.

Devo usar 'datetime' ou 'smalldatetime'?


A Microsoft recomenda datetime2 para novos trabalhos (e pelos mesmos motivos listados acima).

Portanto, você deve usar datetime2 , a menos que você tenha um motivo específico para não fazê-lo (como trabalhar com um sistema legado).

Exemplo 1 – Comparação básica


Aqui está um exemplo rápido para demonstrar a diferença básica entre datetime2 e pequena data e 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 |
+-----------------------------+---------------------+

Aqui, defino uma pequena data e hora variável para o mesmo valor que datetime2 variável. Isso faz com que o valor seja convertido em smalldatetime e podemos então usar um SELECT para ver o valor de cada variável.

Nesse caso, o datetime2 variável usa uma escala de 7, o que significa que tem 7 casas decimais. A pequena data e hora valor, por outro lado, não tem nenhum casas decimais. Além disso, seus segundos são definidos como zero e seus minutos são arredondados para cima.

Isso é esperado, porque a documentação oficial da Microsoft afirma que smalldatetime A hora de 's é baseada em um dia de 24 horas, com segundos sempre zero (:00) e sem segundos fracionários .

Assim, podemos ver que o datetime2 type fornece um valor de data/hora muito mais preciso e preciso.

Claro, você pode não precisar de todos esses segundos fracionários. Uma das coisas boas de datetime2 é que você pode especificar quantos (se houver) segundos fracionários você deseja.

Exemplo 2 – Usando menos casas decimais


Neste exemplo, reduzo o datetime2 escala para 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 |
+---------------------+---------------------+

Nesse caso, o datetime2 valor não inclui mais uma parte fracionária. Ambos os tipos agora compartilham o mesmo comprimento de caractere (19 posições).

Mas ainda há diferenças.

O datetime2 value honra o valor dos segundos, embora neste caso seus segundos tenham sido arredondados para cima. Conforme mencionado, a pequena data e hora o componente de segundos do valor é sempre definido como zero e, neste caso, seus minutos foram arredondados.

O motivo do datetime2 segundos é arredondado é porque a parte fracionária é 5 ou superior. Se reduzirmos a parte fracionária, nenhum arredondamento é realizado:
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 |
+---------------------+---------------------+

No entanto, o smalldatetime os minutos do valor continuam sendo arredondados para cima.

Exemplo 3 – Definindo valores de literais de string


Nos exemplos anteriores, o smalldatime o valor foi atribuído definindo-o com o mesmo valor que datetime2 valor. Quando fazemos isso, o SQL Server realiza uma conversão implícita para que os dados “se ajustem” ao novo tipo de dados.

No entanto, se tentarmos atribuir esse mesmo literal de string ao smalldatetime variável, obtemos um erro:
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.

Isso porque smalldatetime aceita apenas literais de string com 3 ou menos segundos fracionários.

Você pode esperar que ele não aceite literais de string com qualquer segundos fracionários, visto que não inclui segundos fracionários, mas não é o caso. Aceita felizmente 3 segundos fracionários, mas não mais.

Então, para superar esse problema, precisamos reduzir a parte fracionária para apenas 3 (ou menos) casas decimais.
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 |
+---------------------+---------------------+

O datetime2 type não tem essa limitação, mesmo usando uma escala de 0.

Exemplo 4 – Tamanho de armazenamento


A pequena data e hora tipo de dados tem um tamanho de armazenamento fixo de 4 bytes. Este é um dos poucos benefícios smalldatetime tem mais de datetime2 .

O datetime2 pode ser de 6, 7 ou 8 bytes, dependendo de sua precisão. Portanto, um datetime2 value sempre usará pelo menos 2 bytes a mais de armazenamento do que um smalldatetime valor.

A Microsoft afirma que o datetime2 type também usa 1 byte extra para armazenar sua precisão, caso em que usaria pelo menos 3 bytes a mais que smalldatetime .

No entanto, isso provavelmente depende se estamos armazenando em uma tabela ou em uma variável e se estamos convertendo ou não em uma constante binária.

Aqui está o que acontece se usarmos o DATALENGTH() função para retornar o número de bytes usados ​​para cada um dos nossos 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               |
+-------------+-----------------+

Mas se os convertermos em varbinary , obtemos o seguinte:
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               |
+-------------+-----------------+

Então datetime2 usa um byte extra quando convertido em varbinary . Muitos desenvolvedores supõem que a conversão para varbinary é representativo de como o SQL Server realmente armazena valores de data e hora.

Isso é apenas parcialmente verdade. Embora seja verdade que o SQL Server armazena seus valores de data e hora em hexadecimal, esse valor hexadecimal na verdade não inclui a precisão. Isso ocorre porque a precisão está incluída na definição da coluna. Mas quando convertemos para varbinary como fizemos no exemplo anterior, a precisão é prefixada e isso adiciona um byte extra.

O exemplo a seguir demonstra isso. Mostra que quando os dados são armazenados em uma coluna de banco de dados, obtemos um comprimento de 6 bytes para datetime2 vs 4 bytes para smalldatetime .

Exemplo 5 – Tamanho de armazenamento para dados armazenados


Neste exemplo, crio um banco de dados e uso COL_LENGTH para retornar o comprimento de cada coluna, em bytes. Em seguida, insiro um datetime2 e pequena data e hora valor nele e use DBCC PAGE() para encontrar o comprimento dos dados reais no arquivo de paginação. Isso nos mostra o espaço de armazenamento que cada tipo de dados usa quando armazenado em um banco de dados.

Crie um banco de dados:
CREATE DATABASE CompareTypes;

Crie uma tabela:
USE CompareTypes;

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

Nesse caso, crio duas colunas - uma é datetime2(0) coluna e a outra é um smalldatetime coluna.

Verifique o comprimento da coluna


Verifique o comprimento (em bytes) de cada coluna:
SELECT 
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheDateTime2' ) AS 'datetime2',
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheSmallDateTime' ) AS 'smalldatetime';  

Resultado:
+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 6           | 4               |
+-------------+-----------------+

Assim, vemos que o datetime2(0) coluna tem um comprimento de 6 bytes, em comparação com smalldatetime comprimento de 4 bytes.

Inserir dados


Agora vamos ver o tamanho do armazenamento dos valores reais de data e hora quando eles são armazenados no SQL Server. Podemos usar DBCC PAGE() para inspecionar a página real no arquivo de dados.

Mas primeiro, precisamos inserir dados em nossas colunas.

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

Selecione os dados (só para conferir):
SELECT * FROM Datetime2vsSmalldatetime;

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

Usando DBCC PAGE()


Aqui é onde usamos DBCC PAGE() para inspecionar a página real no arquivo de dados.

Primeiro, usaremos DBCC IND() para encontrar o PagePID:
DBCC IND('CompareTypes', 'dbo.Datetime2vsSmalldatetime', 0);

Resultado (usando saída 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

Isso retorna dois registros. Estamos interessados ​​no PageType de 1 (o segundo registro). Queremos o PagePID desse registro. Nesse caso, o PagePID é 344 .

Agora podemos pegar esse PagePID e usá-lo da seguinte forma:
DBCC TRACEON(3604, -1);
DBCC PAGE(CompareTypes, 1, 344, 3);

Isso produz muitos dados, mas estamos interessados ​​principalmente na seguinte 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                                          

Isso mostra que smalldatetime tem um comprimento de 4 bytes e datetime2(0) tem 6 bytes quando armazenado em um banco de dados.

Portanto, neste caso, há apenas uma diferença de 2 bytes, mas datetime2(0) é mais preciso e segue os padrões ANSI e ISO 8601.