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.