Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Разбиране на размера на съхранение „datetime2“ в SQL Server

В тази статия споделям някои наблюдения, които имах по отношение на datetime2 размер на съхранение на типа данни в SQL Server. Може би ще изясня някои точки относно действителния размер на съхранение, използван от този тип данни, когато се съхранява в база данни.

По-специално гледам на следното:

  • Документацията на Microsoft
  • Данни, съхранявани в променлива
    • Дължина в байтове с помощта на DATALENGTH()
    • Дължина в байтове с помощта на DATALENGTH() след конвертиране в varbinary
  • Данни, съхранявани в база данни
    • Дължина в байтове с помощта на COL_LENGTH()
    • Дължина в байтове с помощта на DBCC PAGE()

Някои от тях изглежда си противоречат и ще видите две различни суми за съхранение за една и съща стойност, в зависимост от това къде търсите.

Adatetime2 стойността може да показва различен размер на съхранение, в зависимост от това дали се съхранява в база данни, като datetime2 променлива или преобразувана в варбинарна .

Но има правдоподобно обяснение за това – зависи от това къде е прецизността се съхранява.

По време на моето проучване по този въпрос открих задълбочената статия на Ronen Ariely за това как datetime2 се съхранява във файла с данни много информативно и ме подтикна да изпълня някои подобни тестове в моята собствена среда за разработка и да ги представя тук.

Документацията на Microsoft

Първо, нека да разгледаме какво пише в официалната документация.

Документацията на Microsoft за datetime2 типът данни посочва, че размерът му за съхранение е както следва:

6 байта за прецизност по-малка от 3.
7 байта за прецизност 3 или 4.
За всички останали прецизност са необходими 8 байта.

Но тя квалифицира горната таблица със следното изявление:

Първият байт на datetime2 value съхранява прецизността на стойността, което означава действителното съхранение, необходимо за datetime2 стойността е размерът на паметта, посочен в таблицата по-горе, плюс 1 допълнителен байт за съхраняване на точността. Това прави максималния размер на datetime2 стойност 9 байта – 1 байт съхранява прецизност плюс 8 байта за съхранение на данни с максимална точност.

Така че като се има предвид горната информация, очевидното заключение, което трябва да се направи, би било, че таблицата може/(трябва?) да бъде написана, както следва:

7 байта за прецизност по-малка от 3.
8 байта за точност 3 или 4.
Всичката друга точност изисква 9 байта.

По този начин няма да е необходимо да го квалифицират с допълнителна информация за прецизност.

Но не е толкова просто.

Данни, съхранявани в променлива

Първо, нека съхраним datetime2 стойност в променлива и проверете нейния размер за съхранение. След това ще преобразувам тази стойност в varbinary и го проверете отново.

Дължина в байтове с използване на DATALENGTH

Ето какво се случва, ако използваме DATALENGTH() функция за връщане на броя байтове, използвани за datetime2(7) стойност:

DECLARE @d datetime2(7);SET @d ='2025-05-21 10:15:30.1234567';ИЗБЕРЕТЕ @d КАТО 'Стойност', DATALENGTH(@d) КАТО 'Дължина в байтове'; 

Резултат

+----------------------------+---------------- ---+| Стойност | Дължина в байтове ||----------------------------+-------------- ----|| 2025-05-21 10:15:30.1234567 | 8 |+----------------------------+---------------- --+

Стойността в този пример има максимален мащаб от 7 (защото декларирам променливата като datetime2(7) ) и връща дължина от 8 байта.

Това изглежда противоречи на заявеното от Microsoft за необходимостта от допълнителен байт за съхраняване на прецизността. За да цитирам Microsoft, Това прави максималният размер на datetime2 стойност 9 байта – 1 байт съхранява прецизност плюс 8 байта за съхранение на данни с максимална точност. .

Въпреки че е вярно, че изглежда получаваме 8 байта за съхранение на данни , изглежда, че липсва 1 байт, използван за съхраняване на точността.

Ако обаче преобразуваме стойността в varbinary получаваме различна история.

Дължина в байтове след преобразуване във „варбиниран“

Ето какво се случва, ако преобразуваме нашата datetime2 стойност до варбинарна :

DECLARE @d datetime2(7);SET @d ='2025-05-21 10:15:30.1234567';ИЗБЕРЕТЕ CONVERT(VARBINARY(10), @d) КАТО 'Стойност', DATALENGTH(CONVERT(VARBINARY( 10), @d)) AS „Дължина в байтове“;

Резултат

+---------------------+-------------------+| Стойност | Дължина в байтове ||---------------------+------------------|| 0x0787A311FC553F480B | 9 |+---------------------+-------------------+

В този случай получаваме 9 байта.

Това е шестнадесетично представяне на datetime2 стойност. Действителната стойност на датата и часа (и нейната прецизност) е всичко след 0x . Всяка двойка шестнадесетични знаци е байт. Има 9 двойки и следователно 9 байта. Това се потвърждава, когато използваме DATALENGTH() за да върнете дължината в байтове.

В този пример можем да видим, че първият байт е 07 . Това представлява прецизността (използвах скала от 7 и това е, което се показва тук).

Ако променя мащаба, можем да видим, че първият байт се променя, за да съответства на мащаба:

DECLARE @d datetime2(3);SET @d ='2025-05-21 10:15:30.1234567';ИЗБЕРЕТЕ CONVERT(VARBINARY(10), @d) КАТО 'Стойност', DATALENGTH(CONVERT(VARBINARY( 10), @d)) AS „Дължина в байтове“;

Резултат

+--------------------+-------------------+| Стойност | Дължина в байтове ||--------------------+-------------------|| 0x034B8233023F480B | 8 |+-------------------+-------------------+

Също така можем да видим, че дължината е съответно намалена.

Така че в този случай нашите резултати съвпадат перфектно с документацията на Microsoft – добавен е допълнителен байт за прецизност.

Много разработчици предполагат, че по този начин SQL Server съхранява своята datetime2 стойности в базата данни. Това предположение обаче изглежда неправилно.

Данни, съхранявани в база данни

В този пример създавам база данни, която съдържа таблица с различни datetime2(n) колони. След това използвам COL_LENGTH() за да върне дължината на всяка колона в байтове. След това вмъквам стойности в него, преди да използвам DBCC PAGE за да проверите размера на хранилището, което всяка datetime2 стойността заема файла на страницата.

Създайте база данни:

СЪЗДАВАНЕ НА БАЗА ДАННИ Тест;

Създайте таблица:

ИЗПОЛЗВАЙТЕ тест;СЪЗДАЙТЕ ТАБЛИЦА Datetime2Test (d0 datetime2(0), d1 datetime2(1), d2 datetime2(2), d3 datetime2(3), d4 datetime2(4), d5 datetime2(5), d6 datetime2(6 ), d7 дата и време2(7) );

В този случай създавам осем колони – по една за всеки дефиниран от потребителя мащаб, който можем да използваме с datetime2(n) .

Сега можем да проверим размера на съхранение на всяка колона.

Дължина в байтове с помощта на COL_LENGTH()

Използвайте COL_LENGTH() за да проверите дължината (в байтове) на всяка колона:

ИЗБЕРЕТЕ COL_LENGTH ( 'Datetime2Test' , 'd0' ) КАТО 'd0', COL_LENGTH ( 'Datetime2Test' , 'd1') КАТО 'd1', COL_LENGTH ( 'Datetime2Test' , 'd2' ) AS 'd ( 'Datetime2Test' , 'd3' ) КАТО 'd3', COL_LENGTH ( 'Datetime2Test' , 'd4' ) КАТО 'd4', COL_LENGTH ( 'Datetime2Test' , 'd5' ) КАТО 'd5', COL_LENGTH, T LENGTH 'd6' ) AS 'd6', COL_LENGTH ( 'Datetime2Test' , 'd7' ) AS 'd7'; 

Резултат:

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

Така че още веднъж, изглежда, че не използваме допълнителния байт за съхраняване на точността.

Използвайте DBCC PAGE, за да проверите съхранените данни

Сега нека използваме DBCC PAGE за да намерите действителния размер за съхранение на данните, които съхраняваме в тази таблица.

Първо, нека вмъкнем някои данни:

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

Сега изберете данните (само за да ги проверите):

ИЗБЕРЕТЕ * ОТ Datetime2Test;

Резултат (с помощта на вертикален изход):

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 | 21.05.2025 10:15:30.1235d5 | 21.05.2025 10:15:30.12346d6 | 2025-05-21 10:15:30.123457d7 | 21.05.2025 10:15:30.1234567

Както се очаква, стойностите използват прецизността, която е била предварително посочена на ниво колона.

Сега, преди да използваме DBCC PAGE() , трябва да знаем кой PagePID да му предадем. Можем да използваме DBCC IND() за да го намерите.

Намерете PagePID:

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

Резултат (с помощта на вертикален изход):

-[ ЗАПИС 1 ]------------------------------PageFID | 1PagePID | 306IAMFID | NULLIAMPID | NULLObjectID | 1205579333 Индекс ИД | 0Номер на дял | 1PartitionID | 72057594043039744iam_chain_type | В ред dataPageType | 10 Ниво на индекс | NULLNextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0-[ ЗАПИС 2 ]------------------------------PageFID | 1PagePID | 360IAMFID | 1IAMPID | 306ObjectID | 1205579333 Индекс ИД | 0Номер на дял | 1PartitionID | 72057594043039744iam_chain_type | В ред dataPageType | 1Ниво на индекс | 0Следваща страницаFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0

Това връща два записа. Интересуваме се от PageType от 1 (2-ри запис). Искаме PagePID от този запис. В този случай PagePID е 360 .

Сега можем да вземем този PagePID и да го използваме в следното:

DBCC TRACEON(3604, -1);DBCC PAGE(Тест, 1, 360, 3);

Това произвежда много данни, но ние се интересуваме основно от следната част:

Слот 0 Колона 1 Отместване 0x4 Дължина 6 Дължина (физическа) 6d0 =2025-05-21 10:15:30 Слот 0 Колона 2 Отместване 0xa Дължина 6 Дължина (физическа) 6d1-1 =0:02 30.1 Слот 0 Колона 3 Отместване 0x10 Дължина 6 Дължина (физическа) 6d2 =2025-05-21 10:15:30.12 Слот 0 Колона 4 Отместване 0x16 Дължина 7 Дължина (физическа) 6d2 =2025-05-21 10:15:30.12 Слот 0 Колона 4 Отместване 0x16 Дължина 7 Дължина (физическа) =125 S. 0 Колона 5 Отместване 0x1d Дължина 7 Дължина (физическа) 7d4 =2025-05-21 10:15:30.1235 Слот 0 Колона 6 Отместване 0x24 Дължина 8 Дължина (физическа) 8d5-134 =0610 Slot 0:15:30.1235 7 Отместване 0x2c Дължина 8 Дължина (физическа) 8d6 =2025-05-21 10:15:30.123457 Слот 0 Колона 8 Отместване 0x34 Дължина 8 Дължина (физическа) 8d7 =2025:-125-> 

Така че изглежда, че не използва допълнителния байт за прецизност.

Но нека да разгледаме действителните данни, преди да стигнем до каквито и да е заключения.

Действителните данни се съхраняват в тази част от файла на страницата:

 изхвърляне на памет @0x000000041883a0600000000000000000:10003C00 4290003F 480B95A2 053F480B D459383F .. <. B ..? H. • ¢. H.zå.Ü0000000000000028:003f480b c1f63499 083f480b 87a311fc 553f480b .?H.Áö4..?H.‡£.üU?H.0000>03000>00000>00000> 

Както можете да видите, нищо от това не изглежда като резултатите, които бихме получили чрез преобразуване на datetime2 стойност до варбинарна . Но е доста близо.

Ето как изглежда, ако изтрия няколко неща:

4290003f 480b95a2 053f480b d459383f480b4b82 33023f48 0bf31603 163f480b 7ae51edc003f480b c1f63499 38f480b c1f63499 1f63499 

Останалите шестнадесетични цифри съдържат всички наши данни за дата и час, но не и точността . Трябва обаче да пренаредим интервалите, за да получим действителните стойности за всеки ред.

Ето крайния резултат. Поставих всяка стойност за дата/час на нов ред за по-добра четливост.

4290003f480b 95a2053f480b d459383f480b 4b8233023f480bf31603163f480b 7ae51edc003f480b c1f634990bc7 30bc1f634990bc830bc1f634990b83f30bc8f8f8233023f480bf31603163f480b 

Това са действителните шестнадесетични стойности (минус точността ), които ще получим, ако преобразуваме datetime2 стойност до варбинарна . За да сме сигурни, нека да продължим и да направим точно това:

ИЗБЕРЕТЕ CONVERT(VARBINARY(10), d0) КАТО 'd0', CONVERT(VARBINARY(10), d1) КАТО 'd1', CONVERT(VARBINARY(10), d2) КАТО 'd2', CONVERT(VARBINARY( 10), d3) КАТО 'd3', CONVERT(VARBINARY(10), d4) КАТО 'd4', CONVERT(VARBINARY(10), d5) КАТО 'd5', CONVERT(VARBINARY(10), d6) КАТО 'd6 ', CONVERT(VARBINARY(10), d7) КАТО 'd7'ОТ Datetime2Test;

Резултат (с помощта на вертикален изход):

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

Така че получаваме същия резултат – с изключение на това, че е добавен с точност.

Но за да стане нещата кристално ясни, ето таблица, която сравнява действителните данни на файла на страницата с резултатите от CONVERT() операция.

Данни за файл на страницата CONVERT() Данни
4290003f480b 004290003F480B
95a2053f480b 0195A2053F480B
d459383f480b 02D459383F480B
4b8233023f480b 034B8233023F480B
f31603163f480b 04F31603163F480B
7ae51edc003f480b 057AE51EDC003F480B
c1f63499083f480b 06C1F63499083F480B
87a311fc553f480b 0787A311FC553F480B

Така че можем ясно да видим, че файлът на страницата не съхранява точността, но конвертираният резултат го запазва.

Осветих действителните части за дата и час в червено. Премахнах и 0x префикс от преобразуваните резултати, така че да се показват само действителните данни за дата/час (заедно с точността).

Също така имайте предвид, че шестнадесетичният знак не е чувствителен към главни букви, така че фактът, че единият използва малки букви, а другият използва главни букви, не е проблем.

Заключение

При конвертиране на datetime2 стойност до варбинарна , се нуждае от допълнителен байт за съхраняване на точността. Той се нуждае от точността, за да интерпретира частта от време (тъй като това се съхранява като интервал от време, чиято точна стойност ще зависи от точността).

Когато се съхранява в база данни, точността се посочва веднъж на ниво колона. Това би изглеждало логично, тъй като няма нужда да съхранявате допълнителен байт с всеки ред, ако може да бъде указан на ниво колона. Така че, ако посочите кажете, datetime2(7) на ниво колона, тогава всеки отделен ред ще бъде datetime2(7) . Няма нужда да повтаряте това на всеки ред.

Ронен Ариели стигна до същото заключение в статията си, спомената по-горе.

Ако имате милион реда с datetime2(7) стойности, съхраняването на точността с всеки ред ще изисква 9 000 000 байта, в сравнение само с 8 000 001, ако точността се съхранява веднъж за цялата колона.

Това също така укрепва datetime2 Случаят е, когато го сравнявате с дата и час . Дори когато използвате същия брой десетични знаци като дата и час (т.е. 3), datetime2 типът данни използва по-малко място за съхранение (поне когато се съхранява в таблица с повече от един ред). И прави това с по-висока точност. Дата и час стойността използва 8 байта, докато datetime2(3) използва 7 байта (плюс 1 „прецизен“ байт, който е споделен във всички редове).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да копирате огромни данни от таблица в друга таблица в SQL Server

  2. LEFT JOIN срещу LEFT OUTER JOIN в SQL Server

  3. Таблицата за проверка на SQL Server е разделена

  4. Как да изпращам имейл от SQL Server?

  5. ПОКАЗВАНЕ НА ВСИЧКИ данни за дати между две дати; ако не съществува ред за определена дата, покажете нула във всички колони