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

datetime2 vs smalldatetime v SQL Server:Jaký je rozdíl?

Tento článek zkoumá hlavní rozdíly mezi datetime2 a smalldatetime datové typy na serveru SQL.

Oba datové typy se používají pro ukládání hodnot data a času, nicméně mezi nimi jsou některé důležité rozdíly. Ve většině případů je lepší použít datetime2 (Microsoft to také doporučuje), mohou však existovat některé scénáře, kdy budete muset použít smalldatetime .

Zde je tabulka, která uvádí hlavní rozdíly mezi těmito dvěma typy.

Funkce smalldatetime datetime2
Vyhovující SQL (ANSI &ISO 8601) Ne Ano
Časové období 1900-01-01 až 2079-06-06 0001-01-01 až 9999-12-31
Časový rozsah 00:00:00 až 23:59:59 00:00:00 až 23:59:59.9999999
Délka znaků Maximálně 19 pozic Minimálně 19 pozic
Maximálně 27
Velikost úložiště 4 bajty, opraveno 6 až 8 bajtů, v závislosti na přesnosti*

* Plus 1 bajt pro uložení přesnosti

Přesnost Jedna minuta 100 nanosekund
Zlomková sekundová přesnost Ne Ano
Uživatelsky definovaná přesnost na zlomek sekund Ne Ano
Posun časového pásma Žádné Žádné
Sledování a zachování posunu časového pásma Ne Ne
Sledování letního času Ne Ne

Výhody ‚datetime2‘

Jak je vidět v tabulce výše, datetime2 typ má mnoho výhod oproti smalldatetime , včetně:

  • větší časové období
  • přesnost na zlomky sekund
  • volitelná přesnost zadaná uživatelem
  • vyšší přesnost
  • je v souladu se standardy SQL (ANSI a ISO 8601)

* V některých případech datetime2 value používá k uložení přesnosti bajt navíc, ale když je uložena v databázi, je přesnost zahrnuta do definice sloupce, takže skutečná uložená hodnota nevyžaduje bajt navíc.

Mám použít „datetime“ nebo „smalldatetime“?

Společnost Microsoft doporučuje datetime2 pro novou práci (a ze stejných důvodů uvedených výše).

Proto byste měli použít datetime2 , pokud k tomu nemáte konkrétní důvod (například práce se starším systémem).

Příklad 1 – Základní srovnání

Zde je rychlý příklad demonstrující základní rozdíl mezi datetime2 a 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';

Výsledek:

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

Zde nastavím smalldatetime proměnnou na stejnou hodnotu jako datetime2 variabilní. To způsobí, že se hodnota převede na smalldatetime a pak můžeme použít SELECT pro zobrazení hodnoty každé proměnné.

V tomto případě datetime2 proměnná používá stupnici 7, což znamená, že má 7 desetinných míst. smalldatetime hodnotu na druhé straně nemá žádnou desetinná místa. Kromě toho jsou jeho sekundy nastaveny na nulu a jeho minuty jsou zaokrouhleny nahoru.

Dá se to očekávat, protože oficiální dokumentace společnosti Microsoft uvádí, že smalldatetime Čas je založen na 24hodinovém dni, přičemž sekundy jsou vždy nula (:00) a bez zlomků sekund .

Takže vidíme, že datetime2 type poskytuje mnohem přesnější a přesnější hodnotu data/času.

Samozřejmě možná nebudete potřebovat všechny ty zlomkové sekundy. Jedna z dobrých věcí na datetime2 je, že můžete určit, kolik (pokud vůbec) zlomkových sekund chcete.

Příklad 2 – Použití méně desetinných míst

V tomto příkladu zmenšuji datetime2 měřítko na 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';

Výsledek:

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

V tomto případě datetime2 hodnota již nezahrnuje zlomkovou část. Oba typy nyní sdílejí stejnou délku znaků (19 pozic).

Ale stále existují rozdíly.

datetime2 value respektuje hodnotu sekund, i když v tomto případě byly její sekundy zaokrouhleny nahoru. Jak již bylo zmíněno, smalldatetime složka sekund hodnoty je vždy nastavena na nulu a v tomto případě byly její minuty zaokrouhleny nahoru.

Důvodem je datetime2 sekundová složka je zaokrouhlena nahoru, protože zlomková část je 5 nebo vyšší. Zmenšíme-li zlomkovou část, žádné zaokrouhlení se neprovede:

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

Výsledek:

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

Nicméně smalldatetime minuty hodnoty se nadále zaokrouhlují nahoru.

Příklad 3 – Nastavení hodnot z řetězcových literálů

V předchozích příkladech smalldatetime hodnota byla přiřazena nastavením na stejnou hodnotu jako datetime2 hodnota. Když to uděláme, SQL Server provede implicitní převod, aby data „seděla“ novému datovému typu.

Pokud se však pokusíme přiřadit stejný řetězcový literál k smalldatetime proměnné, dostaneme chybu:

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

Výsledek:

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

Důvodem je smalldatetime přijímá pouze řetězcové literály, které mají 3 nebo méně zlomkových sekund.

Můžete očekávat, že nebude akceptovat řetězcové literály s any zlomkové sekundy, protože nezahrnuje zlomkové sekundy, ale není tomu tak. S radostí přijímá 3 zlomkové sekundy, ale ne více.

Abychom tento problém překonali, musíme zmenšit zlomkovou část na pouhá 3 (nebo méně) desetinná místa.

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

Výsledek:

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

datetime2 typ nemá toto omezení, a to ani při použití stupnice 0.

Příklad 4 – Velikost úložiště

smalldatetime datový typ má pevnou velikost úložiště 4 bajty. Toto je jedna z mála výhod smalldatetime má více než datetime2 .

datetime2 může mít buď 6, 7 nebo 8 bajtů, v závislosti na jeho přesnosti. Tedy datetime2 hodnota bude vždy využívat alespoň o 2 bajty více úložiště než smalldatetime hodnotu.

Microsoft uvádí, že datetime2 type také používá 1 bajt navíc k uložení své přesnosti, v takovém případě by použil alespoň o 3 bajty více než smalldatetime .

To však pravděpodobně závisí na tom, zda ji ukládáme do tabulky nebo do proměnné a zda ji převádíme na binární konstantu.

Zde je to, co se stane, když použijeme DATALENGTH() funkce, která vrátí počet bajtů použitých pro každou z našich hodnot:

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

Výsledek

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

Pokud je ale převedeme na varbinární , dostaneme následující:

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

Výsledek

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

Takže datetime2 používá při převodu na varbinary bajt navíc . Mnoho vývojářů předpokládá, že převod na varbinary je reprezentativní pro to, jak SQL Server skutečně ukládá hodnoty data a času.

To je ovšem pravda jen částečně. I když je pravda, že SQL Server ukládá své hodnoty data a času v šestnáctkové soustavě, tato hexadecimální hodnota ve skutečnosti nezahrnuje přesnost. Je to proto, že přesnost je zahrnuta v definici sloupce. Ale když převedeme na varbinary stejně jako v předchozím příkladu je předřazena přesnost, což přidává další bajt.

Následující příklad to ukazuje. Ukazuje, že když jsou data uložena ve sloupci databáze, získáme délku 6 bajtů pro datetime2 vs 4 bajty pro smalldatetime .

Příklad 5 – Velikost úložiště pro uložená data

V tomto příkladu vytvořím databázi a použiji COL_LENGTH vrátit délku každého sloupce v bajtech. Poté vložím datetime2 a smalldatetime do něj a použijte DBCC PAGE() zjistit délku skutečných dat v souboru stránky. To nám ukazuje úložný prostor, který jednotlivé typy dat využívají, když jsou uloženy v databázi.

Vytvořte databázi:

CREATE DATABASE CompareTypes;

Vytvořte tabulku:

USE CompareTypes;

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

V tomto případě vytvořím dva sloupce – jeden je datetime2(0) a druhý je smalldatetime sloupec.

Zkontrolujte délku sloupce

Zkontrolujte délku (v bajtech) každého sloupce:

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

Výsledek:

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

Vidíme tedy, že datetime2(0) sloupec má délku 6 bajtů ve srovnání s smalldatetime s délkou 4 bajtů.

Vložit data

Nyní se podívejme na velikost úložiště skutečných hodnot data a času, když jsou uloženy na serveru SQL. Můžeme použít DBCC PAGE() zkontrolovat skutečnou stránku v datovém souboru.

Nejprve však musíme do našich sloupců vložit data.

Vložit data:

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

Vyberte data (jen pro kontrolu):

SELECT * FROM Datetime2vsSmalldatetime;

Výsledek:

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

Použití DBCC PAGE()

Zde používáme DBCC PAGE() zkontrolovat skutečnou stránku v datovém souboru.

Nejprve použijeme DBCC IND() k nalezení PagePID:

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

Výsledek (při použití vertikálního výstupu):

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

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 344 .

Nyní můžeme vzít toto PagePID a použít jej v následujícím:

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

To produkuje spoustu dat, ale nás zajímá hlavně následující část:

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                                          

To ukazuje, že smalldatetime má délku 4 bajty a datetime2(0) má při uložení v databázi 6 bajtů.

V tomto případě je tedy rozdíl pouze 2 bajty, ale datetime2(0) je přesnější a dodržuje normy ANSI a ISO 8601.


  1. 5 běžných chyb, kterým se při odstraňování duplicitních dat vyhnout

  2. Oprava:„přední přesnost intervalu je příliš malá“ v databázi Oracle

  3. Provádí EXCEPT rychleji než JOIN, když jsou sloupce tabulky stejné

  4. Tipy ke snížení složitosti serveru SQL