2009-07-24 2 views
431

Какой метод обеспечивает лучшую производительность при удалении временной части из поля datetime в SQL Server?Лучший подход к удалению временной части datetime в SQL Server

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0) 

или

b) select cast(convert(char(11), getdate(), 113) as datetime) 

Второй метод не послать еще несколько байт в любом случае, но это не может быть столь же важным, как скорость преобразования.

Оба варианта кажутся очень быстрыми, но может быть разница в скорости при работе с сотнями тысяч или более строк?

Кроме того, возможно ли, что есть еще лучшие методы, чтобы избавиться от временной части datetime в SQL?

+1

Я пробовал это на миллион записей в одной из моих производственных таблиц, и я не мог точно узнать о производительности в любом случае. Однако оба метода вернули то же количество данных. –

+0

Обработка строк - это больше процессорного процесса. DATEADD и DATEDIFF предназначены для наилучшего использования представления хранилища, используемого SQL Server. – MatBailie

+8

На 18 000 000 строк это то, что я нашел (SQL Server 2008): метод b примерно на 24% медленнее, чем метод a. CAST (FLOOR (CAST (getdate() AS FLOAT)) AS DATETIME) на 3,5% медленнее, чем метод a. Метод a, по-видимому, является победителем в отношении производительности. Спасибо всем за отличные ответы. –

ответ

463

Строго говоря, метод a является наименее ресурсоемкий:

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0) 

Проверенных менее ресурсоемким на тот же общую длительность миллиона строк по какому-то один с слишком много времени на их руках: Most efficient way in SQL Server to get date from date+time?

I в аналогичном тесте аналогичный результат был аналогичным.

Я предпочитаю DATEADD/DATEDIFF, потому что:

  • VARCHAR подлежит язык/DateFormat вопросы
    Пример: Why is my CASE expression non-deterministic?
  • поплавок опирается на внутреннюю память
  • простирается отработать первый день месяц, завтра и т. д., заменив базу «0»

Редактировать, окт 2011

Для SQL Server 2008+ вы можете CAST до date. Или просто используйте date, так что нет времени для удаления.

Редактировать, январь 2012

Рабочий пример того, как гибкая это: Need to calculate by rounded time or date figure in sql server

Редактировать, май 2012

Не используйте это в ИНЕКЕ и тому подобное, не думая : добавление функции или CAST в столбец делает недействительным использование индекса. См номер 2 здесь: http://www.simple-talk.com/sql/t-sql-programming/ten-common-sql-programming-mistakes/

Теперь это действительно есть пример позже SQL версии Оптимизатор сервера управления CAST на сегодняшний день правильно, но обычно это будет плохая идея ...

+3

@ Давид Сопко за октябрь 2011 г. редактирование, а затем код: select cast (GETDATE() в качестве даты) –

+1

Для более поздних версий SQL, используя дату вместо datetime, избегает необходимости иметь дело с часами. Используйте следующий пример: declare noTime date = getdate(), withTime datetime = getdate() select @ noTime, @ withTime – ozkary

+0

отличная дата отлично подходит, если вам просто нужна дата. Однако часто вам нужна текущая дата в полночь, поэтому вы можете сделать некоторые дальнейшие манипуляции с датами. время данных «DATE» является отвратительным для того, что он позволит вам делать с такими вещами, как dateadd, датированный и взаимодействующий с другими типами данных даты и времени. Для этих случаев подход 'DATEADD()' царит. – Xedni

2

Полоса времени на вставках/обновлениях в первую очередь. Что касается преобразования на лету, ничто не может бить определенную пользователем функцию maintanability-накрест:

select date_only(dd) 

Реализация date_only может быть все что угодно - теперь это абстрагируются и вызывающий код гораздо гораздо чище.

+0

Однажды я разработал триггер, чтобы очистить время от выбранных столбцов. Если данные не могут быть плохими, вам не нужно их чистить. –

+2

Существует недостаток подхода UDF, они не являются SARGable. Если используется в предложениях JOINs или WHERE, оптимизатор не может использовать INDEX для повышения производительности. Однако использование подхода DATEADD/DATEDIFF является SARGable и сможет извлечь выгоду из INDEX. (По-видимому, метод FLOAT тоже SARGable) – MatBailie

+1

@MatBailie Прошу отличить! UDF, безусловно, не SARGable, но ни Dateadd, ни Convert для float! 'WHERE DateAdd (DateDiff (Column)) = @ DateValue' не будет использовать индекс. С другой стороны, 'WHERE Column> = dbo.UDF (@DateValue) AND Column ErikE

19
SELECT CAST(FLOOR(CAST(getdate() AS FLOAT)) AS DATETIME) 
+0

См. Ответ GBN, многие исследовали это. DATETIME не сохраняются как поплавки, и поэтому использование DATEADD/DATEDIFF позволяет избежать математического манипулирования, необходимо CAST между типами. – MatBailie

+0

Я могу согласиться с тем, что вы можете отказаться от приведения из DATETIME в FLOAT по той причине, которую вы описываете, но в этом случае не подразумевается неявное преобразование с нуля в опцию OP (a) также проблема? Хм ... Полагаю, в этом случае это не FLOAT, и сервер, вероятно, достаточно умный, чтобы отбросить информацию о времени. Хорошо, я соглашаюсь :-) –

+0

0 действительно является неявным преобразованием из числового типа (INT, который я предполагал) в DATETIME. Однако, поскольку это постоянное выражение, оптимизатор может это сделать во время компиляции хранимых процедур, и только один раз нужно выполнить его для динамического выполнения SQL. Короче говоря, для этого есть накладные расходы на один раз, запрос на основе FLOAT имеет равные накладные расходы для каждой строки. – MatBailie

0

Я, лично, почти всегда использую User Defined functions для этого, если имеет дело с SQL Server 2005 (или более низкой версией), однако, следует отметить, что есть определенные недостатки в использовании UDF, особенно если применять их в WHERE (см. ниже и комментарии к этому ответу для получения дополнительной информации). Если используется SQL Server 2008 (или выше) - см. Ниже.

На самом деле, для большинства баз данных, которые я создаю, я добавляю эти UDF прямо в начале, так как я знаю, что есть шанс на 99%, который мне понадобится рано или поздно.

Я создаю один для «только даты» & «только время» (хотя «дата только» одна из них является наиболее используемой из двух).

Вот некоторые ссылки на различных даты, связанные с ОДС:

Essential SQL Server Date, Time and DateTime Functions
Get Date Only Function

Это последнее звено не показывает не менее 3-х различных способов для получения даты только часть поля даты и времени и упоминает некоторые плюсы и минусы каждого подхода.

При использовании UDF следует отметить, что вы должны стараться избегать использования UDF как части предложения WHERE в запросе, поскольку это значительно затруднит выполнение запроса. Основная причина этого заключается в том, что использование UDF в предложении WHERE делает это предложение как non-sargable, что означает, что SQL Server больше не может использовать индекс с этим предложением, чтобы повысить скорость выполнения запроса. Что касается моего собственного использования UDF, я часто использую столбец «raw» date в предложении WHERE, но применяю UDF к столбцу SELECTed. Таким образом, UDF применяется только к фильтруемому набору результатов, а не к каждой строке таблицы как части фильтра.

Конечно, абсолютное лучший подход заключается в использовании SQL Server 2008 (или выше) и разделить ваши dates and times, как ядро ​​базы данных SQL Server, затем изначально предоставления отдельных компонентах даты и времени, а может эффективно запрашивать их самостоятельно, без необходимости использования UDF или другого механизма для извлечения либо даты, либо временной части из составного типа datetime.

+0

Использование UDF может быть хорошим в некоторых ситуациях (например, при чистке параметров). Но в большинстве ситуаций это * ужасное * решение - запуск UDF один раз для каждой строки - это способ просто * убить * производительность запроса, без какой-либо необходимости! – ErikE

+0

@ ErikE - Я не согласен, Эрик, UDF - это убийцы производительности, поэтому я говорю, что если вы можете использовать SQL Server 2008 или выше и использовать встроенный тип данных, который сделает это для вас, это будет лучшим (как с точки зрения достижения требуемых, так и с точки зрения производительности). Если вы застряли в старой версии SQL Server, которая не поддерживает это, вы откажетесь от _something_, чтобы достичь своих требований. – CraigTP

+0

Правда. Было бы неплохо, если бы механизм базы данных дал нам что-то, что было SARGable, но проще выразить. Тем временем, если вы ищете какое-либо значение в любое время в течение целого дня, это по-прежнему лучшее решение (для хотя бы более старых версий SQL): 'WHERE DateColumn> = {TimeTruncatingExpression} (@ DateValue) AND DateColumn <{TimeTruncatingExpression} (@ DateValue + 1) '. Я чувствовал, что должен был что-то сказать, так как вы сказали: «Я почти всегда использую UDF», не объяснял никаких недостатков и способ сделать запрос SARGable только для даты. – ErikE

2

Смотрите этот вопрос:
How can I truncate a datetime in SQL Server?

Что бы вы ни делали, не использовать метод строки. Это самый худший способ сделать это.

+0

Спасибо, я подумал, что это нужно было спросить прежде. Странно, хотя в моих экспериментах указывалось, что метод float на SQL Server 2008 медленнее на 3,5%, чем метод dateadd (dd, 0, датичный (dd, 0, getDate())). Я выполнял свои тесты много раз для каждого метода, и сервер базы данных в то время не использовался ни для чего другого. –

+0

Давайте просто скажем, что я скептически отношусь к бенчмаркам, сделанным всеми, кто не продемонстрировал, что они регулярно проводят тесты и очень научным образом в рамках своей работы. Даже показатель Томаса в ссылке gbn имеет некоторые очевидные проблемы, когда вы смотрите на него. Это не делает его неправильным, а не окончательным. Метод литья/пол/литой был принят самый быстрый путь в течение очень долгого времени, и я подозреваю, что это было когда-то бесспорная истина. Тем не менее, я начинаю пересматривать это; особенно для SQL Server 2008, где это абсолютно не нужно. –

+1

Строковый метод чрезвычайно прост в использовании, для чтения и для запоминания. Это очень важные факторы, которые, я думаю, вы недооцениваете! – Ben

8

Вот еще один ответ, из другого duplicate question:

SELECT CAST(CAST(getutcdate() - 0.50000004 AS int) AS datetime) 

Этот волшебный метод номер немного выполняет быстрее, чем метод DATEADD.(Это выглядит как ~ 10%)

ЦП Время на несколько раундов миллиона записей:

DATEADD MAGIC FLOAT 
500  453 
453  360 
375  375 
406  360 

Но обратите внимание, что эти цифры, возможно, не имеет значения, потому что они уже очень быстро. Если бы у меня не было записей на 100 000 и более, я бы даже не мог заставить процессорное время читать выше нуля.

Учитывая тот факт, что DateAdd предназначен для этой цели и является более надежным, я бы сказал, что используйте DateAdd.

+1

Это ужасно. Я никогда не ставил бы свои данные в такой ситуации. Кто знает, верно ли это для * все * datetimes, а не только те, которые вы тестировали. – usr

+0

@usr О, это правильно, это просто волшебное число и не должно использоваться по этой причине. Если вы хотите проверить его правильность, просто заполните все возможные даты за один день в таблице и проверьте результаты! Также [см. Этот пост] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql-server/3696991 # 3696991) для получения дополнительной информации. – ErikE

+0

@ErikE хороший пункт. Ваш ответ дает возможность использовать '' 12: 00: 00.003'', который, я думаю, намного лучше. – usr

2

Уже ответил, но плохо бросить это там тоже ... этот suposedly также преформ, но она работает, выбрасывая десятичной (в котором хранится время) с плавающей точкой и возвращает только целую часть (которая является дата)

CAST(
FLOOR(CAST(GETDATE() AS FLOAT)) 
AS DATETIME 
) 

второй раз, когда я нашел это решение ... i grabbed this code off

+1

Преобразование в плавание [небезопасно] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql- сервер/3696991 # 3696991). – ErikE

0

Если это возможно, для специальных вещей, как это, я хотел бы использовать функции CLR.

В этом случае:

[Microsoft.SqlServer.Server.SqlFunction] 
    public static SqlDateTime DateOnly(SqlDateTime input) 
    { 
     if (!input.IsNull) 
     { 
      SqlDateTime dt = new SqlDateTime(input.Value.Year, input.Value.Month, input.Value.Day, 0, 0, 0); 

      return dt; 
     } 
     else 
      return SqlDateTime.Null; 
    } 
2
CAST(round(cast(getdate()as real),0,1) AS datetime) 

Этот метод не использует функцию строки. Date - это в основном настоящий тип данных с цифрами до десятичной дроби - это доля дня.

это, я думаю, будет быстрее, чем много.

+1

Кастинг как плавающий [небезопасен] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql- сервер/3696991 # 3696991). – ErikE

1

Я думаю, что вы имеете в виду cast(floor(cast(getdate()as float))as datetime)

реален только 32 бита, и может потерять некоторую информацию

Это быстрый cast(cast(getdate()+x-0.5 as int)as datetime)

... хотя только около 10% быстрее (about 0.49 microseconds CPU vs. 0.58)

было рекомендовано, и требуется то же самое время в моем тесте: DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

В SQL 2008 функция SQL CLR примерно в 5 раз быстрее, чем использование функции SQL, было бы в 1,35 микросекундах против 6,5 микросекунд, что указывает на гораздо более низкую накладную функциональность для функции SQL CLR по сравнению с простым SQL UDF ,

В SQL Server 2005, функция SQL CLR в 16 раз быстрее, в ходе тестирования, по сравнению с этой медленной функции:

create function dateonly ( @dt datetime) 
returns datetime 
as 
begin 
return cast(floor(cast(@dt as float))as int) 
end 
29

Of-конечно, это старая нить, но чтобы сделать его полным.

С SQL 2008 вы можете использовать DATE тип данных, так что вы можете просто сделать:

SELECT CONVERT(DATE,GETDATE()) 
+8

В принятом ответе упоминается этот вариант. –

2

Для меня ниже код всегда победитель:

SELECT CONVERT(DATETIME, FLOOR(CONVERT(FLOAT,GETDATE()))); 
+1

По существу такие же, как [предложение @Gary McGill] (http://stackoverflow.com/questions/1177449/best-approach-to-remove-time-part-of-datetime-in-sql-server#1177479). –

+1

Кастинг как плавающий [небезопасен] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql- сервер/3696991 # 3696991). – ErikE

4
SELECT CAST(CAST(GETDATE() AS DATE) AS DATETIME) 
+3

Действительный вариант, да. Тем не менее, предлагалось не раз в этой теме. –

1

Я думаю, что если вы строго придерживайтесь TSQL, что это самый быстрый способ усечения времени:

select convert(datetime,convert(int,convert(float,[Modified]))) 

Я нашел этот метод усечения примерно на 5% быстрее, чем метод DateAdd. И это может быть легко изменен, чтобы округлить до ближайшего дня, как это:

select convert(datetime,ROUND(convert(float,[Modified]),0)) 
+0

Преобразование в поплавок [небезопасно] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql- сервер/3696991 # 3696991). – ErikE

47

В SQL Server 2008, вы можете использовать:

CONVERT(DATE, getdate(), 101) 
+7

Третий аргумент не имеет никакого отношения к результату при преобразовании из 'datetime' в' date', поэтому ваше решение эффективно сводится к простому 'CONVERT (DATE, getdate())', который уже был предложен более чем один раз. –

-2

Я хотел бы использовать:

CAST 
(
CAST(YEAR(DATEFIELD) as varchar(4)) + '/' CAST(MM(DATEFIELD) as varchar(2)) + '/' CAST(DD(DATEFIELD) as varchar(2)) as datetime 
) 

Таким образом, эффективно создавая новое поле из поля даты, которое у вас уже есть.

+1

Зачем вам это делать? Считаете ли вы, что извлечение битов из значения «datetime», преобразование их в строки, объединение этих соединений и окончательное преобразование результата обратно в «datetime» лучше, чем, например, выполнение прямых вычислений в исходном 'datetime' (метод DATEADD' /' DATEDIFF')? –

+0

Кроме того, что такое 'MM' и' DD'? В SQL Server таких функций нет. –

2

ОЗНАКОМЬТЕСЬ!

Способ a) и b) НЕ всегда имеет одинаковый выход!

select DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0) 

Выход: 2014-01-01 00:00:00.000

select cast(convert(char(11), '2013-12-31 23:59:59.999', 113) as datetime) 

Выход: 2013-12-31 00:00:00.000

(Проверено на MS SQL Server 2005 и 2008 R2)

EDIT: В соответствии с комментарием Адама, это не может, если произойдет вы читаете значение даты из таблицы, но это может произойти, если вы укажете свое значение даты как литерал (пример: как параметр вызова хранимой процедуры ed через ADO.NET).

+0

.999 не может храниться в SQL Server в столбце DATETIME. Самый высокий доступный .997 От: http://msdn.microsoft.com/en-us/library/ms187819.aspx вы увидите, что значения округлены, чтобы иметь тысячное место в 0, 3 или 7. OP не увидит значение вашего теста в своих таблицах. –

+0

Вы правы. Я не собирался публиковать это как ответ на вопрос OP, но в качестве комментария для других, но я только имел 11 точек репутации и 15 для комментариев. – broslav

+0

В вашем первом фрагменте строковая константа неявно преобразуется в datetime, а во втором она остается строкой (а 113 просто игнорируется). –

2

выберите CONVERT (символ (10), GetDate(), 126)

+0

В чем принципиальное отличие вашего предложения от метода, упомянутого в ответе @ broslav, или от метода, который был определен как самый медленный ** в [этой теме] (http://stackoverflow.com/questions/133081/most- Эффективный способ в sql-server-to-get-date-from-datetime «Самый эффективный способ в SQL Server для получения даты с даты + времени?») (та же ссылка, что и в принятом ответе)? –

1

Как насчет select cast(cast my_datetime_field as date) as datetime)? Это приводит к той же дате с временем, установленным в 00:00, но избегает любого преобразования в текст, а также избегает любого явного округления чисел.

+1

Alread, предлагаемый в этих ответах: http://stackoverflow.com/a/17449578/569436 http://stackoverflow.com/a/10451347/569436 http://stackoverflow.com/a/19541838/569436 http: // stackoverflow.com/a/1177529/569436 –

+0

Они не то же самое.В других ответах предлагается указать его на дату * без компонента времени * и оставить это так. Моя публикация устанавливает его в datetime со временем в полночь. Существует большая разница; попробуйте экспортировать в MS Excel, и вы увидите, что он обрабатывает дату и время намного лучше даты. –

+0

Первый точно такой же. –

11

В SQL Server 2008 существует тип данных DATE (также тип данных TIME).

CAST(GetDate() as DATE) 

или

declare @Dt as DATE = GetDate() 
+0

Это то, что я использовал, и он работал хорошо. Похоже, самый простой ответ. Любые недостатки над использованием в сочетании с CONVERT? – joelmdev

1

Только в случае, если кто-то ищет здесь для версии Sybase, так как некоторые из версий выше не работает

CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME) 
  • Испытано в I SQL v11 работает на Adaptive Server 15.7
+0

Это лучше подходит для редактирования принятого ответа. С 20 другими ответами это будет похоронено и почти невозможно. Также в принятом ответе упоминается использование 'cast': _For SQL Server 2008+, вы можете CAST на сегодняшний день. Или просто используйте дату, поэтому нет времени для удаления ._ – EWit

+0

Лучше всего разместить это в качестве ответа на эквивалентный вопрос Sybase. Если такого вопроса нет, вы можете создать его (и сами ответьте). –

+0

Кроме того, бессмысленно указывать третий параметр CONVERT, когда вы конвертируете 'datetime' в' date': ни один из них не имеет встроенного формата. –

1

Здесь я сделал функцию, чтобы удалить некоторые части datetime для SQL Server. Использование:

  • Первый параметр - это время, которое нужно удалить.
  • Второй параграф:
    • s: раунды до секунд; удаляет миллисекунды
    • m: раунды до минут; удаляет секунды и миллисекунды
    • h: раунды до часов; удаляет минуты, секунды и миллисекунды.
    • d: раунды до дней; удаляет часы, минуты, секунды и миллисекунды.
  • возвращает новый объект DateTime

create function dbo.uf_RoundDateTime(@dt as datetime, @part as char) returns datetime as begin if CHARINDEX(@part, 'smhd',0) = 0 return @dt; return cast( Case @part when 's' then convert(varchar(19), @dt, 126) when 'm' then convert(varchar(17), @dt, 126) + '00' when 'h' then convert(varchar(14), @dt, 126) + '00:00' when 'd' then convert(varchar(14), @dt, 112) end as datetime) end

+1

http://stackoverflow.com/a/1177683 –

+0

Спасибо Andriy! Я не знал, что моя рекомендация не так эффективна. По крайней мере, это работает, но вы правы. –

1

мне очень нравится:

[date] = CONVERT(VARCHAR(10), GETDATE(), 120) 

120 код формата будет принуждать дату в ISO 8601 стандарт:

'YYYY-MM-DD' or '2017-01-09' 

Супер прост в использовании в dplyr (R) и пандах (Python)!

Смежные вопросы