V tomto článku se podívám na velikost úložiště v čase datový typ na serveru SQL Server.
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í
Dokumentace společnosti Microsoft
Oficiální dokumentace společnosti Microsoft v čas datový typ označuje, že velikost jeho úložiště je mezi 3 a 5 bajty, v závislosti na použité přesnosti.
Tento datový typ umožňuje uživatelem definovanou přesnost. Můžete použít čas(n) k určení přesnosti, kde n je stupnice mezi 0 a 7.
Zde jsou data, která společnost Microsoft prezentuje za dobu datový typ:
Zadané měřítko | Výsledek (přesnost, měřítko) | Délka sloupce (bajty) | Přesnost na zlomky sekund |
---|---|---|---|
čas | (16,7) | 5 | 7 |
čas(0) | (8,0) | 3 | 0–2 |
čas(1) | (10,1) | 3 | 0–2 |
čas(2) | (11,2) | 3 | 0–2 |
čas(3) | (12,3) | 4 | 3–4 |
čas(4) | (13,4) | 4 | 3–4 |
čas(5) | (14,5) | 5 | 5–7 |
čas(6) | (15,6) | 5 | 5–7 |
čas(7) | (16,7) | 5 | 5–7 |
Pro účely tohoto článku mě zajímá hlavně délka sloupce (bajty) sloupec. To nám říká, kolik bajtů se používá k uložení tohoto datového typu v databázi.
Z pohledu uživatele čas datový typ funguje stejně jako časová část datetime2 . Má uživatelsky definovanou přesnost ve zlomcích sekund a přijímá stupnici od 0 do 7.
Zbytek tohoto článku obsahuje různé příklady, kdy vracím velikost úložiště čas hodnoty v různých kontextech.
Data uložená v proměnné
Nejprve uložím čas 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 vrátí počet bajtů použitých za čas(7) hodnota:
DECLARE @t time(7); SET @t = '10:15:30.1234567'; SELECT @t AS 'Value', DATALENGTH(@t) AS 'Length in Bytes';
Výsledek
+------------------+-------------------+ | Value | Length in Bytes | |------------------+-------------------| | 10:15:30.1234567 | 5 | +------------------+-------------------+
Hodnota v tomto příkladu má maximální měřítko 7 (protože proměnnou deklaruji jako time(7) ) a vrátí délku 5 bajtů.
To se dá očekávat, protože odpovídá velikosti úložiště uvedené v tabulce společnosti Microsoft.
Pokud však hodnotu převedeme na varbinary dostaneme jiný výsledek.
Délka v bajtech po převodu na ‚varbinary‘
Někteří vývojáři rádi převádějí čas nebo datetime2 proměnné na varbinary protože je to více reprezentativní pro to, jak to SQL Server ukládá do databáze. I když je to částečně pravda, výsledky nejsou úplně stejné jako uložená hodnota (více o tom níže).
Co se stane, když převedeme čas hodnotu na varbinary :
DECLARE @t time(7); SET @t = '10:15:30.1234567'; SELECT CONVERT(VARBINARY(16), @t) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';
Výsledek
+----------------+-------------------+ | Value | Length in Bytes | |----------------+-------------------| | 0x0787A311FC55 | 6 | +----------------+-------------------+
V tomto případě dostaneme 6 bajtů. Naše hodnota nyní používá o 1 bajt více, než je uvedeno v dokumentaci.
Je to proto, že k uložení přesnosti potřebuje další bajt.
Toto je hexadecimální vyjádření času hodnota. Skutečná časová hodnota (a její přesnost) je vše za 0x
. Každý pár hexadecimálních znaků je bajt. Existuje 6 párů, a tedy 6 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 @t time(3); SET @t = '10:15:30.1234567'; SELECT CONVERT(VARBINARY(16), @t) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';
Výsledek
+--------------+-------------------+ | Value | Length in Bytes | |--------------+-------------------| | 0x034B823302 | 5 | +--------------+-------------------+
Můžeme také vidět, že délka je odpovídajícím způsobem snížena. Ale opět je to o jeden bajt více, než by měl podle dokumentace používat.
Ačkoli dokumentace společnosti Microsoft pro čas Dokumentace pro datetime2 to výslovně nezmiňuje uvádí následující:
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í.
A datetime2 datový typ funguje přesně stejným způsobem s ohledem na výše uvedené příklady. Jinými slovy, pouze hlásí bajt navíc, když je převeden na varbinary .
Zdá se tedy, že bajt navíc uvedený v dokumentaci společnosti Microsoft platí také pro čas .
Skutečná velikost úložiště vašeho času hodnoty budou na místě, kde jsou data uložena.
Data uložená v databázi
Když má sloupec databáze typ čas , jeho přesnost je určena na úrovni sloupce – ne na datové úrovni. Jinými slovy, je zadán jednou pro celý sloupec. To dává smysl, protože když definujete sloupec jako time(7) , víte, že všechny řádky budou time(7) . Není třeba spotřebovávat vzácné bajty opakující tuto skutečnost na každém řádku.
Když zkoumáte čas hodnota, jak je uložena na serveru SQL, uvidíte, že je stejná jako varbinary výsledek, ale bez přesnost.
Níže jsou uvedeny příklady, které ukazují, jak čas hodnoty jsou uloženy na serveru SQL Server.
V těchto příkladech vytvářím databázi s různým časem(n) sloupce a poté použijte COL_LENGTH()
vrátit délku každého sloupce v bajtech. Do těchto sloupců pak vložím hodnoty před použitím DBCC PAGE
pro kontrolu velikosti úložiště, které pokaždé value zabírá soubor stránky.
Vytvořte databázi:
CREATE DATABASE Test;
Vytvořte tabulku:
USE Test; CREATE TABLE TimeTest ( t0 time(0), t1 time(1), t2 time(2), t3 time(3), t4 time(4), t5 time(5), t6 time(6), t7 time(7) );
V tomto případě vytvořím osm sloupců – jeden pro každé uživatelem definované měřítko, které můžeme použít s time(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 ( 'TimeTest' , 't0' ) AS 't0', COL_LENGTH ( 'TimeTest' , 't1' ) AS 't1', COL_LENGTH ( 'TimeTest' , 't2' ) AS 't2', COL_LENGTH ( 'TimeTest' , 't3' ) AS 't3', COL_LENGTH ( 'TimeTest' , 't4' ) AS 't4', COL_LENGTH ( 'TimeTest' , 't5' ) AS 't5', COL_LENGTH ( 'TimeTest' , 't6' ) AS 't6', COL_LENGTH ( 'TimeTest' , 't7' ) AS 't7';
Výsledek:
+------+------+------+------+------+------+------+------+ | t0 | t1 | t2 | t3 | t4 | t5 | t6 | t7 | |------+------+------+------+------+------+------+------| | 3 | 3 | 3 | 4 | 4 | 5 | 5 | 5 | +------+------+------+------+------+------+------+------+
Opět tedy dostaneme stejný výsledek, jaký dokumentace uvádí, že dostaneme. To se dalo očekávat, protože dokumentace výslovně uvádí „délku sloupce (bajty)“, což je přesně to, co zde měříme.
Pamatujte, že toto je před vložíme jakákoliv data. Samotné sloupce určují přesnost (a tedy i velikost úložiště) vkládaných dat – nikoli naopak.
Ke kontrole uložených dat použijte DBCC PAGE
Nyní vložíme data a poté použijeme DBCC PAGE
zjistit skutečnou velikost úložiště dat, která ukládáme v každém sloupci.
Vložit data:
DECLARE @t time(7) = '10:15:30.1234567'; INSERT INTO TimeTest ( t0, t1, t2, t3, t4, t5, t6, t7 ) SELECT @t, @t, @t, @t, @t, @t, @t, @t;
Nyní vyberte data (jen pro kontrolu):
SELECT * FROM TimeTest;
Výsledek (při použití vertikálního výstupu):
t0 | 10:15:30 t1 | 10:15:30.1000000 t2 | 10:15:30.1200000 t3 | 10:15:30.1230000 t4 | 10:15:30.1235000 t5 | 10:15:30.1234600 t6 | 10:15:30.1234570 t7 | 10:15:30.1234567
Podle očekávání používají hodnoty přesnost, která byla dříve zadána na úrovni sloupců.
Všimněte si, že můj systém zobrazuje koncové nuly. Vaše to může, ale nemusí. Bez ohledu na to to nemá vliv na skutečnou přesnost nebo přesnost.
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.TimeTest', 0);
Výsledek (při použití vertikálního výstupu):
-[ RECORD 1 ]------------------------- PageFID | 1 PagePID | 308 IAMFID | NULL IAMPID | NULL ObjectID | 1541580530 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043236352 iam_chain_type | In-row data PageType | 10 IndexLevel | NULL NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 0 -[ RECORD 2 ]------------------------- PageFID | 1 PagePID | 384 IAMFID | 1 IAMPID | 308 ObjectID | 1541580530 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043236352 iam_chain_type | In-row data PageType | 1 IndexLevel | 0 NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 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 384 .
Nyní můžeme vzít toto PagePID a použít jej v následujícím:
DBCC TRACEON(3604, -1); DBCC PAGE(Test, 1, 384, 3);
Právě teď nás zajímá hlavně následující část:
Slot 0 Column 1 Offset 0x4 Length 3 Length (physical) 3 t0 = 10:15:30 Slot 0 Column 2 Offset 0x7 Length 3 Length (physical) 3 t1 = 10:15:30.1 Slot 0 Column 3 Offset 0xa Length 3 Length (physical) 3 t2 = 10:15:30.12 Slot 0 Column 4 Offset 0xd Length 4 Length (physical) 4 t3 = 10:15:30.123 Slot 0 Column 5 Offset 0x11 Length 4 Length (physical) 4 t4 = 10:15:30.1235 Slot 0 Column 6 Offset 0x15 Length 5 Length (physical) 5 t5 = 10:15:30.12346 Slot 0 Column 7 Offset 0x1a Length 5 Length (physical) 5 t6 = 10:15:30.123457 Slot 0 Column 8 Offset 0x1f Length 5 Length (physical) 5 t7 = 10:15:30.1234567
Dostaneme tedy stejný výsledek, jaký uvádí dokumentace. To by naznačovalo, že přesnost není uložena s hodnotami.
Můžeme to potvrdit zkoumáním skutečných dat.
Skutečné časové hodnoty jsou uloženy v této části souboru stránky:
Memory Dump @0x0000000423ADA060 0000000000000000: 10002400 42900095 a205d459 384b8233 02f31603 ..$.B..¢.ÔY8K3.ó.. 0000000000000014: 167ae51e dc00c1f6 34990887 a311fc55 080000 .zå.Ü.Áö4..£.üU...
Skutečné časové hodnoty můžeme extrahovat odstraněním několika věcí. Po odstranění zůstane následující:
42900095 a205d459 384b8233 02f31603 167ae51e dc00c1f6 34990887 a311fc55
Tyto hexadecimální číslice obsahují všechny naše časové údaje, ale ne přesnost . Jsou však uspořádány do 4bajtových bloků, takže musíme změnit uspořádání mezer, abychom získali jednotlivé hodnoty.
Zde je konečný výsledek. Pro lepší čitelnost jsem každou hodnotu data/času umístil na nový řádek.
429000 95a205 d45938 4b823302 f3160316 7ae51edc00 c1f6349908 87a311fc55
Toto jsou skutečné hexadecimální hodnoty (minus přesnost ), které bychom získali, kdybychom převedli čas hodnotu na varbinary . Takhle:
SELECT CONVERT(VARBINARY(16), t0) AS 't0', CONVERT(VARBINARY(16), t1) AS 't1', CONVERT(VARBINARY(16), t2) AS 't2', CONVERT(VARBINARY(16), t3) AS 't3', CONVERT(VARBINARY(16), t4) AS 't4', CONVERT(VARBINARY(16), t5) AS 't5', CONVERT(VARBINARY(16), t6) AS 't6', CONVERT(VARBINARY(16), t7) AS 't7' FROM TimeTest;
Výsledek (při použití vertikálního výstupu):
t0 | 0x00429000 t1 | 0x0195A205 t2 | 0x02D45938 t3 | 0x034B823302 t4 | 0x04F3160316 t5 | 0x057AE51EDC00 t6 | 0x06C1F6349908 t7 | 0x0787A311FC55
Tento dotaz poskytuje stejný výsledek – kromě toho, že každá hodnota byla předřazena s přesností.
Zde je tabulka, která porovnává skutečná data souboru stránky s výsledky CONVERT()
operace.
Data souboru stránky | CONVERT() Data |
---|---|
429 000 | 00429000 |
95a205 | 0195A205 |
d45938 | 02D45938 |
4b823302 | 034B823302 |
f3160316 | 04F3160316 |
7ae51edc00 | 057AE51EDC00 |
c1f6349908 | 06C1F6349908 |
87a311fc55 | 0787A311FC55 |
Můžeme tedy vidět, ž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 času 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ů. Zdá se to logické, protože není třeba přidávat přesnost ke každému řádku, když všechny řádky mají stejnou přesnost. To by vyžadovalo bajt navíc pro každý řádek, což by zbytečně zvýšilo nároky na úložiště.