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

Forståelse af 'datetimeoffset' Storage Size i SQL Server

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
  • 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()

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 bruger DBCC 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:00

Som 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 bruge DBCC 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 | 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 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:00 

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

De 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 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba480ba4040f405b48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 

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

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


  1. Materialiseret visning vs. tabeller:Hvad er fordelene?

  2. Hvordan får man adgang til array internt indeks med postgreSQL?

  3. Operand bør indeholde 1 kolonne - MySQL NOT IN

  4. Sådan fjerner du den rigtige polstring på dagsnavnet i Oracle