sql >> Databasteknik >  >> RDS >> Sqlserver

Förstå "tid" lagringsstorlek i SQL Server

I den här artikeln tittar jag på lagringsstorleken för tiden datatyp i SQL Server.

Jag tittar särskilt på följande:

  • Microsofts dokumentation
  • Data lagrad i en variabel
    • Längd i byte med DATALENGTH()
    • Längd i byte med DATALENGTH() efter konvertering till varbinary
  • Data lagrad i en databas
    • Längd i byte med COL_LENGTH()
    • Längd i byte med DBCC PAGE()

Microsofts dokumentation

Microsofts officiella dokumentation på tiden datatyp indikerar att dess lagringsstorlek är mellan 3 och 5 byte, beroende på vilken precision som används.

Denna datatyp möjliggör användardefinierad precision. Du kan använda tid(n) för att ange precisionen, där n är en skala mellan 0 och 7.

Här är informationen som Microsoft presenterar för tiden datatyp:

Specificerad skala Resultat (precision, skala) Kolumnlängd (byte) Bråksekundersprecision
tid (16,7) 5 7
tid(0) (8,0) 3 0-2
tid(1) (10,1) 3 0-2
tid(2) (11,2) 3 0-2
tid(3) (12,3) 4 3-4
tid(4) (13,4) 4 3-4
tid(5) (14,5) 5 5-7
tid(6) (15,6) 5 5-7
tid(7) (16,7) 5 5-7

I den här artikeln är jag främst intresserad av Kolumnlängden (byte) kolumn. Detta talar om för oss hur många byte som används för att lagra denna datatyp i en databas.

Ur en användares perspektiv, tiden datatypen fungerar på samma sätt som tidsdelen av datetime2 . Den har en användardefinierad precision på bråkdelssekunder och den accepterar en skala från 0 till 7.

Resten av den här artikeln går igenom olika exempel där jag returnerar lagringsstorleken tid värderingar i olika sammanhang.

Data lagrad i en variabel

Först sparar jag en tid värde i en variabel och kontrollera dess lagringsstorlek. Sedan konverterar jag det värdet till varbinary och kontrollera det igen.

Längd i byte med DATALENGTH

Här är vad som händer om vi använder DATALENGTH() funktion för att returnera antalet byte som används för en tid(7) värde:

DECLARE @t time(7);
SET @t = '10:15:30.1234567';
SELECT 
  @t AS 'Value',
  DATALENGTH(@t) AS 'Length in Bytes';

Resultat

+------------------+-------------------+
| Value            | Length in Bytes   |
|------------------+-------------------|
| 10:15:30.1234567 | 5                 |
+------------------+-------------------+

Värdet i det här exemplet har den maximala skalan på 7 (eftersom jag deklarerar variabeln som tid(7) ), och den returnerar en längd på 5 byte.

Detta är att vänta, eftersom det matchar lagringsstorleken som beskrivs i Microsofts tabell.

Men om vi konverterar värdet till varbinary vi får ett annat resultat.

Längd i byte efter konvertering till 'varbinary'

Vissa utvecklare gillar att konvertera tid eller datetime2 variabler till variabler eftersom det är mer representativt för hur SQL Server lagrar det i databasen. Även om detta delvis är sant, är resultaten inte exakt samma som det lagrade värdet (mer om det nedan).

Här är vad som händer om vi konverterar vår tid värde till 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';

Resultat

+----------------+-------------------+
| Value          | Length in Bytes   |
|----------------+-------------------|
| 0x0787A311FC55 | 6                 |
+----------------+-------------------+

I det här fallet får vi 6 byte. Vårt värde använder nu 1 byte mer än vad som anges i dokumentationen.

Det beror på att det behöver en extra byte för att lagra precisionen.

Detta är en hexadecimal representation av tiden värde. Det faktiska tidsvärdet (och dess precision) är allt efter 0x . Varje par av hexadecken är en byte. Det finns 6 par och därför 6 byte. Detta bekräftas när vi använder DATALENGTH() för att returnera längden i byte.

I det här exemplet kan vi se att den första byten är 07 . Detta representerar precisionen (jag använde en skala på 7 och så det är vad som visas här).

Om jag ändrar skalan kan vi se att den första byten ändras för att matcha skalan:

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';

Resultat

+--------------+-------------------+
| Value        | Length in Bytes   |
|--------------+-------------------|
| 0x034B823302 | 5                 |
+--------------+-------------------+

Vi kan också se att längden minskar i motsvarande mån. Men återigen, det är en byte mer än vad dokumentationen säger att den ska använda.

Även om Microsofts dokumentation för tid nämner inte detta uttryckligen, dokumentationen för datetime2 anger följande:

Den första byten i en datetime2 värde lagrar värdets precision, vilket innebär den faktiska lagring som krävs för en datetime2 värde är lagringsstorleken som anges i tabellen ovan plus 1 extra byte för att lagra precisionen. Detta gör den maximala storleken för en datetime2 värde 9 byte – 1 byte lagrar precision plus 8 byte för datalagring med maximal precision.

Och datetime2 datatyp fungerar på exakt samma sätt med avseende på ovanstående exempel. Med andra ord rapporterar den bara den extra byten när den konverteras till varbinary .

Så den extra byten som nämns i Microsoft-dokumentationen verkar också gälla tid .

Men den faktiska lagringsstorleken för din tid värden kommer på var data lagras.

Data lagrad i en databas

När en databaskolumn har en typ av tid , dess precision anges på kolumnnivå – inte på datanivå. Med andra ord, det anges en gång för hela kolumnen. Detta är vettigt, för när du definierar en kolumn som tid(7) , du vet att alla rader kommer att vara tid(7) . Du behöver inte använda upp värdefulla bytes som upprepar detta faktum på varje rad.

När du undersöker en tid värde som det lagras i SQL Server, kommer du att se att det är samma som varbinary resultat, men utan precisionen.

Nedan finns exempel som visar hur tid värden lagras i SQL Server.

I dessa exempel skapar jag en databas med olika tid(n) kolumner och använd sedan COL_LENGTH() för att returnera varje kolumns längd, i byte. Jag infogar sedan värden i dessa kolumner innan jag använder DBCC PAGE för att kontrollera lagringsstorleken som varje gång värde tar upp på sidfilen.

Skapa en databas:

CREATE DATABASE Test;

Skapa en tabell:

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)
    );

I det här fallet skapar jag åtta kolumner – en för varje användardefinierad skala som vi kan använda med tid(n) .

Nu kan vi kontrollera lagringsstorleken för varje kolumn.

Längd i byte med COL_LENGTH()

Använd COL_LENGTH() för att kontrollera längden (i byte) för varje kolumn:

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';  

Resultat:

+------+------+------+------+------+------+------+------+
| t0   | t1   | t2   | t3   | t4   | t5   | t6   | t7   |
|------+------+------+------+------+------+------+------|
| 3    | 3    | 3    | 4    | 4    | 5    | 5    | 5    |
+------+------+------+------+------+------+------+------+

Så återigen får vi samma resultat som dokumentationen säger att vi kommer att få. Detta är att vänta, eftersom dokumentationen uttryckligen anger "Kolumnlängd (bytes)", vilket är exakt vad vi mäter här.

Kom ihåg att det här är före vi lägger in alla data. Kolumnerna bestämmer själva precisionen (och därmed lagringsstorleken) för all data som infogas – inte tvärtom.

Använd DBCC PAGE för att kontrollera lagrad data

Låt oss nu infoga data och sedan använda DBCC PAGE för att hitta den faktiska lagringsstorleken för data som vi lagrar i varje kolumn.

Infoga 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;

Välj nu data (bara för att kontrollera det):

SELECT * FROM TimeTest;

Resultat (med vertikal utdata):

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

Som förväntat använder värdena den precision som tidigare har angetts på kolumnnivå.

Observera att mitt system visar avslutande nollor. Din kanske gör det eller inte. Oavsett vilket påverkar detta inte den faktiska precisionen eller noggrannheten.

Nu, innan vi använder DBCC PAGE() måste vi veta vilket PagePID som ska skickas till det. Vi kan använda DBCC IND() för att hitta det.

Hitta PagePID:

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

Resultat (med vertikal utdata):

-[ 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

Detta returnerar två rekord. Vi är intresserade av PageType of 1 (den andra posten). Vi vill ha PagePID från den posten. I det här fallet är PagePID 384 .

Nu kan vi ta det PagePID och använda det i följande:

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

Just nu är vi främst intresserade av följande del:

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                                                                      

Så vi får samma resultat som dokumentationen anger. Detta skulle tyda på att precisionen inte lagras med värdena.

Vi kan bekräfta det genom att undersöka de faktiska uppgifterna.

De faktiska tidsvärdena lagras i denna del av sidfilen:

Memory Dump @0x0000000423ADA060

0000000000000000:   10002400 42900095 a205d459 384b8233 02f31603  ..$.B..•¢.ÔY8K‚3.ó..
0000000000000014:   167ae51e dc00c1f6 34990887 a311fc55 080000    .zå.Ü.Áö4..‡£.üU...

Vi kan extrahera de faktiska tidsvärdena genom att ta bort några saker. När den tagits bort kommer följande att finnas kvar:

42900095 a205d459 384b8233 02f31603
167ae51e dc00c1f6 34990887 a311fc55

Dessa hexadecimala siffror innehåller all vår tidsdata, men inte precisionen . Men de är ordnade i 4 byte-bitar, så vi måste ordna om utrymmena för att få de individuella värdena.

Här är slutresultatet. Jag har placerat varje datum/tidsvärde på en ny rad för bättre läsbarhet.

429000
95a205
d45938
4b823302
f3160316
7ae51edc00
c1f6349908
87a311fc55

Det är de faktiska hexadecimala värdena (minus precisionen ) som vi skulle få om vi konverterade tiden värde till varbinary . Så här:

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;

Resultat (med vertikal utdata):

t0 | 0x00429000
t1 | 0x0195A205
t2 | 0x02D45938
t3 | 0x034B823302
t4 | 0x04F3160316
t5 | 0x057AE51EDC00
t6 | 0x06C1F6349908
t7 | 0x0787A311FC55

Den frågan ger samma resultat – förutom att varje värde har satts in med precisionen.

Här är en tabell som jämför den faktiska sidfilens data med resultaten av CONVERT() operation.

Sidfilsdata CONVERT() Data
429000 00429000
95a205 0195A205
d45938 02D45938
4b823302 034B823302
f3160316 04F3160316
7ae51edc00 057AE51EDC00
c1f6349908 06C1F6349908
87a311fc55 0787A311FC55

Så vi kan se att sidfilen inte lagrar precisionen, men det konverterade resultatet gör det.

Jag markerade de faktiska datum- och tidsdelarna i rött. Jag tog också bort 0x prefix från de konverterade resultaten, så att endast de faktiska datum-/tidsdata visas (tillsammans med precisionen).

Observera också att hexadecimal är skiftlägesokänslig, så det faktum att den ena använder gemener och den andra använder versaler är inget problem.

Slutsats

När du konverterar en tid värde till varbinary , behöver den en extra byte för att lagra precisionen. Den behöver precisionen för att tolka tidsdelen (eftersom denna lagras som ett tidsintervall, vars exakta värde beror på precisionen).

När den lagras i en databas anges precisionen en gång på kolumnnivå. Detta verkar logiskt, eftersom det inte finns något behov av att lägga till precisionen till varje rad när alla rader ändå har samma precision. Det skulle kräva en extra byte för varje rad, vilket skulle öka lagringskraven i onödan.


  1. Kan vi ha flera MED AS i singel sql - Oracle SQL

  2. Installera Laravel på en Mac php artisan migreringsfel:Ingen sådan fil eller katalog

  3. ORACLE Hur man använder spolen med dynamisk spolplacering

  4. Hur man skriver lagrade procedurer för professionella SSRS-rapporter