sql >> Databasteknik >  >> RDS >> Sqlserver

datetime2 vs smalldatetime i SQL Server:Vad är skillnaden?

Den här artikeln utforskar de viktigaste skillnaderna mellan datetime2 och smalldatetime datatyper i SQL Server.

Båda datatyperna används för att lagra datum- och tidsvärden, men det finns några viktiga skillnader mellan de två. I de flesta fall är det bättre att använda datetime2 (Microsoft rekommenderar också detta), men det kan finnas vissa scenarier där du behöver använda smalldatetime .

Här är en tabell som beskriver de viktigaste skillnaderna mellan dessa två typer.

Funktion smalldatetime datetime2
SQL-kompatibel (ANSI &ISO 8601) Nej Ja
Datumintervall 1900-01-01 till 2079-06-06 0001-01-01 till 9999-12-31
Tidsintervall 00:00:00 till 23:59:59 00:00:00 till 23:59:59.9999999
Teckenlängd max 19 positioner 19 positioner minst
27 max
Lagringsstorlek 4 byte, fast 6 till 8 byte, beroende på precisionen*

* Plus 1 byte för att lagra precisionen

Noggrannhet En minut 100 nanosekunder
Bråkdelssekundprecision Nej Ja
Användardefinierad precision på bråkdelssekunder Nej Ja
Tidszonsförskjutning Inga Inga
Medveten om och bevarande av tidszonförskjutning Nej Nej
Medveten om sommartid Nej Nej

Fördelar med "datetime2"

Som framgår av tabellen ovan är datetime2 typ har många fördelar jämfört med smalldatetime , inklusive:

  • större datumintervall
  • bråkdelssekunderprecision
  • valfri användarspecificerad precision
  • högre noggrannhet
  • överensstämmer med SQL-standarderna (ANSI &ISO 8601)

* I vissa fall en datetime2 värde använder en extra byte för att lagra precisionen, men när den lagras i en databas ingår precisionen i kolumndefinitionen, så det faktiska lagrade värdet kräver inte den extra byten.

Ska jag använda 'datetime' eller 'smalldatetime'?

Microsoft rekommenderar datetime2 för nytt arbete (och av samma skäl som anges ovan).

Därför bör du använda datetime2 , såvida du inte har en specifik anledning att inte göra det (som att arbeta med ett äldre system).

Exempel 1 – Grundläggande jämförelse

Här är ett snabbt exempel för att visa den grundläggande skillnaden mellan datetime2 och 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';

Resultat:

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

Här ställer jag in en smalldatetime variabel till samma värde som datetime2 variabel. Detta gör att värdet konverteras till smalldatetime och vi kan sedan använda en SELECT uttalande för att se värdet av varje variabel.

I det här fallet, datetime2 variabeln använder en skala på 7, vilket betyder att den har 7 decimaler. smalldatetime värde å andra sidan, har inte något decimaler. Dessutom är dess sekunder nollställda och minuterna avrundas uppåt.

Detta är att vänta, eftersom Microsofts officiella dokumentation säger att smalldatetime 's tid är baserad på en 24-timmarsdygn, med sekunder alltid noll (:00) och utan bråkdelar .

Så vi kan se att datetime2 typ ger ett mycket mer exakt och exakt datum/tidsvärde.

Naturligtvis kanske du inte behöver alla dessa bråkdelar. En av de bra sakerna med datetime2 är att du kan ange hur många (om några) bråkdelar du vill ha.

Exempel 2 – Använd färre decimaler

I det här exemplet minskar jag datetime2 skala till 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';

Resultat:

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

I det här fallet, datetime2 värdet inkluderar inte längre en bråkdel. Båda typerna delar nu samma teckenlängd (19 positioner).

Men det finns fortfarande skillnader.

datetime2 värde respekterar sekundvärdet, även om dess sekunder i detta fall har avrundats uppåt. Som nämnts, smalldatetime värdets sekundkomponent är alltid noll, och i det här fallet har dess minuter avrundats uppåt.

Anledningen till datetime2 sekunders komponent avrundas uppåt beror på att bråkdelen är 5 eller högre. Om vi ​​minskar bråkdelen utförs ingen avrundning:

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

Resultat:

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

Men smalldatetime värdes minuter fortsätter att avrundas uppåt.

Exempel 3 – Ställa in värden från strängbokstaver

I de tidigare exemplen, smalldatetime värde tilldelades genom att sätta det till samma värde som datetime2 värde. När vi gör det utför SQL Server en implicit konvertering för att data ska "passa" till den nya datatypen.

Men om vi försöker tilldela samma sträng bokstavligt till smalldatetime variabel får vi ett fel:

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

Resultat:

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

Det beror på att smalldatetime accepterar endast strängliteraler som har 3 eller färre bråksekunder.

Du kanske förväntar dig att den inte skulle acceptera strängliteraler med någon bråkdelar, eftersom det inte inkluderar bråkdelar, men så är inte fallet. Den tar gärna emot 3 bråkdelar, men inte mer.

Så för att lösa det här problemet måste vi minska bråkdelen till bara 3 (eller färre) decimaler.

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

Resultat:

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

datetime2 typ har inte denna begränsning, även om du använder en skala på 0.

Exempel 4 – Förvaringsstorlek

smalldatetime datatypen har en fast lagringsstorlek på 4 byte. Detta är en av få fördelar smalldatetime har över datetime2 .

datetime2 kan vara antingen 6, 7 eller 8 byte, beroende på dess precision. Alltså en datetime2 värde kommer alltid att använda minst 2 byte mer lagringsutrymme än en smalldatetime värde.

Microsoft uppger att datetime2 typ använder också 1 extra byte för att lagra sin precision, i vilket fall den skulle använda minst 3 byte mer än smalldatetime .

Men detta beror förmodligen på om vi lagrar det i en tabell eller i en variabel, och om vi konverterar det till en binär konstant eller inte.

Här är vad som händer om vi använder DATALENGTH() funktion för att returnera antalet byte som används för vart och ett av våra värden:

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

Resultat

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

Men om vi konverterar dem till varbinary , får vi följande:

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

Resultat

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

datetime2 använder en extra byte när den konverteras till varbinary . Många utvecklare antar att konvertera till varbinary är representativt för hur SQL Server faktiskt lagrar datum- och tidsvärden.

Detta är dock bara delvis sant. Även om det är sant att SQL Server lagrar sina datum- och tidsvärden hexadecimalt, inkluderar det hexadecimala värdet faktiskt inte precisionen. Det beror på att precisionen ingår i kolumndefinitionen. Men när vi konverterar till varbinary precis som vi gjorde i det föregående exemplet är precisionen prependerad, och detta lägger till en extra byte.

Följande exempel visar detta. Det visar att när data lagras i en databaskolumn får vi en längd på 6 byte för datetime2 kontra 4 byte för smalldatetime .

Exempel 5 – Lagringsstorlek för lagrad data

I det här exemplet skapar jag en databas och använder COL_LENGTH för att returnera varje kolumns längd, i byte. Jag infogar sedan en datetime2 och smalldatetime värde i den och använd DBCC PAGE() för att hitta längden på den faktiska datan i sidfilen. Detta visar oss vilket lagringsutrymme varje datatyp använder när de lagras i en databas.

Skapa en databas:

CREATE DATABASE CompareTypes;

Skapa en tabell:

USE CompareTypes;

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

I det här fallet skapar jag två kolumner – en är en datetime2(0) kolumn och den andra är en smalldatetime kolumn.

Kontrollera kolumnlängden

Kontrollera längden (i byte) för varje kolumn:

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

Resultat:

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

Så vi ser att datetime2(0) kolumnen har en längd på 6 byte, jämfört med smalldatetime s längd på 4 byte.

Infoga data

Låt oss nu titta på lagringsstorleken för de faktiska datum- och tidsvärdena när de lagras i SQL Server. Vi kan använda DBCC PAGE() för att inspektera den faktiska sidan i datafilen.

Men först måste vi infoga data i våra kolumner.

Infoga data:

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

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

SELECT * FROM Datetime2vsSmalldatetime;

Resultat:

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

Med DBCC PAGE()

Det är här vi använder DBCC PAGE() för att inspektera den faktiska sidan i datafilen.

Först använder vi DBCC IND() för att hitta PagePID:

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

Resultat (med vertikal utdata):

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

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

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

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

Detta producerar mycket data, men vi är främst intresserade av följande del:

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                                          

Detta visar att smalldatetime har en längd på 4 byte och datetime2(0) har 6 byte när den lagras i en databas.

Så i det här fallet är det bara en skillnad på 2 byte, men datetime2(0) är mer exakt och följer ANSI- och ISO 8601-standarderna.


  1. Produktrecension – Stellar Repair för MySQL

  2. Hur installerar man ett Python-paket på Linux så att det hittas av den redan fungerande PostgreSQL 13 plpython3u-tillägget?

  3. Nätverksbelastningstestning med iPerf

  4. Hur återskapar man en raderad tabell med Django Migrations?