Тази статия разглежда основните разлики между datetime2 и smalldatetime типове данни в SQL Server.
И двата типа данни се използват за съхранение на стойности за дата и час, но има някои важни разлики между двете. В повечето случаи е по-добре да използвате datetime2 (Microsoft също препоръчва това), но може да има някои сценарии, при които трябва да използвате smalldatetime .
Ето таблица, която очертава основните разлики между тези два типа.
Функция | smalldatetime | datetime2 |
---|---|---|
Съвместим с SQL (ANSI и ISO 8601) | Не | Да |
Период от време | 1900-01-01 до 2079-06-06 | 0001-01-01 до 9999-12-31 |
Диапазон от време | 00:00:00 до 23:59:59 | 00:00:00 до 23:59:59,9999999 |
Дължина на знака | Максимум 19 позиции | 19 позиции минимум 27 максимум |
Размер на съхранение | 4 байта, фиксирани | 6 до 8 байта, в зависимост от прецизността* * Плюс 1 байт за съхраняване на точността |
Точност | Една минута | 100 наносекунди |
Дробна секундна точност | Не | Да |
Дефинирана от потребителя точност на дробна секунда | Не | Да |
Изместване на часовата зона | Няма | Няма |
Осведомяване и запазване на изместването на часовата зона | Не | Не |
Наясно с лятното часово време | Не | Не |
Предимства на ‘datetime2’
Както се вижда в таблицата по-горе, datetime2 type има много предимства пред smalldatetime , включително:
- по-голям период от време
- точност до части от секунди
- по избор, определена от потребителя точност
- по-висока точност
- в съответствие със стандартите SQL (ANSI и ISO 8601)
* В някои случаи datetime2 value използва допълнителен байт за съхраняване на точността, но когато се съхранява в база данни, прецизността е включена в дефиницията на колоната, така че действително съхранената стойност не изисква допълнителния байт.
Трябва ли да използвам „datetime“ или „smalldatetime“?
Microsoft препоръчва datetime2 за нова работа (и по същите причини, изброени по-горе).
Следователно, трябва да използвате datetime2 , освен ако нямате конкретна причина да не го правите (като например работа с наследена система).
Пример 1 – Основно сравнение
Ето един бърз пример за демонстриране на основната разлика между datetime2 и 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';
Резултат:
+-----------------------------+---------------------+ | datetime2 | smalldatetime | |-----------------------------+---------------------| | 2025-05-21 10:15:30.5555555 | 2025-05-21 10:16:00 | +-----------------------------+---------------------+
Тук зададох малка дата променлива на същата стойност като datetime2 променлива. Това кара стойността да се преобразува в smalldatetime и след това можем да използваме SELECT
изявление, за да видите стойността на всяка променлива.
В този случай datetime2 променливата използва скала от 7, което означава, че има 7 знака след десетичната запетая. Smalldatetime стойност от друга страна, няма никакви десетични знаци. Освен това секундите му са настроени на нула, а минутите се закръгляват нагоре.
Това може да се очаква, тъй като в официалната документация на Microsoft се посочва, че smalldatetime
Времето на ‘s е базирано на 24-часов ден, със секунди винаги нула (:00) и без части от секунди
.
Така че можем да видим, че datetime2 type предоставя много по-точна и точна стойност за дата/час.
Разбира се, може да не са ви необходими всички тези части от секундите. Едно от добрите неща за datetime2 е, че можете да посочите колко (ако има такива) частични секунди искате.
Пример 2 – Използване на по-малко десетични знаци
В този пример намалявам datetime2 мащабиране до 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';
Резултат:
+---------------------+---------------------+ | datetime2 | smalldatetime | |---------------------+---------------------| | 2025-05-21 10:15:31 | 2025-05-21 10:16:00 | +---------------------+---------------------+
В този случай datetime2 стойността вече не включва дробна част. И двата типа вече споделят една и съща дължина на знаците (19 позиции).
Но все още има разлики.
datetime2 value уважава стойността на секундите, въпреки че в този случай нейните секунди са закръглени нагоре. Както бе споменато, smalldatetime Компонентът секунди на стойността винаги е настроен на нула и в този случай минутите му са закръглени нагоре.
Причината е datetime2 Компонентът секунди се закръглява нагоре, защото дробната част е 5 или по-висока. Ако намалим дробната част, не се извършва закръгляне:
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';
Резултат:
+---------------------+---------------------+ | datetime2 | smalldatetime | |---------------------+---------------------| | 2025-05-21 10:15:30 | 2025-05-21 10:16:00 | +---------------------+---------------------+
Въпреки това, малката дата минутите на стойността продължават да се закръглят нагоре.
Пример 3 – Задаване на стойности от низови литерали
В предишните примери, smalldateime стойността беше присвоена чрез задаване на същата стойност като datetime2 стойност. Когато направим това, SQL Server извършва имплицитно преобразуване, за да могат данните да „прилягат“ на новия тип данни.
Въпреки това, ако се опитаме да присвоим същия низов литерал на smalldatetime променлива, получаваме грешка:
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';
Резултат:
Msg 295, Level 16, State 3, Line 5 Conversion failed when converting character string to smalldatetime data type.
Това е така, защото малка дата приема само низови литерали, които имат 3 или по-малко дробни секунди.
Може да очаквате, че няма да приеме низови литерали с някои дробни секунди, като се има предвид, че не включва дробни секунди, но това не е така. Той с удоволствие приема 3 части от секунди, но не повече.
Така че, за да преодолеем този проблем, трябва да намалим дробната част до само 3 (или по-малко) знака след десетичната запетая.
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';
Резултат:
+---------------------+---------------------+ | datetime2 | smalldatetime | |---------------------+---------------------| | 2025-05-21 10:15:30 | 2025-05-21 10:16:00 | +---------------------+---------------------+
datetime2 типът няма това ограничение, дори когато се използва скала от 0.
Пример 4 – Размер за съхранение
Smalldatetime типът данни има фиксиран размер на съхранение от 4 байта. Това е едно от малкото предимства smalldatetime има над datetime2 .
datetime2 може да бъде 6, 7 или 8 байта, в зависимост от неговата точност. И така, datetime2 стойност винаги ще използва най-малко 2 байта повече място за съхранение от smalldatetime стойност.
Microsoft заявява, че datetime2 типът също използва 1 допълнителен байт, за да съхрани своята прецизност, в който случай ще използва поне 3 байта повече от smalldatetime .
Това обаче вероятно зависи от това дали го съхраняваме в таблица или в променлива и дали го преобразуваме в двоична константа или не.
Ето какво се случва, ако използваме DATALENGTH()
функция за връщане на броя байтове, използвани за всяка от нашите стойности:
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';
Резултат
+-------------+-----------------+ | datetime2 | smalldatetime | |-------------+-----------------| | 6 | 4 | +-------------+-----------------+
Но ако ги преобразуваме в varbinary , получаваме следното:
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';
Резултат
+-------------+-----------------+ | datetime2 | smalldatetime | |-------------+-----------------| | 7 | 4 | +-------------+-----------------+
И така, datetime2 използва допълнителен байт, когато се преобразува в varbinary . Много разработчици приемат, че конвертирането в varbinary е представителен за това как SQL Server всъщност съхранява стойности за дата и час.
Това обаче е само частично вярно. Въпреки че е вярно, че SQL Server съхранява своите стойности за дата и час в шестнадесетични, тази шестнадесетична стойност всъщност не включва точността. Това е така, защото точността е включена в дефиницията на колоната. Но когато преобразуваме в varbinary както направихме в предишния пример, точността се добавя преди и това добавя допълнителен байт.
Следващият пример демонстрира това. Показва, че когато данните се съхраняват в колона на база данни, получаваме дължина от 6 байта за datetime2 срещу 4 байта за smalldatetime .
Пример 5 – Размер на съхранение за съхранени данни
В този пример създавам база данни и използвам COL_LENGTH
за да върне дължината на всяка колона в байтове. След това вмъквам datetime2 и smalldatetime стойност в него и използвайте DBCC PAGE()
за да намерите дължината на действителните данни във файла на страницата. Това ни показва пространството за съхранение, което всеки тип данни използва, когато се съхранява в база данни.
Създайте база данни:
CREATE DATABASE CompareTypes;
Създайте таблица:
USE CompareTypes; CREATE TABLE Datetime2vsSmalldatetime ( TheDateTime2 datetime2(0), TheSmallDateTime smalldatetime );
В този случай създавам две колони – едната е datetime2(0) колона, а другата е smalldatetime колона.
Проверете дължината на колоната
Проверете дължината (в байтове) на всяка колона:
SELECT COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheDateTime2' ) AS 'datetime2', COL_LENGTH ( 'dbo.Datetime2vsSmalldatetime' , 'TheSmallDateTime' ) AS 'smalldatetime';
Резултат:
+-------------+-----------------+ | datetime2 | smalldatetime | |-------------+-----------------| | 6 | 4 | +-------------+-----------------+
И така, виждаме, че datetime2(0) колоната има дължина от 6 байта в сравнение с smalldatetime с дължина от 4 байта.
Вмъкване на данни
Сега нека разгледаме размера на съхранение на действителните стойности за дата и час, когато те се съхраняват в SQL Server. Можем да използваме DBCC PAGE()
за да проверите действителната страница във файла с данни.
Но първо трябва да вмъкнем данни в нашите колони.
Вмъкване на данни:
DECLARE @thedatetime2 datetime2 = '2025-05-21 10:15:30'; INSERT INTO Datetime2vsSmalldatetime ( TheSmallDateTime, TheDateTime2 ) SELECT @thedatetime2, @thedatetime2;
Изберете данните (само за да ги проверите):
SELECT * FROM Datetime2vsSmalldatetime;
Резултат:
+---------------------+---------------------+ | TheDateTime2 | TheSmallDateTime | |---------------------+---------------------| | 2025-05-21 10:15:30 | 2025-05-21 10:16:00 | +---------------------+---------------------+
Използване на DBCC PAGE()
Ето къде използваме DBCC PAGE()
за да проверите действителната страница във файла с данни.
Първо, ще използваме DBCC IND()
за да намерите PagePID:
DBCC IND('CompareTypes', 'dbo.Datetime2vsSmalldatetime', 0);
Резултат (с помощта на вертикален изход):
-[ 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
Това връща два записа. Интересуваме се от PageType от 1 (2-ри запис). Искаме PagePID от този запис. В този случай PagePID е 344 .
Сега можем да вземем този PagePID и да го използваме в следното:
DBCC TRACEON(3604, -1); DBCC PAGE(CompareTypes, 1, 344, 3);
Това произвежда много данни, но ние се интересуваме основно от следната част:
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
Това показва, че smalldatetime има дължина от 4 байта и datetime2(0) има 6 байта, когато се съхранява в база данни.
Така че в този случай има само 2 байта разлика, но datetime2(0) е по-точен и се придържа към стандартите ANSI и ISO 8601.