I denne artikel ser jeg på, hvordan datetime offset datatypen gemmes i SQL Server, og hvordan du kan få forskellige rapporterede lagerstørrelsesresultater, afhængigt af hvad du laver med den.
Dette svarer til, hvad jeg gjorde med datetime2 datatype.
Jeg ser især på følgende:
- Microsofts dokumentation
- Data gemt i en variabel
- Længde i bytes ved hjælp af
DATALENGTH()
- Længde i bytes ved hjælp af
DATALENGTH()
efter konvertering til varbinary
- Længde i bytes ved hjælp af
- Data gemt i en database
- Længde i bytes ved hjælp af
COL_LENGTH()
- Længde i bytes ved hjælp af
DBCC PAGE()
- Længde i bytes ved hjælp af
Microsofts dokumentation
Microsofts officielle dokumentation om datotidsforskydningen datatype angiver, at dens lagerstørrelse er mellem 8 og 10 bytes, afhængigt af den anvendte præcision.
Svarende til datetime2(n) , kan du bruge datetimeoffset(n) for at angive præcisionen, hvor n er en skala mellem 0 og 7.
Her er de data, som Microsoft præsenterer for denne datatype:
Specificeret skala | Resultat (præcision, skala) | Kolonnelængde (bytes) | Brøksekunders præcision |
---|---|---|---|
datotidsforskydning | (34,7) | 10 | 7 |
datetimeoffset(0) | (26,0) | 8 | 0-2 |
datetimeoffset(1) | (28,1) | 8 | 0-2 |
datetimeoffset(2) | (29,2) | 8 | 0-2 |
datetimeoffset(3) | (30,3) | 9 | 3-4 |
datetimeoffset(4) | (31,4) | 9 | 3-4 |
datetimeoffset(5) | (32,5) | 10 | 5-7 |
datetimeoffset(6) | (33,6) | 10 | 5-7 |
datetimeoffset(7) | (34,7) | 10 | 5-7 |
I forbindelse med denne artikel er jeg primært interesseret i Kolonnelængden (bytes) kolonne. Dette fortæller os, hvor mange bytes der bruges til at gemme denne datatype i en database.
Hovedårsagen til, at jeg ønskede at skrive denne artikel (og køre eksperimenterne nedenfor), er, at Microsoft-dokumentationen ikke forklarer, at der bruges en ekstra byte til præcisionen (som det gør i dokumentationen for datetime2) stærk> datatype). I sin dokumentation for datetime2 , står der:
Den første byte af en datetime2 værdi gemmer nøjagtigheden af værdien, hvilket betyder den faktiske lagring, der kræves for en datetime2 værdi er lagerstørrelsen angivet i tabellen ovenfor plus 1 ekstra byte til at gemme præcisionen. Dette gør den maksimale størrelse af en datetime2 værdi 9 bytes – 1 byte gemmer præcision plus 8 bytes til datalagring med maksimal præcision.
Men dokumentationen for datetimeoffset inkluderer ikke denne tekst, og det gør tiden heller ikke dokumentation.
Dette fik mig til at spekulere på, om der er forskel på, hvordan disse datatyper gemmer deres værdier. Logikken fortalte mig, at de skulle fungere ens, da de alle har en brugerdefineret præcision, men det ville jeg gerne finde ud af.
Det korte svar er ja, datetime offset ser ud til at fungere på samme måde som datetime2 (med hensyn til den ekstra byte), selvom den ikke er dokumenteret som sådan.
Resten af artiklen gennemgår forskellige eksempler, hvor jeg returnerer lagerstørrelsen for datetimeoffset værdier i forskellige sammenhænge.
Data gemt i en variabel
Lad os gemme en dato-tidsforskydning værdi i en variabel og kontroller dens lagerstørrelse. Så konverterer jeg denne værdi til varbinary og tjek det igen.
Længde i bytes ved hjælp af DATALENGTH
Her er, hvad der sker, hvis vi bruger DATALENGTH()
funktion til at returnere antallet af bytes brugt til en datetimeoffset(7) værdi:
DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT @d AS 'Value', DATALENGTH(@d) AS 'Length in Bytes ';
Resultat
+--------------------------------------------+-------- ----------+| Værdi | Længde i bytes ||------------------------------------------------+-------- ----------|| 2025-05-21 10:15:30.1234567 +07:00 | 10 |+--------------------------------------------+---------------- ----------+
Værdien i dette eksempel har den maksimale skala på 7 (fordi jeg erklærer variablen som datetimeoffset(7) ), og det returnerer en længde på 10 bytes.
Ingen overraskelser her, dette er den nøjagtige lagerstørrelse, som Microsoft-dokumentationen angiver, den skal være.
Men hvis vi konverterer værdien til varbinary vi får et andet resultat.
Længde i bytes efter konvertering til 'varbinary'
Nogle udviklere kan lide at konvertere datetimeoffset og datetime2 variabler til variable , fordi det er mere repræsentativt for, hvordan SQL Server gemmer det i databasen. Selvom dette er delvist sandt, er resultaterne ikke nøjagtigt de samme som den lagrede værdi (som du vil se senere).
Her er, hvad der sker, hvis vi konverterer vores datotidsforskydning værdi til varbinary :
DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) AS 'Value', DATALENGTH( CONVERT(VARBINARY(16), @d)) AS 'Længde i bytes';
Resultat
+---------------------------+------------------------ +| Værdi | Længde i bytes ||--------------------------------+------------------------ -|| 0x0787CBB24F1B3F480BA401 | 11 |+---------------------------+------------------------+
I dette tilfælde får vi 11 bytes.
Dette er en hexadecimal repræsentation af datetime offset værdi. Den faktiske dato-tidsforskydningsværdi (og dens præcision) er alt efter 0x
. Hvert par hex-tegn er en byte. Der er 11 par og derfor 11 bytes. Dette bekræftes, når vi bruger DATALENGTH()
for at returnere længden i bytes.
I dette eksempel kan vi se, at den første byte er 07
. Dette repræsenterer præcisionen (jeg brugte en skala fra 7, og det er det, der vises her).
Hvis jeg ændrer skalaen, kan vi se, at den første byte ændres til at matche skalaen:
DECLARE @d datetimeoffset(3);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) AS 'Value', DATALENGTH( CONVERT(VARBINARY(16), @d)) AS 'Længde i bytes';
Resultat
+------------------------+------------------------+| Værdi | Længde i bytes ||------------------------+------------------------| | 0x03CBFCB2003F480BA401 | 10 |+------------------------+------------------------+Vi kan også se, at længden reduceres tilsvarende.
Data gemt i en database
I dette eksempel opretter jeg en database med forskellige datetimeoffset(n) kolonner, og brug derefter
COL_LENGTH()
for at returnere hver kolonnes længde i bytes. Jeg indsætter derefter værdier i kolonnerne, før jeg brugerDBCC PAGE
for at kontrollere lagerstørrelsen, som hver datetime offset værdi optager på sidefilen.Opret en database:
CREATE DATABASE Test;Opret en tabel:
BRUG Test; CREATE TABLE DatetimeoffsetTest (d0 datetimeoffset(0), d1 datetimeoffset(1), d2 datetimeoffset(2), d3 datetimeoffset(3), d4 datetimeoffset(4), d5 datetimeoffset(5), d6 datetimeoffset(6) ), d7 datetimeoffset(7) );I dette tilfælde opretter jeg otte kolonner – én for hver brugerdefineret skala, som vi kan bruge med datetimeoffset(n) .
Nu kan vi kontrollere lagerstørrelsen for hver kolonne.
Længde i bytes ved hjælp af COL_LENGTH()
Brug
COL_LENGTH()
for at kontrollere længden (i bytes) af hver kolonne:SELECT COL_LENGTH ( 'DatetimeoffsetTest' , 'd0' ) AS 'd0', COL_LENGTH ( 'DatetimeoffsetTest' , 'd1' ) AS 'd1', COL_LENGTH ( 'DatetimeoffsetTest' , 'd2' ) AS 'd2', COL_LENGTH ( 'DatetimeoffsetTest' , 'd3' ) AS 'd3', COL_LENGTH ( 'DatetimeoffsetTest' , 'd4' ) AS 'd4', COL_LENGTH ( 'DatetimeoffsetTest' , 'd5' ) AS 'd5', COL_LENGT ( 'estDatetime,offset 'd6' ) AS 'd6', COL_LENGTH ( 'DatetimeoffsetTest', 'd7') AS 'd7';Resultat:
+------+------+------+------+------+------+---- --+------+| d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 ||------+------+------+------+------+------+----- -+------|| 8 | 8 | 8 | 9 | 9 | 10 | 10 | 10 |+------+------+------+------+------+------+----- -+------+Så endnu en gang får vi det samme resultat, som dokumentationen siger, vi vil få. Dette kan forventes, fordi dokumentationen udtrykkeligt angiver "Kolonnelængde (bytes)", hvilket er præcis, hvad vi måler her.
Brug DBCC PAGE til at kontrollere de lagrede data
Lad os nu bruge
DBCC PAGE
for at finde den faktiske lagerstørrelse for de data, vi gemmer i denne tabel.Lad os først indsætte nogle data:
DECLARE @d datetimeoffset(7) ='2025-05-21 10:15:30.1234567 +07:00';INSERT INTO DatetimeoffsetTest ( d0, d1, d2, d3, d4, d5, d6, d7 )SELECT @ d, @d, @d, @d, @d, @d, @d, @d;Vælg nu dataene (bare for at kontrollere dem):
SELECT * FROM DatetimeoffsetTest;Resultat (ved hjælp af lodret output):
d0 | 2025-05-21 10:15:30.0000000 +07:00d1 | 2025-05-21 10:15:30.1000000 +07:00d2 | 2025-05-21 10:15:30.1200000 +07:00d3 | 2025-05-21 10:15:30.1230000 +07:00d4 | 2025-05-21 10:15:30.1235000 +07:00d5 | 2025-05-21 10:15:30.1234600 +07:00d6 | 2025-05-21 10:15:30.1234570 +07:00d7 | 2025-05-21 10:15:30.1234567 +07:00Som forventet bruger værdierne den præcision, der tidligere er angivet på kolonneniveau.
Bemærk, at mit system viser efterfølgende nuller. Dine gør det måske eller ikke. Uanset hvad påvirker dette ikke den faktiske præcision eller nøjagtighed.
Nu, før vi bruger
DBCC PAGE()
, skal vi vide, hvilket PagePID der skal sendes til det. Vi kan brugeDBCC IND()
at finde det.Find PagePID:
DBCC IND('Test', 'dbo.DatetimeoffsetTest', 0);Resultat (ved hjælp af lodret output):
-[ RECORD 1 ]-------------------------PageFID | 1PagePID | 307IAMFID | NULLIAMPID | NULLObjectID | 1525580473Indeks-ID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | In-row dataPageType | 10Indeksniveau | NULLNextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0-[ RECORD 2 ]--------------------------------PageFID | 1PagePID | 376IAMFID | 1IAMPID | 307Objekt-ID | 1525580473Indeks-ID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | In-row dataPageType | 1Indeksniveau | 0NextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0Dette 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 376 .
Nu kan vi tage det PagePID og bruge det i følgende:
DBCC TRACEON(3604, -1);DBCC PAGE(Test, 1, 376, 3);Lige nu er vi primært interesserede i følgende del:
Slot 0 Kolonne 1 Offset 0x4 Længde 8 Længde (fysisk) 8d0 =2025-05-21 10:15:30 +07:00 Slot 0 Kolonne 2 Offset 0xc Længde 8 Længde (fysisk) 8d1 =2015-05-2 10:15:30.1 +07:00 Slot 0 Kolonne 3 Offset 0x14 Længde 8 Længde (fysisk) 8d2 =2025-05-21 10:15:30.12 +07:00 Slot 0 Kolonne 4 Offset 0x1c Længde 9 Længde (fysisk) 9 =2025-05-21 10:15:30.123 +07:00 Slot 0 Kolonne 5 Offset 0x25 Længde 9 Længde (fysisk) 9d4 =2025-05-21 10:15:30.1235 +07:00Slot 0 Offset Søjle 0 Længde (fysisk) 10d5 =2025-05-21 10:15:30.12346 +07:00 Slot 0 Kolonne 7 Offset 0x38 Længde 10 Længde (fysisk) 10d6 =2025-05-21 10:17:30 + 10:17:30. Kolonne 8 Offset 0x42 Længde 10 Længde (fysisk) 10d7 =2025-05-21 10:15:30.1234567 +07:00Så vi får samme resultat igen. Præcis som det fremgår af dokumentationen.
Mens vi er her, lad os undersøge dataene – de faktiske dato/tidsværdier, som de er gemt i SQL Server.
De faktiske værdier gemmes i denne del af sidefilen:
hukommelsesdump @0x000000041951A0600000000000000000:10004C00 D22D003F 480BA401 35CA013F 480BA401 ..L.ò- H.¤.óßý0000000000000028:063F480B A4017ABF EA45003F 480BA401 C17A2BBB.? H.¤.z¿êe.? H.¤.áz+»0000000000003c:023f480B A40187CB B24f1b3f 4801 ..Det inkluderer stadig et par ekstra bits. Lad os slette et par ting, så kun vores dato- og tidsværdier forbliver:
d22d003f 480ba401 35ca013f 480ba40114e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd063f480b a4017abf ea45003f 480ba401 c17a2bbb023f480b a40187cb b24f1b3f 480ba401De resterende hex-cifre indeholder alle vores dato- og tidsdata, men ikke præcisionen . De er dog arrangeret i 4 byte bidder, så vi skal omarrangere mellemrummene for at få de individuelle værdier.
Her er slutresultatet. Jeg har placeret hver dato/tidsværdi på en ny linje for bedre læsbarhed.
d22d003f480ba401 35ca013f480ba40114e6113f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba480ba4040f405b48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001Det er de faktiske hexadecimale værdier (minus præcisionen ), som vi ville få, hvis vi konverterede datetime offset værdi til varbinary . Sådan:
VÆLG KONVERTER(VARBINÆR(16), d0) SOM 'd0', KONVERTER(VARBINÆR(16), d1) SOM 'd1', KONVERTER(VARBINÆR(16), d2) SOM 'd2', KONVERTER(VARBINÆR( 16), d3) SOM 'd3', KONVERTER(VARBINARY(16), d4) SOM 'd4', KONVERTER(VARBINARY(16), d5) SOM 'd5', KONVERTER(VARBINARY(16), d6) SOM 'd6 ', CONVERT(VARBINARY(16); d7) AS 'd7'FROM DatetimeoffsetTest;Resultat (ved hjælp af lodret output):
d0 | 0x00D22D003F480BA401d1 | 0x0135CA013F480BA401d2 | 0x0214E6113F480BA401d3 | 0x03CBFCB2003F480BA401d4 | 0x04F3DFFD063F480BA401d5 | 0x057ABFEA45003F480BA401d6 | 0x06C17A2BBB023F480BA401d7 | 0x0787CBB24F1B3F480BA401Så vi får det samme resultat – bortset fra at det er blevet præpenderet med præcisionen.
Her er en tabel, der sammenligner de faktiske sidefildata med resultaterne af
CONVERT()
operation.
Sidefildata | CONVERT() data |
---|---|
d22d003f480ba401 | 00D22D003F480BA401 |
35ca013f480ba401 | 0135CA013F480BA401 |
14e6113f480ba401 | 0214E6113F480BA401 |
cbfcb2003f480ba401 | 03CBFCB2003F480BA401 |
f3dffd063f480ba401 | 04F3DFFD063F480BA401 |
7abfea45003f480ba401 | 057ABFEA45003F480BA401 |
c17a2bbb023f480ba401 | 06C17A2BBB023F480BA401 |
87cbb24f1b3f480ba401 | 0787CBB24F1B3F480BA401 |
Så vi kan se, at sidefilen ikke gemmer præcisionen, men det gør det konverterede resultat.
Jeg fremhævede de faktiske dato- og tidsdele med rødt. Jeg fjernede også 0x
præfiks fra de konverterede resultater, så kun de faktiske dato/tidsdata vises (sammen med præcisionen).
Bemærk også, at hexadecimal ikke skelner mellem store og små bogstaver, så det er ikke et problem, at den ene bruger små bogstaver og den anden bruger store bogstaver.
Konklusion
Når du konverterer en datotidsforskydning værdi til varbinary , den har brug for en ekstra byte for at gemme præcisionen. Den har brug for præcisionen for at fortolke tidsdelen (fordi denne er gemt som et tidsinterval, hvis nøjagtige værdi vil afhænge af præcisionen).
Ved lagring i en database angives præcisionen én gang på kolonneniveau. Dette virker logisk, da der ikke er behov for at tilføje præcisionen til hver række, når alle rækker alligevel bruger den samme præcision. Det ville kræve en ekstra byte for hver række, hvilket ville øge lagerkravene unødigt.