Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Grundlegendes zur „Zeit“-Speichergröße in SQL Server

In diesem Artikel betrachte ich die Speichergröße der Zeit Datentyp in SQL Server.

Insbesondere schaue ich mir Folgendes an:

  • Dokumentation von Microsoft
  • In einer Variablen gespeicherte Daten
    • Länge in Bytes mit DATALENGTH()
    • Länge in Bytes mit DATALENGTH() nach der Konvertierung in varbinary
  • In einer Datenbank gespeicherte Daten
    • Länge in Bytes mit COL_LENGTH()
    • Länge in Byte mit DBCC PAGE()

Microsoft-Dokumentation

Die offizielle Dokumentation von Microsoft zur Zeit Datentyp gibt an, dass seine Speichergröße zwischen 3 und 5 Bytes liegt, abhängig von der verwendeten Genauigkeit.

Dieser Datentyp ermöglicht eine benutzerdefinierte Genauigkeit. Sie können time(n) verwenden um die Genauigkeit anzugeben, wobei n ist eine Skala zwischen 0 und 7.

Hier sind die Daten, die Microsoft für die Zeit präsentiert Datentyp:

Angegebener Maßstab Ergebnis (Präzision, Skalierung) Spaltenlänge (Bytes) Präzision in Sekundenbruchteilen
Zeit (16,7) 5 7
Zeit(0) (8,0) 3 0-2
Zeit(1) (10,1) 3 0-2
Zeit(2) (11,2) 3 0-2
Zeit(3) (12,3) 4 3-4
Zeit(4) (13,4) 4 3-4
Zeit(5) (14,5) 5 5-7
Zeit(6) (15,6) 5 5-7
Zeit(7) (16,7) 5 5-7

Für die Zwecke dieses Artikels interessiere ich mich hauptsächlich für die Spaltenlänge (Bytes) Säule. Dies sagt uns, wie viele Bytes verwendet werden, um diesen Datentyp in einer Datenbank zu speichern.

Aus Nutzersicht die Zeit Der Datentyp funktioniert genauso wie der Zeitteil von datetime2 . Es hat eine benutzerdefinierte Genauigkeit in Sekundenbruchteilen und akzeptiert eine Skala von 0 bis 7.

Der Rest dieses Artikels führt durch verschiedene Beispiele, in denen ich die Speichergröße von time zurückgebe Werte in verschiedenen Kontexten.

In einer Variablen gespeicherte Daten

Zuerst speichere ich eine Zeit Wert in einer Variablen und überprüfen Sie ihre Speichergröße. Dann konvertiere ich diesen Wert in varbinary und überprüfe es erneut.

Länge in Bytes mit DATALENGTH

Folgendes passiert, wenn wir DATALENGTH() verwenden Funktion, um die Anzahl der für eine Zeit(7) verwendeten Bytes zurückzugeben Wert:

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

Ergebnis

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

Der Wert in diesem Beispiel hat die maximale Skalierung von 7 (weil ich die Variable als time(7) deklariere ) und gibt eine Länge von 5 Bytes zurück.

Dies ist zu erwarten, da es der in der Tabelle von Microsoft angegebenen Speichergröße entspricht.

Wenn wir den Wert jedoch in varbinary umwandeln wir erhalten ein anderes Ergebnis.

Länge in Bytes nach der Konvertierung in „varbinary“

Einige Entwickler konvertieren gerne Zeit oder datetime2 Variablen in varbinary weil es repräsentativer dafür ist, wie SQL Server es in der Datenbank speichert. Obwohl dies teilweise zutrifft, stimmen die Ergebnisse nicht genau mit dem gespeicherten Wert überein (mehr dazu weiter unten).

Folgendes passiert, wenn wir unsere Zeit umrechnen Wert auf 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';

Ergebnis

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

In diesem Fall erhalten wir 6 Bytes. Unser Wert verwendet jetzt 1 Byte mehr als in der Dokumentation angegeben.

Das liegt daran, dass ein zusätzliches Byte benötigt wird, um die Genauigkeit zu speichern.

Dies ist eine hexadezimale Darstellung der Zeit Wert. Der tatsächliche Zeitwert (und seine Genauigkeit) ist alles nach dem 0x . Jedes Paar Hexadezimalzeichen ist ein Byte. Es gibt 6 Paare und daher 6 Bytes. Dies wird bestätigt, wenn wir DATALENGTH() verwenden um die Länge in Bytes zurückzugeben.

In diesem Beispiel sehen wir, dass das erste Byte 07 ist . Dies stellt die Genauigkeit dar (ich habe eine Skala von 7 verwendet und das wird hier angezeigt).

Wenn ich die Skalierung ändere, können wir sehen, dass sich das erste Byte ändert, um der Skalierung zu entsprechen:

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

Ergebnis

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

Wir können auch sehen, dass die Länge entsprechend reduziert wird. Aber auch hier ist es ein Byte mehr als in der Dokumentation angegeben.

Obwohl Microsofts Dokumentation für Zeit erwähnt dies nicht explizit, die Dokumentation zu datetime2 besagt Folgendes:

Das erste Byte eines datetime2 value speichert die Genauigkeit des Werts, d. h. den tatsächlichen Speicherbedarf für ein datetime2 Wert ist die in der obigen Tabelle angegebene Speichergröße plus 1 zusätzliches Byte zum Speichern der Genauigkeit. Dies ergibt die maximale Größe eines datetime2 Wert 9 Byte – 1 Byte speichert die Genauigkeit plus 8 Byte für die Datenspeicherung mit maximaler Genauigkeit.

Und die datetime2 Der Datentyp funktioniert in Bezug auf die obigen Beispiele genauso. Mit anderen Worten, es meldet das zusätzliche Byte nur, wenn es in varbinary konvertiert wird .

Das in der Microsoft-Dokumentation erwähnte zusätzliche Byte scheint also auch für Zeit zu gelten .

Allerdings ist die tatsächliche Speichergröße Ihrer Zeit Werte werden dort angezeigt, wo die Daten gespeichert sind.

In einer Datenbank gespeicherte Daten

Wenn eine Datenbankspalte einen Typ Zeit hat , seine Genauigkeit wird auf Spaltenebene angegeben – nicht auf Datenebene. Mit anderen Worten, es wird einmal für die gesamte Spalte angegeben. Das ist sinnvoll, denn wenn Sie eine Spalte als time(7) definieren , wissen Sie, dass alle Zeilen time(7) sein werden . Keine Notwendigkeit, wertvolle Bytes zu verbrauchen, die diese Tatsache in jeder Zeile wiederholen.

Wenn Sie eine Zeit untersuchen -Wert, da er in SQL Server gespeichert ist, sehen Sie, dass er mit varbinary identisch ist Ergebnis, aber ohne die Präzision.

Nachfolgend finden Sie Beispiele, die zeigen, wie Zeit Werte werden in SQL Server gespeichert.

In diesen Beispielen erstelle ich eine Datenbank mit verschiedenen Zeit(en) Spalten und verwenden Sie dann COL_LENGTH() um die Länge jeder Spalte in Bytes zurückzugeben. Ich füge dann Werte in diese Spalten ein, bevor ich DBCC PAGE verwende um die Speichergröße mal zu überprüfen Wert nimmt die Auslagerungsdatei auf.

Erstellen Sie eine Datenbank:

CREATE DATABASE Test;

Erstellen Sie eine Tabelle:

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

In diesem Fall erstelle ich acht Spalten – eine für jede benutzerdefinierte Skala, die wir mit time(n) verwenden können .

Jetzt können wir die Speichergröße jeder Spalte überprüfen.

Länge in Bytes mit COL_LENGTH()

Verwenden Sie COL_LENGTH() um die Länge (in Bytes) jeder Spalte zu überprüfen:

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

Ergebnis:

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

Wir erhalten also noch einmal das gleiche Ergebnis, das die Dokumentation besagt, dass wir es bekommen werden. Dies ist zu erwarten, da in der Dokumentation ausdrücklich „Spaltenlänge (Bytes)“ angegeben ist, was wir hier genau messen.

Denken Sie daran, dies ist vorher Wir fügen beliebige Daten ein. Die Spalten selbst bestimmen die Genauigkeit (und damit die Speichergröße) aller eingefügten Daten – nicht umgekehrt.

Verwenden Sie DBCC PAGE, um die gespeicherten Daten zu überprüfen

Lassen Sie uns nun Daten einfügen und dann DBCC PAGE verwenden um die tatsächliche Speichergröße der Daten zu ermitteln, die wir in jeder Spalte speichern.

Daten einfügen:

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;

Wählen Sie nun die Daten aus (nur zur Kontrolle):

SELECT * FROM TimeTest;

Ergebnis (bei vertikaler Ausgabe):

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

Wie erwartet verwenden die Werte die Genauigkeit, die zuvor auf Spaltenebene angegeben wurde.

Beachten Sie, dass mein System nachgestellte Nullen anzeigt. Ihre kann dies tun oder auch nicht. Unabhängig davon hat dies keinen Einfluss auf die tatsächliche Präzision oder Genauigkeit.

Nun, bevor wir DBCC PAGE() verwenden , müssen wir wissen, welche PagePID an sie übergeben werden soll. Wir können DBCC IND() verwenden um das zu finden.

Suchen Sie die PagePID:

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

Ergebnis (bei vertikaler Ausgabe):

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

Dies gibt zwei Datensätze zurück. Uns interessiert der PageType von 1 (der 2. Datensatz). Wir wollen die PagePID aus diesem Datensatz. In diesem Fall ist die PagePID 384 .

Jetzt können wir diese PagePID nehmen und sie im Folgenden verwenden:

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

Im Moment interessiert uns hauptsächlich der folgende Teil:

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                                                                      

Wir erhalten also das gleiche Ergebnis wie in der Dokumentation angegeben. Dies würde darauf hindeuten, dass die Genauigkeit nicht mit den Werten gespeichert wird.

Wir können dies bestätigen, indem wir die tatsächlichen Daten untersuchen.

Die tatsächlichen Zeitwerte werden in diesem Teil der Auslagerungsdatei gespeichert:

Memory Dump @0x0000000423ADA060

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

Wir können die tatsächlichen Zeitwerte extrahieren, indem wir ein paar Dinge entfernen. Nach dem Entfernen bleibt Folgendes bestehen:

42900095 a205d459 384b8233 02f31603
167ae51e dc00c1f6 34990887 a311fc55

Diese Hex-Ziffern enthalten alle unsere Zeitdaten, aber nicht die Genauigkeit . Sie sind jedoch in 4-Byte-Blöcke angeordnet, sodass wir die Leerzeichen neu anordnen müssen, um die einzelnen Werte zu erhalten.

Hier ist das Endergebnis. Zur besseren Lesbarkeit habe ich jeden Datums-/Uhrzeitwert in eine neue Zeile eingefügt.

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

Das sind die tatsächlichen Hexadezimalwerte (abzüglich der Genauigkeit ), die wir erhalten würden, wenn wir die Zeit umwandeln würden Wert auf varbinary . So:

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;

Ergebnis (bei vertikaler Ausgabe):

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

Diese Abfrage liefert das gleiche Ergebnis – außer dass jedem Wert die Genauigkeit vorangestellt wurde.

Hier ist eine Tabelle, die die tatsächlichen Daten der Auslagerungsdatei mit den Ergebnissen von CONVERT() vergleicht Betrieb.

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

Wir können also sehen, dass die Auslagerungsdatei die Genauigkeit nicht speichert, aber das konvertierte Ergebnis.

Ich habe die tatsächlichen Datums- und Uhrzeitteile rot hervorgehoben. Ich habe auch das 0x entfernt Präfix aus den konvertierten Ergebnissen, sodass nur die tatsächlichen Datums-/Uhrzeitdaten angezeigt werden (zusammen mit der Genauigkeit).

Beachten Sie auch, dass bei Hexadezimalzahlen die Groß- und Kleinschreibung nicht beachtet wird, sodass die Tatsache, dass der eine Kleinbuchstaben und der andere Großbuchstaben verwendet, kein Problem darstellt.

Schlussfolgerung

Beim Umrechnen einer Zeit Wert auf varbinary , benötigt es ein zusätzliches Byte, um die Genauigkeit zu speichern. Es benötigt die Genauigkeit, um den Zeitabschnitt zu interpretieren (weil dies als Zeitintervall gespeichert wird, dessen genauer Wert von der Genauigkeit abhängt).

Beim Speichern in einer Datenbank wird die Genauigkeit einmal auf Spaltenebene angegeben. Dies erscheint logisch, da die Genauigkeit nicht jeder Zeile hinzugefügt werden muss, wenn alle Zeilen sowieso die gleiche Genauigkeit haben. Das würde ein zusätzliches Byte für jede Zeile erfordern, was den Speicherbedarf unnötig erhöhen würde.