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

datetime2 vs. smalldatetime in SQL Server:Was ist der Unterschied?

In diesem Artikel werden die Hauptunterschiede zwischen datetime2 untersucht und smalldatetime Datentypen in SQL Server.

Beide Datentypen werden zum Speichern von Datums- und Uhrzeitwerten verwendet, es gibt jedoch einige wichtige Unterschiede zwischen den beiden. In den meisten Fällen ist es besser, datetime2 zu verwenden (Microsoft empfiehlt dies ebenfalls), es kann jedoch einige Szenarien geben, in denen Sie smalldatetime verwenden müssen .

Hier ist eine Tabelle, die die Hauptunterschiede zwischen diesen beiden Typen umreißt.

Funktion smalldatetime datetime2
SQL-konform (ANSI &ISO 8601) Nein Ja
Zeitraum 1.1.1900 bis 6.6.2079 0001-01-01 bis 9999-12-31
Zeitbereich 00:00:00 bis 23:59:59 00:00:00 bis 23:59:59.9999999
Zeichenlänge Maximal 19 Positionen mindestens 19 Positionen
maximal 27
Speichergröße 4 Byte, fest 6 bis 8 Byte, je nach Genauigkeit*

* Plus 1 Byte zum Speichern der Genauigkeit

Genauigkeit Eine Minute 100 Nanosekunden
Präzision auf Sekundenbruchteile Nein Ja
Benutzerdefinierte Genauigkeit für Sekundenbruchteile Nein Ja
Zeitzonenverschiebung Keine Keine
Bewusstsein und Erhaltung der Zeitzonenverschiebung Nein Nein
Sommerzeit bewusst Nein Nein

Vorteile von ‚datetime2‘

Wie in der obigen Tabelle zu sehen, ist die datetime2 type hat viele Vorteile gegenüber smalldatetime , einschließlich:

  • größerer Datumsbereich
  • Präzision in Sekundenbruchteilen
  • optionale benutzerdefinierte Genauigkeit
  • höhere Genauigkeit
  • richtet sich an den SQL-Standards (ANSI &ISO 8601)

* In manchen Fällen ein datetime2 Der Wert verwendet ein zusätzliches Byte zum Speichern der Genauigkeit, aber wenn er in einer Datenbank gespeichert wird, ist die Genauigkeit in der Spaltendefinition enthalten, sodass der tatsächlich gespeicherte Wert das zusätzliche Byte nicht benötigt.

Soll ich „datetime“ oder „smalldatetime“ verwenden?

Microsoft empfiehlt datetime2 für neue Arbeiten (und aus denselben oben aufgeführten Gründen).

Daher sollten Sie datetime2 verwenden , es sei denn, Sie haben einen bestimmten Grund, dies nicht zu tun (z. B. die Arbeit mit einem Legacy-System).

Beispiel 1 – Grundlegender Vergleich

Hier ist ein kurzes Beispiel, um den grundlegenden Unterschied zwischen datetime2 zu demonstrieren und smalldatetime .

DECLARE 
  @thedatetime2 datetime2(7), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.5555555';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Ergebnis:

+-----------------------------+---------------------+
| datetime2                   | smalldatetime       |
|-----------------------------+---------------------|
| 2025-05-21 10:15:30.5555555 | 2025-05-21 10:16:00 |
+-----------------------------+---------------------+

Hier setze ich eine smalldatetime Variable auf den gleichen Wert wie datetime2 Variable. Dadurch wird der Wert in smalldatetime konvertiert und wir können dann ein SELECT verwenden -Anweisung, um den Wert jeder Variablen anzuzeigen.

In diesem Fall datetime2 Die Variable verwendet eine Skala von 7, was bedeutet, dass sie 7 Dezimalstellen hat. Die smalldatetime value hingegen hat keine Nachkommastellen. Außerdem werden die Sekunden auf Null gesetzt und die Minuten aufgerundet.

Dies ist zu erwarten, da die offizielle Dokumentation von Microsoft besagt, dass smalldatetime Die Uhrzeit ist basierend auf einem 24-Stunden-Tag, mit Sekunden immer Null (:00) und ohne Sekundenbruchteile .

Wir können also sehen, dass datetime2 type bietet einen viel präziseren und genaueren Datums-/Uhrzeitwert.

Natürlich brauchen Sie all diese Bruchteile von Sekunden nicht. Eines der guten Dinge an datetime2 ist, dass Sie angeben können, wie viele (falls vorhanden) Sekundenbruchteile Sie möchten.

Beispiel 2 – Weniger Dezimalstellen verwenden

In diesem Beispiel reduziere ich die datetime2 Skalierung auf 0:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.5555555';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Ergebnis:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:31 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

In diesem Fall datetime2 Der Wert enthält keinen Bruchteil mehr. Beide Typen haben nun dieselbe Zeichenlänge (19 Stellen).

Aber es gibt immer noch Unterschiede.

Die datetime2 value berücksichtigt den Sekundenwert, obwohl in diesem Fall die Sekunden aufgerundet wurden. Wie bereits erwähnt, die smalldatetime Die Sekundenkomponente des Werts wird immer auf Null gesetzt, und in diesem Fall wurden die Minuten aufgerundet.

Der Grund ist datetime2 Sekundenkomponente aufgerundet wird, weil der Bruchteil 5 oder höher ist. Wenn wir den Bruchteil kürzen, wird nicht gerundet:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.4444444';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Ergebnis:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

Die smalldatetime Die Minuten des Werts werden weiterhin aufgerundet.

Beispiel 3 – Setzen von Werten aus String-Literalen

In den vorherigen Beispielen wurde die smalldateime value wurde zugewiesen, indem es auf den gleichen Wert wie datetime2 gesetzt wurde Wert. Wenn wir das tun, führt SQL Server eine implizite Konvertierung durch, damit die Daten dem neuen Datentyp „passen“.

Wenn wir jedoch versuchen, smalldatetime dasselbe Zeichenfolgenliteral zuzuweisen Variable erhalten wir einen Fehler:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime
SET @thedatetime2 = '2025-05-21 10:15:30.4444444'
SET @thesmalldatetime = '2025-05-21 10:15:30.4444444'
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Ergebnis:

Msg 295, Level 16, State 3, Line 5
Conversion failed when converting character string to smalldatetime data type.

Das liegt an smalldatetime akzeptiert nur Zeichenfolgenliterale mit 3 oder weniger Sekundenbruchteilen.

Sie könnten erwarten, dass es keine String-Literale mit any akzeptiert Bruchteile von Sekunden, da es keine Sekundenbruchteile enthält, aber das ist nicht der Fall. Es akzeptiert gerne 3 Sekundenbruchteile, aber nicht mehr.

Um dieses Problem zu lösen, müssen wir also den Bruchteil auf nur 3 (oder weniger) Dezimalstellen reduzieren.

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30.4444444';
SET @thesmalldatetime = '2025-05-21 10:15:30.444';
SELECT 
  @thedatetime2 AS 'datetime2',
  @thesmalldatetime AS 'smalldatetime';

Ergebnis:

+---------------------+---------------------+
| datetime2           | smalldatetime       |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

Die datetime2 Typ hat diese Einschränkung nicht, selbst wenn eine Skala von 0 verwendet wird.

Beispiel 4 – Speichergröße

Die smalldatetime Datentyp hat eine feste Speichergröße von 4 Bytes. Dies ist einer der wenigen Vorteile von smalldatetime hat über datetime2 .

Die datetime2 kann je nach Genauigkeit entweder 6, 7 oder 8 Byte groß sein. Also ein datetime2 -Wert wird immer mindestens 2 Bytes mehr Speicherplatz benötigen als ein smalldatetime Wert.

Microsoft gibt an, dass die datetime2 type verwendet auch 1 zusätzliches Byte, um seine Genauigkeit zu speichern, in diesem Fall würde es mindestens 3 Bytes mehr als smalldatetime verwenden .

Dies hängt jedoch wahrscheinlich davon ab, ob wir es in einer Tabelle oder in einer Variablen speichern und ob wir es in eine binäre Konstante konvertieren oder nicht.

Folgendes passiert, wenn wir DATALENGTH() verwenden Funktion, um die Anzahl der Bytes zurückzugeben, die für jeden unserer Werte verwendet werden:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  DATALENGTH(@thedatetime2) AS 'datetime2',
  DATALENGTH(@thesmalldatetime) AS 'smalldatetime';

Ergebnis

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 6           | 4               |
+-------------+-----------------+

Aber wenn wir sie in varbinary umwandeln , erhalten wir Folgendes:

DECLARE 
  @thedatetime2 datetime2(0), 
  @thesmalldatetime smalldatetime;
SET @thedatetime2 = '2025-05-21 10:15:30';
SET @thesmalldatetime = @thedatetime2;
SELECT 
  DATALENGTH(CAST(@thedatetime2 AS varbinary(10))) AS 'datetime2',
  DATALENGTH(CAST(@thesmalldatetime AS varbinary(10))) AS 'smalldatetime';

Ergebnis

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 7           | 4               |
+-------------+-----------------+

Also datetime2 verwendet ein zusätzliches Byte, wenn es in varbinary konvertiert wird . Viele Entwickler gehen davon aus, dass die Konvertierung in varbinary ist repräsentativ dafür, wie SQL Server tatsächlich Datums- und Uhrzeitwerte speichert.

Dies stimmt jedoch nur teilweise. Es stimmt zwar, dass SQL Server seine Datums- und Uhrzeitwerte im Hexadezimalformat speichert, aber dieser Hexadezimalwert enthält nicht wirklich die Genauigkeit. Dies liegt daran, dass die Genauigkeit in der Spaltendefinition enthalten ist. Aber wenn wir in varbinary konvertieren Wie im vorherigen Beispiel wird die Genauigkeit vorangestellt, und dies fügt ein zusätzliches Byte hinzu.

Das folgende Beispiel demonstriert dies. Es zeigt, dass wir, wenn die Daten in einer Datenbankspalte gespeichert werden, eine Länge von 6 Bytes für datetime2 erhalten gegenüber 4 Bytes für smalldatetime .

Beispiel 5 – Speichergröße für gespeicherte Daten

In diesem Beispiel erstelle ich eine Datenbank und verwende COL_LENGTH um die Länge jeder Spalte in Bytes zurückzugeben. Dann füge ich ein datetime2 ein und smalldatetime Wert hinein und verwenden Sie DBCC PAGE() um die Länge der tatsächlichen Daten in der Auslagerungsdatei zu finden. Dies zeigt uns den Speicherplatz, den jeder Datentyp verwendet, wenn er in einer Datenbank gespeichert wird.

Erstellen Sie eine Datenbank:

CREATE DATABASE CompareTypes;

Erstellen Sie eine Tabelle:

USE CompareTypes;

CREATE TABLE Datetime2vsSmalldatetime (
    TheDateTime2 datetime2(0),
    TheSmallDateTime smalldatetime
    );

In diesem Fall erstelle ich zwei Spalten – eine ist ein datetime2(0) Spalte und die andere ist eine smalldatetime Spalte.

Überprüfen Sie die Spaltenlänge

Prüfen Sie die Länge (in Bytes) jeder Spalte:

SELECT 
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheDateTime2' ) AS 'datetime2',
  COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheSmallDateTime' ) AS 'smalldatetime';  

Ergebnis:

+-------------+-----------------+
| datetime2   | smalldatetime   |
|-------------+-----------------|
| 6           | 4               |
+-------------+-----------------+

Wir sehen also, dass datetime2(0) Spalte hat im Vergleich zu smalldatetime eine Länge von 6 Bytes Länge von 4 Bytes.

Daten einfügen

Sehen wir uns nun die Speichergröße der tatsächlichen Datums- und Uhrzeitwerte an, wenn sie in SQL Server gespeichert werden. Wir können DBCC PAGE() verwenden um die aktuelle Seite in der Datendatei zu überprüfen.

Aber zuerst müssen wir Daten in unsere Spalten einfügen.

Daten einfügen:

DECLARE @thedatetime2 datetime2 = '2025-05-21 10:15:30';
INSERT INTO Datetime2vsSmalldatetime ( TheSmallDateTime, TheDateTime2 )
SELECT @thedatetime2, @thedatetime2;

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

SELECT * FROM Datetime2vsSmalldatetime;

Ergebnis:

+---------------------+---------------------+
| TheDateTime2        | TheSmallDateTime    |
|---------------------+---------------------|
| 2025-05-21 10:15:30 | 2025-05-21 10:16:00 |
+---------------------+---------------------+

Verwendung von DBCC PAGE()

Hier verwenden wir DBCC PAGE() um die aktuelle Seite in der Datendatei zu überprüfen.

Zuerst verwenden wir DBCC IND() um die PagePID zu finden:

DBCC IND('CompareTypes', 'dbo.Datetime2vsSmalldatetime', 0);

Ergebnis (bei vertikaler Ausgabe):

-[ RECORD 1 ]-------------------------
PageFID         | 1
PagePID         | 308
IAMFID          | NULL
IAMPID          | NULL
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
iam_chain_type  | In-row data
PageType        | 10
IndexLevel      | NULL
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0
-[ RECORD 2 ]-------------------------
PageFID         | 1
PagePID         | 344
IAMFID          | 1
IAMPID          | 308
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
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 344 .

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

DBCC TRACEON(3604, -1);
DBCC PAGE(CompareTypes, 1, 344, 3);

Dies erzeugt eine Menge Daten, aber wir interessieren uns hauptsächlich für den folgenden Teil:

Slot 0 Column 1 Offset 0x4 Length 6 Length (physical) 6

TheDateTime2 = 2025-05-21 10:15:30  

Slot 0 Column 2 Offset 0xa Length 4 Length (physical) 4

TheSmallDateTime = 2025-05-21 10:16:00.000                                          

Dies zeigt diese smalldatetime hat eine Länge von 4 Bytes und datetime2(0) hat 6 Bytes, wenn es in einer Datenbank gespeichert wird.

In diesem Fall gibt es also nur einen Unterschied von 2 Byte, aber datetime2(0) ist genauer und entspricht den Standards ANSI und ISO 8601.