sql >> Databasteknik >  >> RDS >> Sqlserver

Förstå 'datetimeoffset' lagringsstorlek i SQL Server

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
  • Data lagrad i en databas
    • Längd i byte med COL_LENGTH()
    • Längd i byte med DBCC PAGE()

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änder DBCC 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:00

Som 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ända DBCC 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 | 0

Detta 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:00 

Så 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 480ba401

De å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 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480003f480ba40404048b4803405555500000000000000000000000000000000000000000000000000000000000000000000000000000011 

Det ä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 | 0x0787CBB24F1B3F480BA401

Så 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.


  1. Visa alla frågor som kommer till en Oracle-databas

  2. 4 sätt att få databassamlingen i MariaDB

  3. Hur listar man poster med datum från de senaste 10 dagarna?

  4. Vilken är platsen för mysql-klienten .my.cnf i XAMPP för Windows?