sql >> Database >  >> RDS >> Sqlserver

datetime2 vs smalldatetime in SQL Server:wat is het verschil?

Dit artikel onderzoekt de belangrijkste verschillen tussen de datetime2 en smalldatetime gegevenstypen in SQL Server.

Beide gegevenstypen worden gebruikt voor het opslaan van datum- en tijdwaarden, maar er zijn enkele belangrijke verschillen tussen de twee. In de meeste gevallen kunt u beter datetime2 . gebruiken (Microsoft raadt dit ook aan), maar er kunnen enkele scenario's zijn waarin u smalldatetime moet gebruiken .

Hier is een tabel met de belangrijkste verschillen tussen deze twee typen.

Functie smalldatetime datetime2
SQL-compatibel (ANSI &ISO 8601) Nee Ja
Datumbereik 1900-01-01 tot 2079-06-06 0001-01-01 tot 9999-12-31
Tijdbereik 00:00:00 tot 23:59:59 00:00:00 tot 23:59:59,999999
Tekenlengte 19 posities maximum 19 posities minimaal
27 maximaal
Opslaggrootte 4 bytes, vast 6 tot 8 bytes, afhankelijk van de precisie*

* Plus 1 byte om de precisie op te slaan

Nauwkeurigheid Eén minuut 100 nanoseconden
Fractionele tweede precisie Nee Ja
Door gebruiker gedefinieerde precisie van fractionele seconden Nee Ja
Tijdzoneverschuiving Geen Geen
Bewustwording en behoud van tijdzoneverschuiving Nee Nee
Bewust zomertijd Nee Nee

Voordelen van ‘datetime2’

Zoals te zien is in de bovenstaande tabel, is de datetime2 type heeft veel voordelen ten opzichte van smalldatetime , waaronder:

  • grotere periode
  • precisie van fractionele seconden
  • optionele door de gebruiker gespecificeerde precisie
  • hogere nauwkeurigheid
  • komt overeen met de SQL-standaarden (ANSI &ISO 8601)

* In sommige gevallen een datetime2 waarde gebruikt een extra byte om de precisie op te slaan, maar wanneer opgeslagen in een database, wordt de precisie opgenomen in de kolomdefinitie, dus de daadwerkelijke opgeslagen waarde vereist de extra byte niet.

Moet ik 'datetime' of 'smalldatetime' gebruiken?

Microsoft raadt datetime2 aan voor nieuw werk (en om dezelfde redenen als hierboven genoemd).

Gebruik daarom datetime2 , tenzij u een specifieke reden heeft om dit niet te doen (zoals werken met een legacy-systeem).

Voorbeeld 1 – Basisvergelijking

Hier is een snel voorbeeld om het fundamentele verschil te demonstreren tussen datetime2 en smalldatetime .

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

Resultaat:

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

Hier stel ik een smalldatetime in variabele naar dezelfde waarde als de datetime2 variabel. Hierdoor wordt de waarde geconverteerd naar smalldatetime en we kunnen dan een SELECT . gebruiken statement om de waarde van elke variabele te zien.

In dit geval is de datetime2 variabele gebruikt een schaal van 7, wat betekent dat deze 7 decimalen heeft. De smalldatetime waarde daarentegen heeft geen decimalen. Bovendien worden de seconden op nul gezet en worden de minuten naar boven afgerond.

Dit is te verwachten, omdat in de officiële documentatie van Microsoft staat dat smalldatetime 's tijd is gebaseerd op een 24-uurs dag, met seconden altijd nul (:00) en zonder fractionele seconden .

We kunnen dus zien dat de datetime2 type biedt een veel preciezere en nauwkeurigere datum/tijd-waarde.

Natuurlijk heb je misschien niet al die fracties van seconden nodig. Een van de goede dingen van datetime2 is dat u kunt specificeren hoeveel (indien aanwezig) fractionele seconden u wilt.

Voorbeeld 2 – Minder decimalen gebruiken

In dit voorbeeld verklein ik de datetime2 schaal naar 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';

Resultaat:

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

In dit geval is de datetime2 waarde bevat niet langer een fractioneel deel. Beide typen hebben nu dezelfde tekenlengte (19 posities).

Maar er zijn nog steeds verschillen.

De datetime2 waarde eert de secondewaarde, hoewel in dit geval de seconden naar boven zijn afgerond. Zoals gezegd, de smalldatetime de secondecomponent van de waarde is altijd op nul gezet, en in dit geval zijn de minuten naar boven afgerond.

De reden waarom de datetime2 secondencomponent wordt naar boven afgerond omdat het breukdeel 5 of hoger is. Als we het breukdeel verkleinen, wordt er niet afgerond:

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

Resultaat:

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

Echter, de smalldatetime de minuten van de waarde worden nog steeds naar boven afgerond.

Voorbeeld 3 – Waarden instellen uit String Literals

In de vorige voorbeelden is de smalldatetime waarde is toegewezen door deze op dezelfde waarde in te stellen als de datetime2 waarde. Wanneer we dat doen, voert SQL Server een impliciete conversie uit zodat de gegevens "passen" in het nieuwe gegevenstype.

Als we echter proberen om dezelfde letterlijke tekenreeks toe te wijzen aan de smalldatetime variabele, krijgen we een foutmelding:

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

Resultaat:

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

Dat komt omdat smalldatetime accepteert alleen letterlijke tekenreeksen die 3 of minder fractionele seconden hebben.

Je zou verwachten dat het geen letterlijke tekenreeksen accepteert met elke fractionele seconden, aangezien het geen fractionele seconden omvat, maar dat is niet het geval. Het accepteert graag 3 fracties van seconden, maar niet meer.

Om dit probleem op te lossen, moeten we het breukdeel verkleinen tot slechts 3 (of minder) decimalen.

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

Resultaat:

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

De datetime2 type heeft deze beperking niet, zelfs niet bij gebruik van een schaal van 0.

Voorbeeld 4 – Opslaggrootte

De smalldatetime datatype heeft een vaste opslaggrootte van 4 bytes. Dit is een van de weinige voordelen smalldatetime heeft meer dan datetime2 .

De datetime2 kan 6, 7 of 8 bytes zijn, afhankelijk van de nauwkeurigheid. Dus een datetime2 waarde gebruikt altijd minimaal 2 bytes meer opslagruimte dan een smalldatetime waarde.

Microsoft stelt dat de datetime2 type gebruikt ook 1 extra byte om de precisie op te slaan, in welk geval het minstens 3 bytes meer zou gebruiken dan smalldatetime .

Dit hangt echter waarschijnlijk af van of we het in een tabel of in een variabele opslaan en of we het al dan niet converteren naar een binaire constante.

Dit is wat er gebeurt als we de DATALENGTH() . gebruiken functie om het aantal bytes terug te geven dat voor elk van onze waarden is gebruikt:

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

Resultaat

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

Maar als we ze converteren naar varbinary , krijgen we het volgende:

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

Resultaat

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

Dus datetime2 gebruikt een extra byte bij conversie naar varbinary . Veel ontwikkelaars gaan ervan uit dat het converteren naar varbinary is representatief voor hoe SQL Server de datum- en tijdwaarden daadwerkelijk opslaat.

Dit is echter maar gedeeltelijk waar. Hoewel het waar is dat SQL Server de datum- en tijdwaarden in hexadecimaal opslaat, bevat die hexadecimale waarde eigenlijk niet de precisie. Dit komt omdat de precisie is opgenomen in de kolomdefinitie. Maar wanneer we converteren naar varbinary zoals we deden in het vorige voorbeeld, wordt de precisie toegevoegd, en dit voegt een extra byte toe.

Het volgende voorbeeld laat dit zien. Het laat zien dat wanneer de gegevens worden opgeslagen in een databasekolom, we een lengte krijgen van 6 bytes voor datetime2 vs 4 bytes voor smalldatetime .

Voorbeeld 5 – Opslaggrootte voor opgeslagen gegevens

In dit voorbeeld maak ik een database en gebruik ik COL_LENGTH om de lengte van elke kolom in bytes te retourneren. Ik voeg dan een datetime2 . in en smalldatetime waarde erin en gebruik DBCC PAGE() om de lengte van de werkelijke gegevens in het paginabestand te vinden. Dit toont ons de opslagruimte die elk gegevenstype gebruikt wanneer het wordt opgeslagen in een database.

Maak een database:

CREATE DATABASE CompareTypes;

Maak een tabel:

USE CompareTypes;

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

In dit geval maak ik twee kolommen – één is een datetime2(0) kolom en de andere is een smalldatetime kolom.

Controleer de kolomlengte

Controleer de lengte (in bytes) van elke kolom:

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

Resultaat:

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

We zien dus dat de datetime2(0) kolom heeft een lengte van 6 bytes, vergeleken met smalldatetime 's lengte van 4 bytes.

Gegevens invoegen

Laten we nu eens kijken naar de opslaggrootte van de werkelijke datum- en tijdwaarden wanneer ze zijn opgeslagen in SQL Server. We kunnen DBCC PAGE() . gebruiken om de eigenlijke pagina in het gegevensbestand te inspecteren.

Maar eerst moeten we gegevens in onze kolommen invoegen.

Gegevens invoegen:

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

Selecteer de gegevens (om het te controleren):

SELECT * FROM Datetime2vsSmalldatetime;

Resultaat:

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

DBCC PAGE() gebruiken

Hier gebruiken we DBCC PAGE() om de eigenlijke pagina in het gegevensbestand te inspecteren.

Eerst gebruiken we DBCC IND() om de PagePID te vinden:

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

Resultaat (met verticale uitvoer):

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

Dit levert twee records op. We zijn geïnteresseerd in het PageType van 1 (het 2e record). We willen de PagePID van dat record. In dit geval is de PagePID 344 .

Nu kunnen we die PagePID nemen en gebruiken in het volgende:

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

Dit levert veel data op, maar we zijn vooral geïnteresseerd in het volgende deel:

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                                          

Dit laat zien dat smalldatetime heeft een lengte van 4 bytes en datetime2(0) heeft 6 bytes wanneer opgeslagen in een database.

Dus in dit geval is er slechts een verschil van 2 bytes, maar datetime2(0) is nauwkeuriger en voldoet aan de ANSI- en ISO 8601-normen.


  1. Top n-records ophalen voor elke groep gegroepeerde resultaten

  2. Hoe maak je een back-up van een versleutelde database met Percona Server voor MySQL 8.0

  3. Hoe de invoegprestaties in PostgreSQL te versnellen

  4. Oracle Fast Recovery Area