Всем привет,
В течение некоторого времени у меня была некоторая путаница с использованием T-SQL для типа DateTime SQL. По сути, я хочу взять значение DateTime, скажем, 2008-12-1 14:30:12, и сделать его 2008-12-1 00:00:00. Многие запросы, которые мы выполняем для отчетов, используют значение даты в предложении WHERE, но у меня либо есть значение даты начала и окончания дня и я использую BETWEEN, либо я нахожу какой-то другой метод.
В настоящее время я использую следующее:
WHERE CAST(CONVERT(VARCHAR, [tstamp], 102) AS DATETIME) = @dateParam
Однако это кажется немного неуклюжим. Я надеялся, что будет что-то более простое, например
CAST([tstamp] AS DATE)
В некоторых местах в Интернете рекомендуют использовать функцию DATEPART (), но тогда я получаю что-то вроде этого:
WHERE DATEPART(year, [tstamp]) = DATEPART(year, @dateParam)
AND DATEPART(month, [tstamp]) = DATEPART(month, @dateParam)
AND DATEPART(day, [tstamp]) = DATEPART(day, @dateParam)
Может, меня слишком беспокоит что-то маленькое, и если да, то дайте мне знать Я просто хочу убедиться, что материал, который я пишу, максимально эффективен. Я хочу устранить все слабые звенья.
Какие-либо предложения?
Спасибо,
C
Спасибо всем за отличный отзыв. Очень много полезной информации. Я собираюсь изменить наши функции, чтобы исключить функцию в левой части оператора. Хотя большинство наших столбцов даты не используют индексы, это, вероятно, по-прежнему лучше.


DATEADD(d, 0, DATEDIFF(d, 0, [tstamp]))
Обновлено: хотя это удалит временную часть вашего datetime, это также сделает условие без SARGable. Если это важно для этого запроса, более уместно индексированное представление или предложение между.
Вот запрос, который вернет все результаты за определенный период дней.
DECLARE @startDate DATETIME
DECLARE @endDate DATETIME
SET @startDate = DATEADD(day, -30, GETDATE())
SET @endDate = GETDATE()
SELECT *
FROM table
WHERE dateColumn >= DATEADD(day, DATEDIFF(day, 0, @startDate), 0)
AND dateColumn < DATEADD(day, 1, DATEDIFF(day, 0, @endDate))
использование трюка -2 MS - плохая практика, потому что он зависит от разрешения типа данных datetime. У людей есть попал в неприятные ситуации из-за этого. Вместо этого прекратите использование BETWEEN и используйте> = и <.
Да, T-SQL временами может казаться чрезвычайно примитивным, и именно такие вещи часто заставляют меня выполнять большую часть моей логики на моем выбранном языке (например, C#).
Однако, когда вам абсолютно необходимо сделать некоторые из этих вещей в SQL по соображениям производительности, лучше всего создать функции для размещения этих «алгоритмов».
Взгляните на эту статью. Он предлагает довольно много удобных функций SQL в этом направлении, которые, я думаю, вам помогут.
http://weblogs.sqlteam.com/jeffs/archive/2007/01/02/56079.aspx
Использование функции для значения столбца снижает производительность. Обновите свой ответ, чтобы прояснить, что UDF следует использовать для скалярного значения даты, а затем столбец по сравнению с диапазоном дат в один день.
это очень плохо для производительности, взгляните на Только в базе данных можно получить улучшение на 1000% +, изменив несколько строк кода
функции в левой части оператора плохие
вот что тебе нужно сделать
declare @d datetime
select @d = '2008-12-1 14:30:12'
where tstamp >= dateadd(dd, datediff(dd, 0, @d)+0, 0)
and tstamp < dateadd(dd, datediff(dd, 0, @d)+1, 0)
Запустите это, чтобы увидеть, что он делает
select dateadd(dd, datediff(dd, 0, getdate())+1, 0)
select dateadd(dd, datediff(dd, 0, getdate())+0, 0)
Если вы используете SQL Server 2008, он уже встроен в него, см. Это в книги онлайн
CAST(GETDATE() AS date)
Хорошо знать! Пора. :)
Без шуток :-) Здесь много нового с датами, похоже, у них есть временной интервал, более точное datetime с настраиваемой точностью и т.д ... конечно, им пришлось пойти и назвать новое время даты ... datetime2 ... тьфу .
Именно то, что мне нужно. Я перебрал множество других запутанных решений, прежде чем нашел ваше. Большое спасибо. +1
WHERE DATEDIFF(day, tstamp, @dateParam) = 0
Это должно помочь вам, если вам наплевать на время.
Это ответ на мета-вопрос о сравнении дат двух значений, когда вас не волнует время.
Проблема в том, что он не может использовать индекс.
Этот №1 не может использовать индекс, как сказал @Rob, а №2 даже без индекса заставляет вычислять каждую строку в таблице, а не только один раз, когда выражение находится справа, как в лучшем решении SQLMenace.
Функции Date, опубликованные другими, являются наиболее правильным способом справиться с этим.
Однако забавно, что вы упомянули термин «пол», потому что есть небольшая хитрость, которая работает несколько быстрее:
CAST(FLOOR(CAST(@dateParam AS float)) AS DateTime)
На мой взгляд, преобразование в float - плохая практика, потому что преобразование туда и обратно в datetime ненадежно. См. этот пост для более подробной информации. Преобразование в float также не так быстро, как решение DateDiff.
@Emtucifor - это старый пост. Обновленная информация здесь: stackoverflow.com/questions/923295/… и здесь: stackoverflow.com/questions/1427469/…
Спасибо, Джоэл, посмотрю твои ссылки. Я просто хотел обновить старые сообщения, чтобы никого не ввести в заблуждение.
Хорошо ... похоже, мне нужно запустить тесты скорости в SQL 2000. Я постараюсь сделать это в ближайшее время. Между тем, я все еще с подозрением отношусь к этому, потому что двусторонние преобразования datetime в float и обратно не сохраняют исходное значение. И для записи, самый быстрый способ (по крайней мере, в SQL 2008, запрет на преобразование в тип данных Date) - это не этот метод, а Convert(datetime, Convert(int, @dateParam - 0.50000004)).
FWIW, я много лет занимаюсь тем же, что и ты
CAST(CONVERT(VARCHAR, [tstamp], 102) AS DATETIME) = @dateParam
Мне кажется, что это один из лучших способов сократить время с точки зрения гибкости, скорости и удобства. (Извините). Некоторые предлагаемые функции UDF могут быть полезны, но UDF могут работать медленно с большими наборами результатов.
Проблема в том, что он не может использовать индекс в столбце [tstamp].
Другая проблема заключается в том, что манипуляции со строками для дат SQL являются обычным антипаттерном. stackoverflow.com/questions/346659/…
Преобразование в varchar также происходит медленнее. См. этот пост для более подробной информации.
Будьте осторожны, если вы используете что-либо, кроме длинных строк WHERE CAST(CONVERT(VARCHAR, [tstamp], 102) AS DATETIME) = @dateParam, это заставит сканировать таблицу, и для этой части не будут использоваться индексы.
Более простой способ сделать это - определить вычисляемый столбец
create table #t (
d datetime,
d2 as
cast (datepart(year,d) as varchar(4)) + '-' +
right('0' + cast (datepart(month,d) as varchar(2)),2) + '-' +
right('0' + cast (datepart(day,d) as varchar(2)),2)
)
-- notice a lot of care need to be taken to ensure the format is comparable. (zero padding)
insert #t
values (getdate())
create index idx on #t(d2)
select d2, count(d2) from #t
where d2 between '2008-01-01' and '2009-01-22'
group by d2
-- index seek is used
Таким образом, вы можете напрямую проверить столбец d2, и индекс будет использоваться, и вам не придется возиться с преобразованиями.
Сэм, создание вычисляемого столбца с индексом - это полный перебор. Вместо этого поместите индекс в d, а затем используйте WHERE d >= '20080101' AND d < '20090123', и вы получите поиск по индексу. См. этот пост для тестирования производительности различных методов удаления временной части.
@Emtucifor этот метод предлагает серьезную оптимизацию производительности, если вы хотите группироваться по дням. в противном случае я согласен, что решение SQLMenaces здесь отлично работает.
Сэм, если нужно сгруппировать по значению, зачем превращать его в строку (которая также имеет лишние тире, занимающие дополнительное место без причины)? Используйте DateDiff () для выполнения работы, которая не только быстрее (что, как я понимаю, в данной ситуации менее проблематично), но и гораздо труднее ошибиться, о чем, похоже, свидетельствует ваше предупреждение в коде.
CONVERT(date, GETDATE()) и CONVERT(time, GETDATE()) работают с SQL Server 2008. Насчет 2005 года я не уверен.
Эти типы данных не существуют в SQL 2005.
В качестве альтернативы вы можете использовать
declare @d datetimeselect
@d = '2008-12-1 14:30:12'
where tstamp
BETWEEN dateadd(dd, datediff(dd, 0, @d)+0, 0)
AND dateadd(dd, datediff(dd, 0, @d)+1, 0)
Этот ответ неверен, потому что BETWEEN использует инклюзивные конечные точки, и запрос будет ошибочно включать значения tstamp '20081202 00: 00: 00.000'.
Как насчет этого?
SELECT DATEADD(dd, DATEDIFF(dd,0,GETDATE()), 0)
возможный дубликат Самый эффективный способ в SQL Server получить дату с даты + времени?