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

Pochopení velikosti úložiště „čas“ na serveru SQL Server

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
  • Data uložená v databázi
    • Délka v bajtech pomocí COL_LENGTH()
    • Délka v bajtech pomocí DBCC PAGE()

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..•¢.ÔY8K‚3.ó..
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ě.


  1. Průvodce po MariaDB Columnstore pro správce MySQL

  2. Vícesloupcový index na 3 polích s heterogenními datovými typy

  3. Nainstalujte Oracle Database Client krok za krokem

  4. Automatické generování souboru odpovědí