Найефективніший спосіб у SQL Server отримати дату з дати + часу?


74

У MS SQL 2000 та 2005, давши час, наприклад „2008-09-25 12:34:56”, який найефективніший спосіб отримати час, що містить лише „2008-09-25”?

Дублюється тут .


4
Мені дуже приємно, що перші три відповіді різні, це означає, що це не дурне питання! :)
Matt Howells

Відповіді:


114

Я повинен визнати, що я раніше не бачив перетворення підлоги з поплавком, показано Меттом. Мені довелося це перевірити.

Я протестував чистий вибір (який поверне дату і час, а не те, що ми хочемо), правильне рішення тут (floor-float), загальноприйнятий "наївний", згаданий тут (stringconvert) і той, про який я згадав тут використання (як я думав, це було найшвидше).

Я перевірив запити на тестовому сервері MS SQL Server 2005, що працює на сервері Win 2003 SP2 із процесором Xeon 3 ГГц, що працює на максимальній пам’яті (32 біти, тобто близько 3,5 Гб). Зараз я вночі, тому машина працює на холостому ходу майже без навантаження. Я все отримав для себе.

Ось журнал мого тестового запуску, який вибирається з великої таблиці, що містить мітки часу, що змінюються до рівня мілісекунд. Цей конкретний набір даних включає дати від 2,5 років. Сама таблиця має понад 130 мільйонів рядків, тому я обмежуюся лише мільйоном верхніх.

SELECT TOP 1000000 CRETS FROM tblMeasureLogv2 
SELECT TOP 1000000 CAST(FLOOR(CAST(CRETS AS FLOAT)) AS DATETIME) FROM tblMeasureLogv2
SELECT TOP 1000000 CONVERT(DATETIME, CONVERT(VARCHAR(10), CRETS, 120) , 120) FROM tblMeasureLogv2 
SELECT TOP 1000000 DATEADD(DAY, DATEDIFF(DAY, 0, CRETS), 0) FROM tblMeasureLogv2

Час аналізу та компіляції SQL Server: час процесора = 0 мс, минулий час = 1 мс.

(1000000 рядків (рядків)) Таблиця 'tblMeasureLogv2'. Кількість сканування 1, логічне читання 4752, фізичне читання 0, читання вперед читає 0, лобічне логічне читання 0, лобове фізичне читання 0, лобове читання 0 читає

Час виконання SQL Server: час процесора = 422 мс, минулий час = 33803 мс.

(1000000 рядків (рядків)) Таблиця 'tblMeasureLogv2'. Кількість сканування 1, логічне читання 4752, фізичне читання 0, читання вперед читає 0, лобічне логічне читання 0, лобове фізичне читання 0, лобове читання 0 читає

Час виконання SQL Server: час процесора = 625 мс, минулий час = 33545 мс.

(1000000 рядків (рядків)) Таблиця 'tblMeasureLogv2'. Кількість сканування 1, логічне читання 4752, фізичне читання 0, читання вперед читає 0, лобічне логічне читання 0, лобове фізичне читання 0, лобове читання 0 читає

Час виконання SQL Server: час процесора = 1953 мс, минулий час = 33843 мс.

(1000000 рядків (рядків)) Таблиця 'tblMeasureLogv2'. Кількість сканування 1, логічне читання 4752, фізичне читання 0, читання вперед читає 0, лобічне логічне читання 0, лобове фізичне читання 0, лобове читання 0 читає

Час виконання SQL Server: час процесора = 531 мс, минулий час = 33440 мс. Час аналізу та компіляції SQL Server: час процесора = 0 мс, минулий час = 1 мс.

Час виконання SQL Server: час процесора = 0 мс, минулий час = 1 мс.

Що ми тут бачимо?

Зупинимося на часі процесора (ми розглядаємо перетворення), і ми бачимо, що маємо такі цифри:

Pure-Select:  422
Floor-cast:   625
String-conv: 1953
DateAdd:      531  

З цього мені здається, що DateAdd (принаймні в цьому конкретному випадку) трохи швидший, ніж метод лиття підлоги.

Перш ніж ви поїдете туди, я провів цей тест кілька разів, змінивши порядок запитів, однакові результати.

Це щось дивне на моєму сервері, чи що?


5
Це зовсім не дивно. По-перше, на мій погляд, перетворення на плаваючу є поганою практикою, оскільки перетворення в обидва кінці на дату і час не є надійним. По-друге, перегляньте цю публікацію з додатковим тестуванням продуктивності різних методів .
ErikE

24
Select DateAdd(Day, DateDiff(Day, 0, GetDate()), 0)

DateDiff (Day, 0, GetDate ()) те саме, що DateDiff (Day, '1900-01-01', GetDate ())

Оскільки DateDiff повертає ціле число, ви отримаєте кількість днів, що минули з 1 січня 1900 року. Потім це ціле число днів додається до 1 січня 1900 року. Чистий ефект видаляє часову складову.

Слід також зазначити, що цей метод працює для будь-якої частини дати / часу (наприклад, року, кварталу, місяця, дня, години, хвилини та секунди).

Select  DateAdd(Year, DateDiff(Year, 0, GetDate()), 0)
Select  DateAdd(Quarter, DateDiff(Quarter, 0, GetDate()), 0)
Select  DateAdd(Month, DateDiff(Month, 0, GetDate()), 0)
Select  DateAdd(Day, DateDiff(Day, 0, GetDate()), 0)
Select  DateAdd(Hour, DateDiff(Hour, 0, GetDate()), 0)
Select  DateAdd(Second, DateDiff(Second, '20000101', GetDate()), '20000101')

Останній, на секунди, вимагає особливого поводження. Якщо ви використовуєте 1 січня 1900 року, ви отримаєте повідомлення про помилку.

Різниця двох стовпців дати та часу спричинила переповнення під час виконання.

Ви можете обійти цю помилку, використовуючи іншу контрольну дату (наприклад, 1 січня 2000 р.).


Ви впевнені, що це найефективніший метод?
Matt Howells,

Мої тести показують, що метод Метта трохи швидший. Я також вважаю це більш читабельним.
Даррел Міллер

2
@Dar На мій погляд, перетворення на float є поганою практикою, оскільки перетворення в обидва кінці на дату не є надійним. Будь ласка, перегляньте цю публікацію, щоб дізнатися більше .
ErikE

12
select cast(floor(cast(@datetime as float)) as datetime)

Працює, оскільки приведення дати до плаваючого дає кількість днів (включаючи частки дня) з 1 січня 1900 року. Підлогою він видаляє дробові дні і залишає кількість цілих днів, які потім можна повернути до дати.


Я використовую той самий метод, і він чудово працює. Наскільки я пам’ятаю, DATETIME фактично фізично зберігається як FLOAT, тому цей метод є дуже ефективним. Також я пам’ятаю, десь читав, що він використовувався внутрішньо компанією Microsoft. Не впевнений, чи це все ще стосується SS2008
Крістоф

4
SQL2008 має набагато вдосконалені (і вкрай необхідні) функції обробки дат. Це було б так просто, як приведення дати та часу до нового типу дати.
Matt Howells,

3
@kristof datetime фізично не зберігається як плаваючий. Це два 4-байтові цілі числа, причому перше число днів з 1900-1-1, а друге - число 1/300-го другого секунди з півночі. Нарешті, на мій погляд, перетворення на float є поганою практикою, оскільки перетворення в обидва кінці на дату і час не є надійним. Будь ласка, перегляньте цю публікацію, щоб дізнатися більше .
ErikE

Здається, він зберігається як фіксована точка, а не як плаваюча точка всередині.
Інженер, що

7

у SQL Server 2012 використання

select cast(getdate() as date)

2
Хоча це правда, питання стосується старих версій MS SQL 2000 та 2005, у яких відсутній dateтип даних.
jpw


0

Щоб отримати РРРР-ММ-ДД, використовуйте:

select convert(varchar(10), getdate(), 120)

Змінити: На жаль, він хоче DateTime замість рядка. Еквівалент TRUNC () в Oracle. Ви можете взяти те, що я опублікував, і повернути назад до DateTime:

select convert(datetime, convert(varchar(10), getdate(), 120) , 120)

2
Зверніть увагу, що перетворення на varchar відбувається повільніше. Детальніше див. У цій публікації .
ErikE




-1

Що про SELECT CAST(CASt(GETDATE() AS int) AS DATETIME)??


1
Тому що це не те, про що просив ОП ..?
mbinette

Я спробував; він може округлити UP, що дає неправильний результат.
Піт Елвін,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.