Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

datetime2 vs smalldatetime w SQL Server:jaka jest różnica?

W tym artykule omówiono główne różnice między datetime2 i smalldatetime typy danych w SQL Server.

Oba typy danych są używane do przechowywania wartości daty i godziny, jednak istnieją między nimi pewne istotne różnice. W większości przypadków lepiej jest użyć datetime2 (Microsoft również to zaleca), jednak mogą istnieć scenariusze, w których trzeba użyć smalldatetime .

Oto tabela przedstawiająca kluczowe różnice między tymi dwoma typami.

Funkcja smalldatetime dataczas2
Zgodny z SQL (ANSI i ISO 8601) Nie Tak
Zakres dat 1900-01-01 do 2079-06-06 0001-01-01 do 9999-12-31
Zakres czasu 00:00:00 do 23:59:59 00:00:00 do 23:59:59,999999
Długość znaku maksymalnie 19 pozycji Minimum 19 pozycji
Maksimum 27
Rozmiar pamięci 4 bajty, stałe 6 do 8 bajtów, w zależności od precyzji*

* Plus 1 bajt do przechowywania precyzji

Dokładność Jedna minuta 100 nanosekund
Ułamkowa druga precyzja Nie Tak
Zdefiniowana przez użytkownika precyzja ułamkowa sekundy Nie Tak
Przesunięcie strefy czasowej Brak Brak
Świadomość i zachowanie przesunięcia strefy czasowej Nie Nie
Świadomość czasu letniego Nie Nie

Zalety „datetime2”

Jak widać w powyższej tabeli, datetime2 typ ma wiele zalet w porównaniu z smalldatetime , w tym:

  • większy zakres dat
  • dokładność ułamków sekund
  • opcjonalna precyzja określona przez użytkownika
  • wyższa dokładność
  • jest zgodny ze standardami SQL (ANSI i ISO 8601)

* W niektórych przypadkach datetime2 value wykorzystuje dodatkowy bajt do przechowywania precyzji, jednak gdy jest przechowywana w bazie danych, precyzja jest zawarta w definicji kolumny, więc rzeczywista przechowywana wartość nie wymaga dodatkowego bajtu.

Czy powinienem używać „datetime” czy „smalldatetime”?

Microsoft zaleca datetime2 do nowej pracy (i z tych samych powodów wymienionych powyżej).

Dlatego powinieneś używać datetime2 , chyba że masz konkretny powód, aby tego nie robić (np. praca ze starszym systemem).

Przykład 1 – Porównanie podstawowe

Oto krótki przykład pokazujący podstawową różnicę między datetime2 i 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';

Wynik:

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

Tutaj ustawiam smalldatetime zmienna na taką samą wartość jak datetime2 zmienny. Powoduje to przekonwertowanie wartości na smalldatetime a następnie możemy użyć SELECT oświadczenie, aby zobaczyć wartość każdej zmiennej.

W tym przypadku datetime2 zmienna używa skali 7, co oznacza, że ​​ma 7 miejsc po przecinku. smalldatetime z drugiej strony nie ma żadnej miejsca dziesiętne. Co więcej, jego sekundy są ustawione na zero, a minuty są zaokrąglane w górę.

Należy się tego spodziewać, ponieważ oficjalna dokumentacja Microsoft stwierdza, że ​​smalldatetime czas jest oparty na dobie 24-godzinnej, z sekundami zawsze zero (:00) i bez ułamków sekund .

Widzimy więc, że datetime2 type zapewnia znacznie bardziej precyzyjną i dokładną wartość daty/czasu.

Oczywiście możesz nie potrzebować tych wszystkich ułamków sekund. Jedna z dobrych rzeczy związanych z datetime2 jest to, że możesz określić, ile (jeśli w ogóle) ułamków sekund chcesz.

Przykład 2 – użycie mniejszej liczby miejsc dziesiętnych

W tym przykładzie zmniejszam datetime2 skaluj do 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';

Wynik:

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

W tym przypadku datetime2 wartość nie zawiera już części ułamkowej. Oba typy mają teraz tę samą długość znaków (19 pozycji).

Ale nadal istnieją różnice.

data/godzina2 value honoruje wartość sekund, chociaż w tym przypadku sekundy zostały zaokrąglone w górę. Jak wspomniano, smalldatetime składnik sekund wartości jest zawsze ustawiony na zero, w tym przypadku jego minuty zostały zaokrąglone w górę.

Powód datetime2 składnik sekund jest zaokrąglany w górę, ponieważ część ułamkowa wynosi 5 lub więcej. Jeśli zmniejszymy część ułamkową, zaokrąglenie nie zostanie wykonane:

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

Wynik:

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

Jednak smalldatetime minuty wartości są nadal zaokrąglane w górę.

Przykład 3 – ustawianie wartości z literałów łańcuchowych

W poprzednich przykładach smalldateime wartość została przypisana przez ustawienie jej na taką samą wartość jak datetime2 wartość. Kiedy to robimy, SQL Server wykonuje niejawną konwersję, aby dane „dopasowały się” do nowego typu danych.

Jeśli jednak spróbujemy przypisać ten sam literał ciągu do smalldatetime zmienna, otrzymujemy błąd:

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

Wynik:

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

Dzieje się tak, ponieważ smalldatetime akceptuje tylko literały ciągów, które mają 3 lub mniej ułamków sekund.

Można by się spodziewać, że nie zaakceptuje literałów ciągów z dowolnym ułamki sekund, ponieważ nie obejmują ułamków sekund, ale tak nie jest. Z radością przyjmuje 3 ułamki sekund, ale nie więcej.

Aby rozwiązać ten problem, musimy zmniejszyć część ułamkową do zaledwie 3 (lub mniej) miejsc po przecinku.

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

Wynik:

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

data/godzina2 typ nie ma tego ograniczenia, nawet przy użyciu skali 0.

Przykład 4 – Rozmiar pamięci

smalldatetime typ danych ma stały rozmiar pamięci wynoszący 4 bajty. To jedna z niewielu korzyści smalldatetime ma ponad datetime2 .

data/godzina2 może mieć 6, 7 lub 8 bajtów, w zależności od jego precyzji. A więc datetime2 wartość zawsze będzie wykorzystywać co najmniej 2 bajty więcej miejsca niż smalldatetime wartość.

Microsoft twierdzi, że datetime2 type wykorzystuje również 1 dodatkowy bajt w celu przechowywania swojej precyzji, w takim przypadku zużyłby co najmniej 3 bajty więcej niż smalldatetime .

Jednak prawdopodobnie zależy to od tego, czy przechowujemy je w tabeli, czy w zmiennej i czy konwertujemy ją na stałą binarną.

Oto, co się stanie, jeśli użyjemy DATALENGTH() funkcja zwracająca liczbę bajtów użytych dla każdej z naszych wartości:

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

Wynik

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

Ale jeśli przekonwertujemy je na varbinary , otrzymujemy:

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

Wynik

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

A więc datetime2 używa dodatkowego bajtu po konwersji na warbinar . Wielu programistów zakłada, że ​​konwersja na varbinary reprezentuje sposób, w jaki SQL Server faktycznie przechowuje wartości daty i godziny.

Jest to jednak tylko częściowo prawda. Chociaż prawdą jest, że SQL Server przechowuje swoje wartości daty i godziny w postaci szesnastkowej, ta wartość szesnastkowa w rzeczywistości nie obejmuje precyzji. Dzieje się tak, ponieważ precyzja jest zawarta w definicji kolumny. Ale kiedy przekonwertujemy na varbinary tak jak w poprzednim przykładzie, precyzja jest poprzedzona, a to dodaje dodatkowy bajt.

Poniższy przykład ilustruje to. Pokazuje, że gdy dane są przechowywane w kolumnie bazy danych, otrzymujemy długość 6 bajtów dla datetime2 vs 4 bajty dla smalldatetime .

Przykład 5 – Rozmiar pamięci dla przechowywanych danych

W tym przykładzie tworzę bazę danych i używam COL_LENGTH aby zwrócić długość każdej kolumny w bajtach. Następnie wstawiam datetime2 i smalldatetime wartość do niego i użyj DBCC PAGE() aby znaleźć długość rzeczywistych danych w pliku strony. Pokazuje nam to miejsce, w którym każdy typ danych jest przechowywany w bazie danych.

Utwórz bazę danych:

CREATE DATABASE CompareTypes;

Utwórz tabelę:

USE CompareTypes;

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

W tym przypadku tworzę dwie kolumny – jedna to datetime2(0) kolumna, a druga to smalldatetime kolumna.

Sprawdź długość kolumny

Sprawdź długość (w bajtach) każdej kolumny:

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

Wynik:

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

Widzimy więc, że datetime2(0) kolumna ma długość 6 bajtów w porównaniu z smalldatetime długość 4 bajtów.

Wstaw dane

Przyjrzyjmy się teraz rozmiarowi przechowywania rzeczywistych wartości daty i godziny, gdy są one przechowywane w SQL Server. Możemy użyć DBCC PAGE() aby sprawdzić rzeczywistą stronę w pliku danych.

Ale najpierw musimy wstawić dane do naszych kolumn.

Wstaw dane:

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

Wybierz dane (tylko po to, aby je sprawdzić):

SELECT * FROM Datetime2vsSmalldatetime;

Wynik:

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

Korzystanie z DBCC PAGE()

Tutaj używamy DBCC PAGE() aby sprawdzić rzeczywistą stronę w pliku danych.

Najpierw użyjemy DBCC IND() aby znaleźć PagePID:

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

Wynik (przy użyciu wyjścia pionowego):

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

Zwraca to dwa rekordy. Interesuje nas PageType 1 (drugi rekord). Chcemy mieć PagePID z tego rekordu. W tym przypadku PagePID to 344 .

Teraz możemy wziąć ten PagePID i użyć go w następujący sposób:

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

Daje to dużo danych, ale interesuje nas głównie następująca część:

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 pokazuje, że smalldatetime ma długość 4 bajtów i datetime2(0) ma 6 bajtów, gdy jest przechowywany w bazie danych.

W tym przypadku jest tylko różnica 2 bajtów, ale datetime2(0) jest dokładniejszy i zgodny z normami ANSI i ISO 8601.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pętla SQL Server - jak mogę przejść przez zestaw rekordów

  2. Typ danych SQL Server BIT — ostateczny przewodnik

  3. Jak dodać właściwość tożsamości do istniejącej kolumny w SQL Server?

  4. Dlaczego NULL =NULL daje wartość false w serwerze SQL?

  5. Połącz PowerShell i SQL Diagnostic Manager, aby zautomatyzować monitorowanie SQL Server