I den här artikeln tittar jag på hur datetimeoffset datatyp lagras i SQL Server, och hur du kan få olika rapporterade lagringsstorleksresultat, beroende på vad du gör med den.
Det här liknar det jag gjorde med datetime2 datatyp.
Jag tittar särskilt på följande:
- Microsofts dokumentation
- Data lagrad i en variabel
- Längd i byte med
DATALENGTH()
- Längd i byte med
DATALENGTH()
efter konvertering till varbinary
- Längd i byte med
- Data lagrad i en databas
- Längd i byte med
COL_LENGTH()
- Längd i byte med
DBCC PAGE()
- Längd i byte med
Microsofts dokumentation
Microsofts officiella dokumentation om datetime offset datatyp indikerar att dess lagringsstorlek är mellan 8 och 10 byte, beroende på vilken precision som används.
Liknar datetime2(n) , kan du använda datetimeoffset(n) för att ange precisionen, där n är en skala mellan 0 och 7.
Här är data som Microsoft presenterar för denna datatyp:
Specificerad skala | Resultat (precision, skala) | Kolumnlängd (byte) | Bråksekundersprecision |
---|---|---|---|
datetime offset | (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 den här artikeln är jag främst intresserad av Kolumnlängden (byte) kolumn. Detta talar om för oss hur många byte som används för att lagra denna datatyp i en databas.
Det främsta skälet till att jag ville skriva den här artikeln (och köra experimenten nedan) är att Microsofts dokumentation inte förklarar att en extra byte används för precisionen (som det gör i dokumentationen för datetime2) stark> data typ). I sin dokumentation för datetime2 , står det:
Den första byten i en datetime2 värde lagrar värdets precision, vilket innebär den faktiska lagring som krävs för en datetime2 värde är lagringsstorleken som anges i tabellen ovan plus 1 extra byte för att lagra precisionen. Detta gör den maximala storleken för en datetime2 värde 9 byte – 1 byte lagrar precision plus 8 byte för datalagring med maximal precision.
Men dokumentationen för datetimeoffset innehåller inte den här texten, och inte heller tiden dokumentation.
Detta fick mig att undra om det finns en skillnad mellan hur dessa datatyper lagrar sina värden. Logiken sa till mig att de borde fungera likadant, eftersom de alla har en användardefinierad precision, men jag ville ta reda på det.
Det korta svaret är ja, datetimeoffset verkar fungera på samma sätt som datetime2 (när det gäller den extra byten), även om den inte är dokumenterad som sådan.
Resten av artikeln går igenom olika exempel där jag returnerar lagringsstorleken för datetimeoffset värderingar i olika sammanhang.
Data lagrad i en variabel
Låt oss lagra en datumtidsförskjutning värde i en variabel och kontrollera dess lagringsstorlek. Sedan konverterar jag det värdet till varbinary och kontrollera det igen.
Längd i byte med DATALENGTH
Här är vad som händer om vi använder DATALENGTH()
funktion för att returnera antalet byte som används för en datetimeoffset(7) värde:
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ärde | Längd i byte ||------------------------------------------------+-------- ----------|| 2025-05-21 10:15:30.1234567 +07:00 | 10 |+--------------------------------------------+---------------- ----------+
Värdet i det här exemplet har den maximala skalan på 7 (eftersom jag deklarerar variabeln som datetimeoffset(7) ), och den returnerar en längd på 10 byte.
Inga överraskningar här, det här är den exakta lagringsstorleken som Microsoft-dokumentationen anger att den borde vara.
Men om vi konverterar värdet till varbinary vi får ett annat resultat.
Längd i byte efter konvertering till 'varbinary'
Vissa utvecklare gillar att konvertera datetimeoffset och datetime2 variabler till variabler , eftersom det är mer representativt för hur SQL Server lagrar det i databasen. Även om detta delvis är sant, är resultaten inte exakt samma som det lagrade värdet (som du kommer att se senare).
Så här händer om vi konverterar vår datumtidsförskjutning värde till 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ängd i bytes';
Resultat
+---------------------------+------------------------ +| Värde | Längd i byte ||--------------------------------+------------------------ -|| 0x0787CBB24F1B3F480BA401 | 11 |+---------------------------+------------------------+
I det här fallet får vi 11 byte.
Detta är en hexadecimal representation av datetimeoffset värde. Det faktiska datumtidsförskjutningsvärdet (och dess precision) är allt efter 0x
. Varje par av hexadecken är en byte. Det finns 11 par och därför 11 byte. Detta bekräftas när vi använder DATALENGTH()
för att returnera längden i byte.
I det här exemplet kan vi se att den första byten är 07
. Detta representerar precisionen (jag använde en skala på 7 och så det är vad som visas här).
Om jag ändrar skalan kan vi se att den första byten ändras för att matcha skalan:
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ängd i bytes';
Resultat
+------------------------+------------------------+| Värde | Längd i byte ||------------------------+------------------------| | 0x03CBFCB2003F480BA401 | 10 |+------------------------+------------------------+Vi kan också se att längden minskar i motsvarande mån.
Data lagrad i en databas
I det här exemplet skapar jag en databas med olika datetimeoffset(n) kolumner och använd sedan
COL_LENGTH()
för att returnera varje kolumns längd, i byte. Jag infogar sedan värden i kolumnerna innan jag använderDBCC PAGE
för att kontrollera lagringsstorleken som varje datetimeoffset värde tar upp på sidfilen.Skapa en databas:
CREATE DATABASE Test;Skapa en tabell:
ANVÄND Test; SKAPA TABELL 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 det här fallet skapar jag åtta kolumner – en för varje användardefinierad skala som vi kan använda med datetimeoffset(n) .
Nu kan vi kontrollera lagringsstorleken för varje kolumn.
Längd i byte med COL_LENGTH()
Använd
COL_LENGTH()
för att kontrollera längden (i byte) för varje kolumn: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å återigen får vi samma resultat som dokumentationen säger att vi kommer att få. Detta är att vänta, eftersom dokumentationen uttryckligen anger "Kolumnlängd (bytes)", vilket är exakt vad vi mäter här.
Använd DBCC PAGE för att kontrollera lagrad data
Låt oss nu använda
DBCC PAGE
för att hitta den faktiska lagringsstorleken för data som vi lagrar i den här tabellen.Låt oss först infoga lite 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älj nu data (bara för att kontrollera det):
SELECT * FROM DatetimeoffsetTest;Resultat (med vertikal utdata):
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 förväntat använder värdena den precision som tidigare har angetts på kolumnnivå.
Observera att mitt system visar avslutande nollor. Din kanske gör det eller inte. Oavsett vilket påverkar detta inte den faktiska precisionen eller noggrannheten.
Nu, innan vi använder
DBCC PAGE()
måste vi veta vilket PagePID som ska skickas till det. Vi kan användaDBCC IND()
för att hitta det.Hitta PagePID:
DBCC IND('Test', 'dbo.DatetimeoffsetTest', 0);Resultat (med vertikal utdata):
-[ RECORD 1 ]--------------------------------PageFID | 1PagePID | 307IAMFID | NULLIAMPID | NULLObjectID | 1525580473Index-ID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | In-row dataPageType | 10Indexnivå | NULLNextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0-[ RECORD 2 ]--------------------------------PageFID | 1PagePID | 376IAMFID | 1IAMPID | 307Objekt-ID | 1525580473Index-ID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | In-row dataPageType | 1Indexnivå | 0NextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0Detta returnerar två rekord. Vi är intresserade av PageType of 1 (den andra posten). Vi vill ha PagePID från den posten. I det här fallet är PagePID 376 .
Nu kan vi ta det PagePID och använda det i följande:
DBCC TRACEON(3604, -1);DBCC PAGE(Test, 1, 376, 3);Just nu är vi främst intresserade av följande del:
Slot 0 Kolumn 1 Offset 0x4 Längd 8 Längd (fysisk) 8d0 =2025-05-21 10:15:30 +07:00 Slot 0 Kolumn 2 Offset 0xc Längd 8 Längd (fysisk) 8d1 =2015-05-2 10:15:30.1 +07:00 Slot 0 Kolumn 3 Offset 0x14 Längd 8 Längd (fysisk) 8d2 =2025-05-21 10:15:30.12 +07:00 Slot 0 Kolumn 4 Offset 0x1c Längd 9 Längd 9d =2025-05-21 10:15:30.123 +07:00 Slot 0 Kolumn 5 Offset 0x25 Längd 9 Längd (fysisk) 9d4 =2025-05-21 10:15:30.1235 +07:00Slot 2 Offset Kolumn 0 Längd (fysisk) 10d5 =2025-05-21 10:15:30.12346 +07:00 Fack 0 Kolumn 7 Offset 0x38 Längd 10 Längd (fysisk) 10d6 =2025-05-21 10:15:307 Fack 10:17:30. Kolumn 8 Offset 0x42 Längd 10 Längd (fysisk) 10d7 =2025-05-21 10:15:30.1234567 +07:00Så vi får samma resultat igen. Precis som dokumentationen anger.
Medan vi är här, låt oss undersöka data – de faktiska datum-/tidsvärdena som de lagras i SQL Server.
De faktiska värdena lagras i denna del av sidfilen:
Memory Dump @0x000000041951A0600000000000000000:10004C00 D22D003F 480BA401 35CA013F 480BA401 ..L.ò. H. text. ..Det inkluderar fortfarande några extra bitar. Låt oss ta bort några saker så att endast våra datum- och tidsvärden kvarstår:
d22d003f 480ba401 35ca013f 480ba40114e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd063f480b a4017abf ea45003f 480ba401 c17a2bbb023f480b a40187cb b24f1b3f 480ba401De återstående hexadecimala siffrorna innehåller alla våra datum- och tidsdata, men inte precisionen . Men de är ordnade i 4 byte-bitar, så vi måste ordna om utrymmena för att få de individuella värdena.
Här är slutresultatet. Jag har placerat varje datum/tidsvärde på en ny rad för bättre läsbarhet.
d22d003f480ba401 35ca013f480ba40114e6113f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480003f480ba40404048b4803405555500000000000000000000000000000000000000000000000000000000000000000000000000000011Det är de faktiska hexadecimala värdena (minus precisionen ) som vi skulle få om vi konverterade datetimeoffset värde till varbinary . Så här:
VÄLJ KONVERTERA(VARBINÄR(16), d0) SOM 'd0', KONVERTERA(VARBINÄR(16), d1) SOM 'd1', KONVERTERA(VARBINÄR(16), d2) SOM 'd2', KONVERTERA(VARBINÄR( 16), d3) AS 'd3', CONVERT(VARBINARY(16), d4) AS 'd4', CONVERT(VARBINARY(16), d5) AS 'd5', CONVERT(VARBINARY(16), d6) AS 'd6 ', CONVERT(VARBINARY(16), d7) AS 'd7'FROM DatetimeoffsetTest;Resultat (med vertikal utdata):
d0 | 0x00D22D003F480BA401d1 | 0x0135CA013F480BA401d2 | 0x0214E6113F480BA401d3 | 0x03CBFCB2003F480BA401d4 | 0x04F3DFFD063F480BA401d5 | 0x057ABFEA45003F480BA401d6 | 0x06C17A2BBB023F480BA401d7 | 0x0787CBB24F1B3F480BA401Så vi får samma resultat – förutom att det har lagts fram med precision.
Här är en tabell som jämför den faktiska sidfilens data med resultaten av
CONVERT()
operation.
Sidfilsdata | CONVERT() Data |
---|---|
d22d003f480ba401 | 00D22D003F480BA401 |
35ca013f480ba401 | 0135CA013F480BA401 |
14e6113f480ba401 | 0214E6113F480BA401 |
cbfcb2003f480ba401 | 03CBFCB2003F480BA401 |
f3dffd063f480ba401 | 04F3DFFD063F480BA401 |
7abfea45003f480ba401 | 057ABFEA45003F480BA401 |
c17a2bbb023f480ba401 | 06C17A2BBB023F480BA401 |
87cbb24f1b3f480ba401 | 0787CBB24F1B3F480BA401 |
Så vi kan se att sidfilen inte lagrar precisionen, men det konverterade resultatet gör det.
Jag markerade de faktiska datum- och tidsdelarna i rött. Jag tog också bort 0x
prefix från de konverterade resultaten, så att endast de faktiska datum-/tidsdata visas (tillsammans med precisionen).
Observera också att hexadecimal är skiftlägesokänslig, så det faktum att den ena använder gemener och den andra använder versaler är inget problem.
Slutsats
När du konverterar en datumtidsförskjutning värde till varbinary , behöver den en extra byte för att lagra precisionen. Den behöver precisionen för att tolka tidsdelen (eftersom denna lagras som ett tidsintervall, vars exakta värde beror på precisionen).
När den lagras i en databas anges precisionen en gång på kolumnnivå. Detta verkar logiskt, eftersom det inte finns något behov av att lägga till precisionen till varje rad när alla rader använder samma precision ändå. Det skulle kräva en extra byte för varje rad, vilket skulle öka lagringskraven i onödan.