sql >> Databáze >  >> RDS >> Sqlserver

Vysvětlení velikosti úložiště „datetime2“ na serveru SQL

V tomto článku sdílím několik postřehů, které jsem měl ohledně datetime2 velikost úložiště datového typu na serveru SQL Server. Možná objasním některé body týkající se skutečné velikosti úložiště používaného tímto typem dat při ukládání do databáze.

Konkrétně se dívám na následující:

  • Dokumentace společnosti Microsoft
  • Data uložená v proměnné
    • Délka v bajtech pomocí DATALENGTH()
    • Délka v bajtech pomocí DATALENGTH() po převodu na varbinary
  • Data uložená v databázi
    • Délka v bajtech pomocí COL_LENGTH()
    • Délka v bajtech pomocí DBCC PAGE()

Zdá se, že některé z nich si odporují a uvidíte dvě různé velikosti úložiště pro stejnou hodnotu, podle toho, kam se podíváte.

A datetime2 hodnota může ukazovat různou velikost úložiště v závislosti na tom, zda je uložena v databázi, jako datetime2 proměnná nebo převedená na varbinary .

Ale existuje pro to přijatelné vysvětlení – záleží na tom, kde je přesnost se ukládá.

Během mého výzkumu na toto téma jsem našel podrobný článek Ronena Arielyho o tom, jak datetime2 je uložena v datovém souboru velmi informativní, a to mě přimělo spustit několik podobných testů ve vlastním vývojovém prostředí a prezentovat je zde.

Dokumentace společnosti Microsoft

Nejprve se podívejme, co říká oficiální dokumentace.

Dokumentace společnosti Microsoft k datetime2 datový typ uvádí, že velikost jeho úložiště je následující:

6 bajtů pro přesnost menší než 3.
7 bajtů pro přesnost 3 nebo 4.
Všechna ostatní přesnost vyžadují 8 bajtů.

Ale kvalifikuje výše uvedenou tabulku následujícím prohlášením:

První bajt datetime2 value ukládá přesnost hodnoty, což znamená skutečné uložení požadované pro datetime2 hodnota je velikost úložiště uvedená v tabulce výše plus 1 další bajt pro uložení přesnosti. Tím je maximální velikost datetime2 hodnota 9 bajtů – 1 bajt ukládá přesnost plus 8 bajtů pro ukládání dat s maximální přesností.

Vzhledem k výše uvedeným informacím je tedy zřejmým závěrem, že by tabulka mohla/(měla by?) být napsána následovně:

7 bajtů pro přesnost menší než 3.
8 bajtů pro přesnost 3 nebo 4.
Všechna ostatní přesnost vyžadují 9 bajtů.

Tímto způsobem by jej nemuseli kvalifikovat dalšími informacemi o přesnosti.

Ale není to tak jednoduché.

Data uložená v proměnné

Nejprve si uložíme datetime2 hodnotu v proměnné a zkontrolujte její velikost úložiště. Potom tuto hodnotu převedu na varbinary a znovu to zkontrolujte.

Délka v bajtech pomocí DATALENGTH

Zde je to, co se stane, když použijeme DATALENGTH() funkce, která vrátí počet bajtů použitých pro datetime2(7) hodnota:

DECLARE @d datetime2(7);SET @d ='2025-05-21 10:15:30.1234567';SELECT @d AS 'Hodnota', DATALENGTH(@d) AS 'Délka v bytech'; 

Výsledek

+-----------------------------+---------------- ---+| Hodnota | Délka v bajtech ||-----------------------------+---------------- ----|| 2025-05-21 10:15:30.1234567 | 8 |+-----------------------------+------------------ --+

Hodnota v tomto příkladu má maximální měřítko 7 (protože proměnnou deklaruji jako datetime2(7) ) a vrátí délku 8 bajtů.

Zdá se, že to odporuje tomu, co uvádí Microsoft o potřebě dalšího bajtu k uložení přesnosti. Abychom citovali společnost Microsoft, To činí maximální velikost datetime2 hodnota 9 bajtů – 1 bajt ukládá přesnost plus 8 bajtů pro ukládání dat s maximální přesností. .

I když je pravda, že se zdá, že získáváme 8 bajtů pro ukládání dat , zdá se, že nám chybí 1 bajt použitý k uložení přesnosti.

Pokud však hodnotu převedeme na varbinary dostaneme jiný příběh.

Délka v bajtech po převodu na ‚varbinary‘

Co se stane, když převedeme datetime2 hodnotu na varbinary :

DECLARE @d datetime2(7);SET @d ='2025-05-21 10:15:30.1234567';SELECT CONVERT(VARBINARY(10), @d) AS 'Value', DATALENGTH(CONVERT(VARBINARY( 10), @d)) JAKO 'Délka v bytech';

Výsledek

+----------------------+-------------------+| Hodnota | Délka v bajtech ||----------------------+-------------------|| 0x0787A311FC553F480B | 9 |+----------------------+-------------------+

V tomto případě dostaneme 9 bajtů.

Toto je hexadecimální reprezentace datetime2 hodnota. Skutečná hodnota data a času (a její přesnost) je vše za 0x . Každý pár hexadecimálních znaků je bajt. Existuje 9 párů, a tedy 9 bajtů. To se potvrdí, když použijeme DATALENGTH() vrátíte délku v bajtech.

V tomto příkladu vidíme, že první bajt je 07 . To představuje přesnost (použil jsem měřítko 7 a tak je to zde zobrazeno).

Pokud změním měřítko, uvidíme, že se první bajt změní tak, aby odpovídal měřítku:

DECLARE @d datetime2(3);SET @d ='2025-05-21 10:15:30.1234567';SELECT CONVERT(VARBINARY(10), @d) AS 'Value', DATALENGTH(CONVERT(VARBINARY( 10), @d)) JAKO 'Délka v bytech';

Výsledek

+--------------------+-------------------+| Hodnota | Délka v bajtech ||--------------------+-------------------|| 0x034B8233023F480B | 8 |+--------------------+-------------------+

Můžeme také vidět, že délka se odpovídajícím způsobem zkrátí.

V tomto případě tedy naše výsledky dokonale odpovídají dokumentaci společnosti Microsoft – kvůli přesnosti byl přidán bajt navíc.

Mnoho vývojářů předpokládá, že takto SQL Server ukládá svůj datetime2 hodnoty v databázi. Zdá se však, že tento předpoklad je nesprávný.

Data uložená v databázi

V tomto příkladu vytvořím databázi, která obsahuje tabulku s různými datetime2(n) sloupců. Poté použiji COL_LENGTH() vrátit délku každého sloupce v bajtech. Poté do něj vložím hodnoty před použitím DBCC PAGE zkontrolovat velikost úložiště, které každé datetime2 value zabírá soubor stránky.

Vytvořte databázi:

Test CREATE DATABASE;

Vytvořte tabulku:

USE Test;CREATE TABLE Datetime2Test ( d0 datetime2(0), d1 datetime2(1), d2 datetime2(2), d3 datetime2(3), d4 datetime2(4), d5 datetime2(5), d6 datetime2(6 ), d7 datetime2(7) );

V tomto případě vytvořím osm sloupců – jeden pro každé uživatelem definované měřítko, které můžeme použít s datetime2(n) .

Nyní můžeme zkontrolovat velikost úložiště každého sloupce.

Délka v bajtech pomocí COL_LENGTH()

Použijte COL_LENGTH() pro kontrolu délky (v bajtech) každého sloupce:

SELECT COL_LENGTH ( 'Datetime2Test' , 'd0' ) AS 'd0', COL_LENGTH ( 'Datetime2Test' , 'd1' ) AS 'd1', COL_LENGTH ( 'Datetime2Test' , 'd2' ) AS 'Datetime2Test' , COLd'G ( 'Datetime2Test' , 'd3' ) AS 'd3', COL_LENGTH ( 'Datetime2Test' , 'd4' ) AS 'd4', COL_LENGTH ( 'Datetime2Test' , 'd5' ) AS 'd5', COL_LENGTH2 'd6' ) AS 'd6', COL_LENGTH ( 'Datetime2Test' , 'd7' ) AS 'd7'; 

Výsledek:

+------+------+------+------+------+------+---- --+------+| d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 ||------+------+------+------+------+------+------ -+------|| 6 | 6 | 6 | 7 | 7 | 8 | 8 | 8 |+------+------+------+------+------+------+------ -+------+

Znovu se tedy nezdá, že bychom využili extra bajt k uložení přesnosti.

Ke kontrole uložených dat použijte DBCC PAGE

Nyní použijeme DBCC PAGE zjistit skutečnou velikost úložiště dat, která ukládáme do této tabulky.

Nejprve vložíme nějaká data:

DECLARE @d datetime2(7) ='2025-05-21 10:15:30.1234567';INSERT INTO Datetime2Test ( d0, d1, d2, d3, d4, d5, d6, d7 )VYBRAT @d, @d , @d, @d, @d, @d, @d, @d;

Nyní vyberte data (jen pro kontrolu):

SELECT * FROM Datetime2Test;

Výsledek (při použití vertikálního výstupu):

d0 | 2025-05-21 10:15:30d1 | 2025-05-21 10:15:30.1d2 | 2025-05-21 10:15:30.12d3 | 2025-05-21 10:15:30.123d4 | 2025-05-21 10:15:30.1235d5 | 2025-05-21 10:15:30.12346d6 | 2025-05-21 10:15:30.123457d7 | 2025-05-21 10:15:30.1234567

Podle očekávání používají hodnoty přesnost, která byla dříve zadána na úrovni sloupců.

Nyní, než použijeme DBCC PAGE() , potřebujeme vědět, které PagePID mu předat. Můžeme použít DBCC IND() najít to.

Najděte PagePID:

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

Výsledek (při použití vertikálního výstupu):

-[ ZÁZNAM 1 ]-------------------------PageFID | 1PagePID | 306IAMFID | NULLIAMPID | NULLObjectID | 1205579333ID indexu | 0Číslo oddílu | 1ID oddílu | 72057594043039744iam_chain_type | DataPageType v řádku | 10Úroveň indexu | NULLNextPageFID | 0DalšíPagePID | 0PrevPageFID | 0PrevPagePID | 0-[ ZÁZNAM 2 ]-------------------------PageFID | 1PagePID | 360IAMFID | 1IAMPID | 306ObjectID | 1205579333ID indexu | 0Číslo oddílu | 1ID oddílu | 72057594043039744iam_chain_type | DataPageType v řádku | 1Úroveň indexu | 0DalšíPageFID | 0DalšíPagePID | 0PrevPageFID | 0PrevPagePID | 0

Tím se vrátí dva záznamy. Zajímá nás PageType 1 (druhý záznam). Chceme PagePID z tohoto záznamu. V tomto případě je PagePID 360 .

Nyní můžeme vzít toto PagePID a použít jej v následujícím:

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

To produkuje spoustu dat, ale nás zajímá hlavně následující část:

Sloup 0 Posun sloupce 1 0x4 Délka 6 Délka (fyzická) 6d0 =2025-05-21 10:15:30 Slot 0 Sloupec 2 Posun 0xa Délka 6 Délka (fyzická) 6d1 =2025-05-21:10:10 30.1 Slot 0 Sloupec 3 Posun 0x10 Délka 6 Délka (fyzická) 6d2 =2025-05-21 10:15:30.12 Slot 0 Sloupec 4 Posun 0x16 Délka 7 Délka (fyzická) 7d3 =2025-05:15.025-05:15.025-05:15. 0 Sloupec 5 Posun 0x1d Délka 7 Délka (fyzická) 7d4 =2025-05-21 10:15:30,1235 Slot 0 Sloupec 6 Posun 0x24 Délka 8 Délka (fyzická) 8d5 =2025-05-21 21025-05-21 30.6lum:7 Offset 0x2c Délka 8 Délka (fyzická) 8d6 =2025-05-21 10:15:30.123457 Slot 0 Sloupec 8 Ofset 0x34 Délka 8 Délka (fyzická) 8d7 =2025-05-21 10:123:30>5:15:15 

Zdá se tedy, že tento bajt navíc nepoužívá pro přesnost.

Než však dojdeme k nějakým závěrům, podívejme se na skutečná data.

Skutečná data jsou uložena v této části souboru stránky:

 Výpis z paměti @0x000000041883A0600000000000000000:10003C00 4290003f 480B95A2 053F480B D459383F .. <. H.zå.Ü0000000000000028:003f480b c1f63499 083f480b 87a311fc 553f480b .?H.Áö4...?H.‡£.üU?0800000000000000000000000000000000 

Jak vidíte, nic z toho nevypadá jako výsledky, kterých bychom dosáhli převedením datetime2 hodnotu na varbinary . Ale je to docela blízko.

Takto to vypadá, když smažu pár věcí:

4290003f 480b95a2 053f480b d459383f480b4b82 33023f48 0bf31603 163f480b 7ae51edc003f480b c1f63499 c1f63499 3b 3f63499 1f63499 4a 

Zbývající hexadecimální číslice obsahují všechny naše údaje o datu a čase, ale ne přesnost . Musíme však změnit uspořádání mezer, abychom získali skutečné hodnoty pro každý řádek.

Zde je konečný výsledek. Pro lepší čitelnost jsem každou hodnotu data/času umístil na nový řádek.

4290003f480b 95a2053f480b d459383f480b 4b8233023f480bf31603163f480b 7ae51edc003f480b c1f634959083f
 Toto jsou skutečné hexadecimální hodnoty (minus přesnost ), které bychom získali, kdybychom převedli datetime2 hodnotu na varbinary . Abyste si byli jisti, pojďme rovnou na to a udělejte to:

SELECT CONVERT(VARBINARY(10), d0) AS 'd0', CONVERT(VARBINARY(10), d1) AS 'd1', CONVERT(VARBINARY(10), d2) AS 'd2', CONVERT(VARBINARY( 10), d3) AS 'd3', CONVERT(VARBINARY(10), d4) AS 'd4', CONVERT(VARBINARY(10), d5) AS 'd5', CONVERT(VARBINARY(10), d6) AS 'd6 ', CONVERT(VARBINARY(10); d7) AS 'd7'FROM Datetime2Test;

Výsledek (při použití vertikálního výstupu):

d0 | 0x004290003F480Bd1 | 0x0195A2053F480Bd2 | 0x02D459383F480Bd3 | 0x034B8233023F480Bd4 | 0x04F31603163F480Bd5 | 0x057AE51EDC003F480Bd6 | 0x06C1F63499083F480Bd7 | 0x0787A311FC553F480B

Dostaneme tedy stejný výsledek – kromě toho, že byl předřazen s přesností.

Aby však bylo vše křišťálově jasné, zde je tabulka, která porovnává skutečná data souboru stránky s výsledky CONVERT() operace.

Data souboru stránky CONVERT() Data
4290003f480b 004290003F480B
95a2053f480b 0195A2053F480B
d459383f480b 02D459383F480B
4b8233023f480b 034B8233023F480B
f31603163f480b 04F31603163F480B
7ae51edc003f480b 057AE51EDC003F480B
c1f63499083f480b 06C1F63499083F480B
87a311fc553f480b 0787A311FC553F480B

Takže jasně vidíme, že soubor stránky neukládá přesnost, ale převedený výsledek ano.

Skutečné části data a času jsem zvýraznil červeně. Také jsem odstranil 0x prefix z převedených výsledků, takže se zobrazí pouze aktuální datum/čas (spolu s přesností).

Všimněte si také, že v šestnáctkové soustavě se nerozlišují velká a malá písmena, takže skutečnost, že jedna používá malá a druhá velká, není problém.

Závěr

Při převodu datetime2 hodnotu na varbinary , potřebuje k uložení přesnosti bajt navíc. K interpretaci časové části potřebuje přesnost (protože je uložena jako časový interval, jehož přesná hodnota bude záviset na přesnosti).

Při ukládání do databáze se přesnost zadává jednou na úrovni sloupců. To by se zdálo logické, protože není nutné ukládat další bajt s každým řádkem, pokud jej lze zadat na úrovni sloupce. Pokud tedy zadáte, řekněte datetime2(7) na úrovni sloupců pak každý řádek bude datetime2(7) . Není třeba to opakovat na každém řádku.

Ronen Ariely dospěl ke stejnému závěru ve svém výše zmíněném článku.

Pokud máte milion řádků s datetime2(7) hodnoty, uložení přesnosti s každým řádkem by vyžadovalo 9 000 000 bajtů, ve srovnání s pouhými 8 000 001, pokud je přesnost uložena jednou pro celý sloupec.

To také posílí datetime2 'případ při porovnání s datetime . I při použití stejného počtu desetinných míst jako datetime (tj. 3), datetime2 datový typ využívá méně úložného prostoru (alespoň když je uložen v tabulce s více než jedním řádkem). A dělá to s vyšší přesností. Datum a čas hodnota používá 8 bajtů, zatímco datetime2(3) používá 7 bajtů (plus 1 „přesný“ bajt, který je sdílený ve všech řádcích).


  1. Protokolování auditu pro PostgreSQL

  2. SQL dotaz pro nalezení posledního dne v měsíci

  3. MariaDB JSON_CONTAINS() Vysvětleno

  4. Rady pomocí kontingenční tabulky v Oracle