sql >> Database teknologi >  >> RDS >> Sqlserver

datetime2 vs smalldatetime i SQL Server:Hvad er forskellen?

Denne artikel udforsker de vigtigste forskelle mellem datetime2 og smalldatetime datatyper i SQL Server.

Begge datatyper bruges til at gemme dato- og tidsværdier, men der er nogle vigtige forskelle mellem de to. I de fleste tilfælde er det bedre at bruge datetime2 (Microsoft anbefaler også dette), men der kan være nogle scenarier, hvor du skal bruge smalldatetime .

Her er en tabel, der skitserer de vigtigste forskelle mellem disse to typer.

Funktion smalldatetime datetime2
SQL-kompatibel (ANSI &ISO 8601) Nej Ja
Datointerval 1900-01-01 til 2079-06-06 0001-01-01 til 9999-12-31
Tidsinterval 00:00:00 til 23:59:59 00:00:00 til 23:59:59.9999999
Tegnlængde 19 positioner maksimum 19 stillinger minimum
27 maksimum
Lagerstørrelse 4 bytes, rettet 6 til 8 bytes, afhængig af præcisionen*

* Plus 1 byte til at gemme præcisionen

Nøjagtighed Et minut 100 nanosekunder
Brøksekundpræcision Nej Ja
Brugerdefineret brøksekundpræcision Nej Ja
Tidszoneforskydning Ingen Ingen
Opmærksomhed og bevarelse af tidszoneforskydning Nej Nej
Sommertid opmærksom på Nej Nej

Fordele ved 'datetime2'

Som det ses i ovenstående tabel, er datetime2 type har mange fordele i forhold til smalldatetime , herunder:

  • større datointerval
  • brøksekunders præcision
  • valgfri brugerspecificeret præcision
  • højere nøjagtighed
  • tilpasser SQL-standarderne (ANSI &ISO 8601)

* I nogle tilfælde en datetime2 værdi bruger en ekstra byte til at gemme præcisionen, men når den er gemt i en database, er præcisionen inkluderet i kolonnedefinitionen, så den faktiske lagrede værdi kræver ikke den ekstra byte.

Skal jeg bruge 'datetime' eller 'smalldatetime'?

Microsoft anbefaler datetime2 for nyt arbejde (og af de samme årsager anført ovenfor).

Derfor bør du bruge datetime2 , medmindre du har en specifik grund til ikke at gøre det (såsom at arbejde med et ældre system).

Eksempel 1 – Grundlæggende sammenligning

Her er et hurtigt eksempel til at demonstrere den grundlæggende forskel mellem datetime2 og 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';

Resultat:

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

Her indstiller jeg en smalldatetime variabel til samme værdi som datetime2 variabel. Dette får værdien til at blive konverteret til smalldatetime og vi kan derefter bruge en SELECT sætning for at se værdien af ​​hver variabel.

I dette tilfælde er datetime2 variabel bruger en skala fra 7, hvilket betyder, at den har 7 decimaler. smalldatetime værdi på den anden side, har ikke nogen decimaler. Desuden er dens sekunder sat til nul, og dens minutter rundes op.

Dette kan forventes, fordi Microsofts officielle dokumentation siger, at smalldatetime 's tid er baseret på en 24-timers dag, med sekunder altid nul (:00) og uden brøkdele af sekunder .

Så vi kan se, at datetime2 type giver en meget mere præcis og nøjagtig dato/tidsværdi.

Selvfølgelig har du måske ikke brug for alle de brøkdele sekunder. En af de gode ting ved datetime2 er, at du kan angive, hvor mange (hvis nogen) brøksekunder, du ønsker.

Eksempel 2 – Brug af færre decimaler

I dette eksempel reducerer jeg datetime2 skaler til 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';

Resultat:

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

I dette tilfælde er datetime2 værdien omfatter ikke længere en brøkdel. Begge typer deler nu den samme tegnlængde (19 positioner).

Men der er stadig forskelle.

datetime2 værdi respekterer sekundværdien, selvom dens sekunder i dette tilfælde er blevet rundet op. Som nævnt er smalldatetime værdiens sekunder-komponent er altid sat til nul, og i dette tilfælde er dens minutter rundet op.

Årsagen til datetime2 sekunders komponent rundes op, fordi brøkdelen er 5 eller højere. Hvis vi reducerer brøkdelen, udføres ingen afrunding:

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

Resultat:

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

Men smalldatetime værdiens minutter fortsætter med at blive rundet op.

Eksempel 3 – Indstilling af værdier fra strenge bogstaver

I de foregående eksempler er smalldatetime værdien blev tildelt ved at indstille den til samme værdi som datetime2 værdi. Når vi gør det, udfører SQL Server en implicit konvertering for at dataene "passer" til den nye datatype.

Men hvis vi forsøger at tildele den samme streng bogstaveligt til smalldatetime variabel, får vi en fejl:

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

Resultat:

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

Det er fordi smalldatetime accepterer kun strengliteraler, der har 3 eller mindre brøksekunder.

Du kan forvente, at den ikke vil acceptere strenge bogstaver med nogle brøksekunder, da det ikke inkluderer brøksekunder, men det er ikke tilfældet. Den accepterer med glæde 3 brøksekunder, men ikke mere.

Så for at løse dette problem skal vi reducere brøkdelen til kun 3 (eller færre) decimaler.

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

Resultat:

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

datetime2 type har ikke denne begrænsning, selv når du bruger en skala på 0.

Eksempel 4 – Lagerstørrelse

smalldatetime datatypen har en fast lagerstørrelse på 4 bytes. Dette er en af ​​de få fordele smalldatetime har over datetime2 .

datetime2 kan være enten 6, 7 eller 8 bytes, afhængigt af dens præcision. Så en datetime2 værdi vil altid bruge mindst 2 bytes mere lagerplads end en smalldatetime værdi.

Microsoft oplyser, at datetime2 type bruger også 1 ekstra byte for at gemme sin præcision, i hvilket tilfælde den ville bruge mindst 3 bytes mere end smalldatetime .

Dette afhænger dog sandsynligvis af, om vi gemmer det i en tabel eller i en variabel, og om vi konverterer det til en binær konstant eller ej.

Her er, hvad der sker, hvis vi bruger DATALENGTH() funktion til at returnere antallet af bytes brugt for hver af vores værdier:

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

Resultat

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

Men hvis vi konverterer dem til varbinary , får vi følgende:

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

Resultat

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

datetime2 bruger en ekstra byte, når den konverteres til varbinary . Mange udviklere antager, at konvertering til varbinary er repræsentativ for, hvordan SQL Server faktisk gemmer dato- og tidsværdier.

Dette er dog kun delvist sandt. Selvom det er rigtigt, at SQL Server gemmer sine dato- og tidsværdier i hexadecimal, inkluderer denne hex-værdi faktisk ikke præcisionen. Dette skyldes, at præcisionen indgår i kolonnedefinitionen. Men når vi konverterer til varbinary ligesom vi gjorde i det foregående eksempel, er præcisionen forudsat, og dette tilføjer en ekstra byte.

Det følgende eksempel viser dette. Det viser, at når dataene er gemt i en databasekolonne, får vi en længde på 6 bytes for datetime2 vs 4 bytes for smalldatetime .

Eksempel 5 – Lagerstørrelse for lagrede data

I dette eksempel opretter jeg en database og bruger COL_LENGTH for at returnere hver kolonnes længde i bytes. Jeg indsætter derefter en datetime2 og smalldatetime værdi ind i den og brug DBCC PAGE() for at finde længden af ​​de faktiske data i sidefilen. Dette viser os den lagerplads, hver datatype bruger, når den er gemt i en database.

Opret en database:

CREATE DATABASE CompareTypes;

Opret en tabel:

USE CompareTypes;

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

I dette tilfælde opretter jeg to kolonner – den ene er en datetime2(0) kolonne og den anden er en smalldatetime kolonne.

Tjek kolonnelængden

Tjek længden (i bytes) af hver kolonne:

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

Resultat:

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

Så vi ser, at datetime2(0) kolonne har en længde på 6 bytes sammenlignet med smalldatetime ’s længde på 4 bytes.

Indsæt data

Lad os nu se på lagerstørrelsen af ​​de faktiske dato- og tidsværdier, når de er gemt i SQL Server. Vi kan bruge DBCC PAGE() for at inspicere den faktiske side i datafilen.

Men først skal vi indsætte data i vores kolonner.

Indsæt data:

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

Vælg dataene (bare for at kontrollere dem):

SELECT * FROM Datetime2vsSmalldatetime;

Resultat:

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

Brug af DBCC PAGE()

Det er her, vi bruger DBCC PAGE() for at inspicere den faktiske side i datafilen.

Først bruger vi DBCC IND() for at finde PagePID:

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

Resultat (ved hjælp af lodret output):

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

Dette returnerer to poster. Vi er interesserede i PageType of 1 (den 2. post). Vi vil have PagePID fra den post. I dette tilfælde er PagePID 344 .

Nu kan vi tage det PagePID og bruge det i følgende:

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

Dette producerer en masse data, men vi er primært interesserede i følgende del:

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                                          

Dette viser, at smalldatetime har en længde på 4 bytes og datetime2(0) har 6 bytes, når den er gemt i en database.

Så i dette tilfælde er der kun en forskel på 2 byte, men datetime2(0) er mere nøjagtig og overholder ANSI- og ISO 8601-standarderne.


  1. Ydeevnemyter:Tabelvariabler er altid i hukommelsen

  2. Hvornår spoler SQL Server-sortering tilbage?

  3. Fejl ORA-00932 ved brug af en select med union og CLOB felter

  4. Jeg kan ikke starte SQL Server-browseren