sql >> Database >  >> RDS >> Sqlserver

Inzicht in 'datetimeoffset' opslaggrootte in SQL Server

In dit artikel bekijk ik hoe de datetimeoffset gegevenstype wordt opgeslagen in SQL Server en hoe u verschillende gerapporteerde resultaten voor de opslaggrootte kunt krijgen, afhankelijk van wat u ermee doet.

Dit is vergelijkbaar met wat ik deed met de datetime2 gegevenstype.

Ik kijk in het bijzonder naar het volgende:

  • Microsoft-documentatie
  • Gegevens opgeslagen in een variabele
    • Lengte in bytes met DATALENGTH()
    • Lengte in bytes met DATALENGTH() na conversie naar varbinary
  • Gegevens opgeslagen in een database
    • Lengte in bytes met COL_LENGTH()
    • Lengte in bytes met DBCC PAGE()

Documentatie van Microsoft

De officiële documentatie van Microsoft over de datetimeoffset gegevenstype geeft aan dat de opslaggrootte tussen 8 en 10 bytes ligt, afhankelijk van de gebruikte precisie.

gelijk aan datetime2(n) , kunt u datetimeoffset(n) . gebruiken om de precisie op te geven, waarbij n is een schaal tussen 0 en 7.

Dit zijn de gegevens die Microsoft presenteert voor dit gegevenstype:

Opgegeven schaal Resultaat (precisie, schaal) Kolomlengte (bytes) Nauwkeurigheid van fracties van seconden
datetimeoffset (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

Voor de doeleinden van dit artikel ben ik vooral geïnteresseerd in de Kolomlengte (bytes) kolom. Dit vertelt ons hoeveel bytes er worden gebruikt om dit gegevenstype in een database op te slaan.

De belangrijkste reden dat ik dit artikel wilde schrijven (en de experimenten hieronder wilde uitvoeren), is dat de Microsoft-documentatie niet uitlegt dat een extra byte wordt gebruikt voor de precisie (zoals wel in de documentatie voor de datetime2 sterk> data type). In de documentatie voor datetime2 , staat er:

De eerste byte van een datetime2 value slaat de precisie van de waarde op, wat betekent dat de daadwerkelijke opslag vereist is voor een datetime2 waarde is de opslaggrootte aangegeven in de bovenstaande tabel plus 1 extra byte om de precisie op te slaan. Dit maakt de maximale grootte van een datetime2 waarde 9 bytes – 1 byte slaat precisie op plus 8 bytes voor gegevensopslag met maximale precisie.

Maar de documentatie voor datetimeoffset bevat deze tekst niet, en de tijd . evenmin documentatie.

Dit zorgde ervoor dat ik me afvroeg of er een verschil is tussen hoe deze gegevenstypen hun waarden opslaan. Logica vertelde me dat ze hetzelfde zouden moeten werken, omdat ze allemaal een door de gebruiker gedefinieerde precisie hebben, maar ik wilde erachter komen.

Het korte antwoord is ja, datetimeoffset lijkt hetzelfde te werken als datetime2 (met betrekking tot de extra byte), ook al is het niet als zodanig gedocumenteerd.

De rest van het artikel behandelt verschillende voorbeelden waarbij ik de opslaggrootte van datetimeoffset . retourneer waarden in verschillende contexten.

Gegevens opgeslagen in een variabele

Laten we een datetimeoffset opslaan waarde in een variabele en controleer de opslaggrootte. Vervolgens converteer ik die waarde naar varbinary en controleer het opnieuw.

Lengte in bytes met DATALENGTH

Dit is wat er gebeurt als we de DATALENGTH() . gebruiken functie om het aantal bytes terug te geven dat is gebruikt voor een datetimeoffset(7) waarde:

DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT @d AS 'Waarde', DATALENGTH(@d) AS 'Lengte in Bytes ';

Resultaat

+------------------------------------+--------- ----------+| Waarde | Lengte in Bytes ||------------------------------------+-------- -----------|| 2025-05-21 10:15:30.1234567 +07:00 | 10 |+------------------------------------+---------- ---------+

De waarde in dit voorbeeld heeft de maximale schaal van 7 (omdat ik de variabele declareer als datetimeoffset(7) ), en het retourneert een lengte van 10 bytes.

Geen verrassingen hier, dit is de exacte opslaggrootte die volgens de Microsoft-documentatie zou moeten zijn.

Als we de waarde echter converteren naar varbinary we krijgen een ander resultaat.

Lengte in bytes na conversie naar 'varbinary'

Sommige ontwikkelaars converteren graag datetimeoffset en datetime2 variabelen naar varbinary , omdat het meer representatief is voor hoe SQL Server het opslaat in de database. Hoewel dit gedeeltelijk waar is, zijn de resultaten niet precies hetzelfde als de opgeslagen waarde (zoals u later zult zien).

Dit is wat er gebeurt als we onze datetimeoffset converteren waarde naar varbinary :

DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) AS 'Waarde', DATALENGTH( CONVERT (VARBINARY (16), @d)) ALS 'Lengte in Bytes';

Resultaat

+--------------------------+------------------- +| Waarde | Lengte in Bytes ||--------------------------+------------------ -|| 0x0787CBB24F1B3F480BA401 | 11 |+--------------------------+-------------------+ 

In dit geval krijgen we 11 bytes.

Dit is een hexadecimale weergave van de datetimeoffset waarde. De werkelijke datum-tijd-offsetwaarde (en de precisie ervan) is alles na de 0x . Elk paar hexadecimale tekens is een byte. Er zijn 11 paren en dus 11 bytes. Dit wordt bevestigd wanneer we DATALENGTH() . gebruiken om de lengte in bytes terug te geven.

In dit voorbeeld kunnen we zien dat de eerste byte 07 . is . Dit vertegenwoordigt de precisie (ik gebruikte een schaal van 7 en dat is dus wat hier wordt weergegeven).

Als ik de schaal verander, kunnen we zien dat de eerste byte verandert om overeen te komen met de schaal:

DECLARE @d datetimeoffset(3);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) AS 'Waarde', DATALENGTH( CONVERT (VARBINARY (16), @d)) ALS 'Lengte in Bytes';

Resultaat

+------------------------+-------------------+| Waarde | Lengte in Bytes ||------------------------+-------------------| | 0x03CBFCB2003F480BA401 | 10 |+------------------------+-------------------+

We kunnen ook zien dat de lengte dienovereenkomstig wordt verminderd.

Gegevens opgeslagen in een database

In dit voorbeeld maak ik een database met verschillende datetimeoffset(n) kolommen, en gebruik dan COL_LENGTH() om de lengte van elke kolom in bytes te retourneren. Ik voeg dan waarden in de kolommen in, voordat ik DBCC PAGE . gebruik om de opslaggrootte te controleren die elke datetimeoffset waarde neemt het paginabestand in beslag.

Maak een database:

DATABASE-test MAKEN;

Maak een tabel:

GEBRUIK Test; MAAK TABEL 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) );

In dit geval maak ik acht kolommen - één voor elke door de gebruiker gedefinieerde schaal die we kunnen gebruiken met datetimeoffset(n) .

Nu kunnen we de opslaggrootte van elke kolom controleren.

Lengte in bytes met COL_LENGTH()

Gebruik COL_LENGTH() om de lengte (in bytes) van elke kolom te controleren:

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_LENGTHT ('DatetimeoffsetTest ('Datetimeoffset 'd6' ) AS 'd6', COL_LENGTH ( 'DatetimeoffsetTest', 'd7') AS 'd7'; 

Resultaat:

+------+------+------+------+------+------+---- --+------+| d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 ||------+------+------+------+------+------+----- -+------|| 8 | 8 | 8 | 9 | 9 | 10 | 10 | 10 |+------+------+------+------+------+------+----- ++------+

Dus nogmaals, we krijgen hetzelfde resultaat als in de documentatie staat dat we zullen krijgen. Dit is te verwachten, want de documentatie vermeldt expliciet "Kolomlengte (bytes)", wat precies is wat we hier meten.

Gebruik DBCC PAGE om de opgeslagen gegevens te controleren

Laten we nu DBCC PAGE gebruiken om de werkelijke opslaggrootte te vinden van de gegevens die we in deze tabel opslaan.

Laten we eerst wat gegevens invoegen:

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;

Selecteer nu de gegevens (om het te controleren):

SELECTEER * FROM DatetimeoffsetTest;

Resultaat (met verticale uitvoer):

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

Zoals verwacht gebruiken de waarden de precisie die eerder is gespecificeerd op kolomniveau.

Merk op dat mijn systeem volgnullen weergeeft. De jouwe kan dit wel of niet doen. Hoe dan ook, dit heeft geen invloed op de werkelijke precisie of nauwkeurigheid.

Nu, voordat we DBCC PAGE() gebruiken , moeten we weten welke PagePID eraan moet worden doorgegeven. We kunnen DBCC IND() . gebruiken om dat te vinden.

Vind de PagePID:

DBCC IND('Test', 'dbo.DatetimeoffsetTest', 0);

Resultaat (met verticale uitvoer):

-[ RECORD 1 ]-------------------------PageFID | 1PaginaPID | 307IAMFID | NULLIAMPID | NULLObjectID | 1525580473Index-ID | 0PartitieNummer | 1Partitie-ID | 72057594043170816iam_chain_type | In-rij dataPageType | 10IndexNiveau | NULLVolgendePaginaFID | 0VolgendePagePID | 0PrevPageFID | 0PrevPaginaPID | 0-[ RECORD 2 ]-------------------------PageFID | 1PaginaPID | 376IAMFID | 1IAMPID | 307Object-ID | 1525580473Index-ID | 0PartitieNummer | 1Partitie-ID | 72057594043170816iam_chain_type | In-rij dataPageType | 1IndexNiveau | 0VolgendePaginaFID | 0VolgendePagePID | 0PrevPageFID | 0PrevPaginaPID | 0

Dit levert twee records op. We zijn geïnteresseerd in het PageType van 1 (het 2e record). We willen de PagePID van dat record. In dit geval is de PagePID 376 .

Nu kunnen we die PagePID nemen en gebruiken in het volgende:

DBCC TRACEON(3604, -1);DBCC PAGE(Test, 1, 376, 3);

Op dit moment zijn we vooral geïnteresseerd in het volgende deel:

Slot 0 Kolom 1 Offset 0x4 Lengte 8 Lengte (fysiek) 8d0 =2025-05-21 10:15:30 +07:00 Slot 0 Kolom 2 Offset 0xc Lengte 8 Lengte (fysiek) 8d1 =2025-05-21 10:15:30.1 +07:00 Slot 0 Kolom 3 Offset 0x14 Lengte 8 Lengte (fysiek) 8d2 =2025-05-21 10:15:30.12 +07:00 Slot 0 Kolom 4 Offset 0x1c Lengte 9 Lengte (fysiek) 9d3 =2025-05-21 10:15:30.123 +07:00 Slot 0 Kolom 5 Offset 0x25 Lengte 9 Lengte (fysiek) 9d4 =2025-05-21 10:15:30.1235 +07:00Slot 0 Kolom 6 Offset 0x2e Lengte 10 Lengte (fysiek) 10d5 =2025-05-21 10:15:30.12346 +07:00 Slot 0 Kolom 7 Offset 0x38 Lengte 10 Lengte (fysiek) 10d6 =2025-05-21 10:15:30.123457 +07:00 Slot 0 Kolom 8 Offset 0x42 Lengte 10 Lengte (fysiek) 10d7 =2025-05-21 10:15:30.1234567 +07:00 

Dus we krijgen weer hetzelfde resultaat. Precies zoals de documentatie aangeeft.

Laten we, terwijl we hier toch zijn, eens kijken naar de gegevens:de werkelijke datum/tijd-waarden zoals ze zijn opgeslagen in SQL Server.

De werkelijke waarden worden opgeslagen in dit deel van het paginabestand:

Geheugendump @0x000000041951A06000000000000000000:10004c00 d22d003f 480ba401 35ca013f 480ba401 ..L.Ò-.?H.¤.5Ê.?H.¤.0000000000000014:14e6113f 480ba401 cbfcb200 3f480ba4 .æf3.˲d. H.¤.óßý0000000000000028:063f480b a4017abf ea45003f 480ba401 c17a2bbb .?H.¤.z¿êE.?H.¤.Áz+»000000000000003C:023f480b a40187cb b24f1b3f 480ba401 080000 .?H.¤.‡Ë² ..

Daar horen nog een paar extra bits bij. Laten we een paar dingen verwijderen, zodat alleen onze datum- en tijdwaarden overblijven:

d22d003f 480ba401 35ca013f 480ba40114e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd063f480b a4017abf ea45003f 480ba401 c17a2bbb023f480b a40187cb b24f1b3f 480ba401f 480ba401 

De overige hexadecimale cijfers bevatten al onze datum- en tijdgegevens, maar niet de precisie . Ze zijn echter gerangschikt in brokken van 4 bytes, dus we moeten de spaties herschikken om de individuele waarden te krijgen.

Hier is het eindresultaat. Ik heb elke datum/tijd-waarde op een nieuwe regel geplaatst voor een betere leesbaarheid.

d22d003f480ba401 35ca013f480ba40114e6113f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba401 c17a2bbb023f480ba40187cbb24f1b3f480ba401

Dat zijn de werkelijke hexadecimale waarden (minus de precisie ) die we zouden krijgen als we de datetimeoffset . zouden omzetten waarde naar varbinary . Zoals dit:

SELECTEER CONVERT(VARBINARY(16), d0) AS 'd0', CONVERT(VARBINARY(16), d1) AS 'd1', CONVERT(VARBINARY(16), d2) AS 'd2', CONVERT(VARBINARY( 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) ALS 'd7'FROM DatetimeoffsetTest;

Resultaat (met verticale uitvoer):

d0 | 0x00D22D003F480BA401d1 | 0x0135CA013F480BA401d2 | 0x0214E6113F480BA401d3 | 0x03CBFCB2003F480BA401d4 | 0x04F3DFFD063F480BA401d5 | 0x057ABFEA45003F480BA401d6 | 0x06C17A2BBB023F480BA401d7 | 0x0787CBB24F1B3F480BA401

We krijgen dus hetzelfde resultaat - behalve dat het met de precisie is toegevoegd.

Hier is een tabel die de daadwerkelijke paginabestandsgegevens vergelijkt met de resultaten van de CONVERT() bediening.

Paginabestandsgegevens CONVERTEREN() gegevens
d22d003f480ba401 00D22D003F480BA401
35ca013f480ba401 0135CA013F480BA401
14e6113f480ba401 0214E6113F480BA401
cbfcb2003f480ba401 03CBFCB2003F480BA401
f3dffd063f480ba401 04F3DFFD063F480BA401
7abfea45003f480ba401 057ABFEA45003F480BA401
c17a2bbb023f480ba401 06C17A2BBB023F480BA401
87cbb24f1b3f480ba401 0787CBB24F1B3F480BA401

We kunnen dus zien dat het paginabestand de precisie niet opslaat, maar het geconverteerde resultaat wel.

Ik heb de feitelijke datum- en tijddelen in rood gemarkeerd. Ik heb ook de 0x . verwijderd prefix van de geconverteerde resultaten, zodat alleen de werkelijke datum/tijd-gegevens worden weergegeven (samen met de precisie).

Houd er ook rekening mee dat hexadecimaal niet hoofdlettergevoelig is, dus het feit dat de ene kleine letters en de andere hoofdletters gebruikt, is geen probleem.

Conclusie

Bij het converteren van een datetimeoffset waarde naar varbinary , heeft het een extra byte nodig om de precisie op te slaan. Het heeft de precisie nodig om het tijdsgedeelte te interpreteren (omdat dit wordt opgeslagen als een tijdsinterval, waarvan de exacte waarde afhangt van de precisie).

Bij opslag in een database wordt de precisie eenmaal gespecificeerd op kolomniveau. Dit lijkt logisch, omdat het niet nodig is om de precisie aan elke rij toe te voegen als alle rijen toch dezelfde precisie gebruiken. Dat zou een extra byte voor elke rij vereisen, wat de opslagvereisten onnodig zou verhogen.


  1. Hoe de dbms_output-buffer te vergroten?

  2. SYSUTCDATETIME() versus GETUTCDATE() in SQL Server:wat is het verschil?

  3. Hoe u alle tabellen kunt ophalen met een primaire sleutelbeperking die is gemaakt in de SQL Server-database - SQL Server / TSQL-zelfstudie 57

  4. Hoe maak je een back-up en herstel je een PostgreSQL-database?