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
- Délka v bajtech pomocí
- Data uložená v databázi
- Délka v bajtech pomocí
COL_LENGTH()
- Délka v bajtech pomocí
DBCC PAGE()
- Délka v bajtech pomocí
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žijemeDATALENGTH()
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ímDBCC 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.1234567Podle 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žítDBCC 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 | 0Tí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:15Zdá 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?0800000000000000000000000000000000Jak 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 4aZbý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 c1f634959083f0847539083fToto 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 | 0x0787A311FC553F480BDostaneme 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).