Дата только в MS SQL без времени

Вопрос

Всем привет,

В течение некоторого времени у меня была некоторая путаница с использованием 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

Решение

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

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
43
1
141 188
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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 и используйте> = и <.

ErikE 13.09.2010 04:46

Да, T-SQL временами может казаться чрезвычайно примитивным, и именно такие вещи часто заставляют меня выполнять большую часть моей логики на моем выбранном языке (например, C#).

Однако, когда вам абсолютно необходимо сделать некоторые из этих вещей в SQL по соображениям производительности, лучше всего создать функции для размещения этих «алгоритмов».

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

http://weblogs.sqlteam.com/jeffs/archive/2007/01/02/56079.aspx

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

ErikE 13.09.2010 04:36
Ответ принят как подходящий

это очень плохо для производительности, взгляните на Только в базе данных можно получить улучшение на 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)

Хорошо знать! Пора. :)

Lusid 22.01.2009 00:43

Без шуток :-) Здесь много нового с датами, похоже, у них есть временной интервал, более точное datetime с настраиваемой точностью и т.д ... конечно, им пришлось пойти и назвать новое время даты ... datetime2 ... тьфу .

JoshBerke 22.01.2009 00:53

Именно то, что мне нужно. Я перебрал множество других запутанных решений, прежде чем нашел ваше. Большое спасибо. +1

Baxter 20.01.2015 22:51
WHERE DATEDIFF(day, tstamp, @dateParam) = 0

Это должно помочь вам, если вам наплевать на время.

Это ответ на мета-вопрос о сравнении дат двух значений, когда вас не волнует время.

Проблема в том, что он не может использовать индекс.

Rob Boek 22.01.2009 00:53

Этот №1 не может использовать индекс, как сказал @Rob, а №2 даже без индекса заставляет вычислять каждую строку в таблице, а не только один раз, когда выражение находится справа, как в лучшем решении SQLMenace.

ErikE 13.09.2010 04:47

Функции Date, опубликованные другими, являются наиболее правильным способом справиться с этим.

Однако забавно, что вы упомянули термин «пол», потому что есть небольшая хитрость, которая работает несколько быстрее:

CAST(FLOOR(CAST(@dateParam AS float)) AS DateTime)

На мой взгляд, преобразование в float - плохая практика, потому что преобразование туда и обратно в datetime ненадежно. См. этот пост для более подробной информации. Преобразование в float также не так быстро, как решение DateDiff.

ErikE 13.09.2010 04:35

@Emtucifor - это старый пост. Обновленная информация здесь: stackoverflow.com/questions/923295/… и здесь: stackoverflow.com/questions/1427469/…

Joel Coehoorn 13.09.2010 07:47

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

ErikE 13.09.2010 08:05

Хорошо ... похоже, мне нужно запустить тесты скорости в SQL 2000. Я постараюсь сделать это в ближайшее время. Между тем, я все еще с подозрением отношусь к этому, потому что двусторонние преобразования datetime в float и обратно не сохраняют исходное значение. И для записи, самый быстрый способ (по крайней мере, в SQL 2008, запрет на преобразование в тип данных Date) - это не этот метод, а Convert(datetime, Convert(int, @dateParam - 0.50000004)).

ErikE 13.09.2010 08:15

FWIW, я много лет занимаюсь тем же, что и ты

CAST(CONVERT(VARCHAR, [tstamp], 102) AS DATETIME) = @dateParam 

Мне кажется, что это один из лучших способов сократить время с точки зрения гибкости, скорости и удобства. (Извините). Некоторые предлагаемые функции UDF могут быть полезны, но UDF могут работать медленно с большими наборами результатов.

Проблема в том, что он не может использовать индекс в столбце [tstamp].

Rob Boek 22.01.2009 00:53

Другая проблема заключается в том, что манипуляции со строками для дат SQL являются обычным антипаттерном. stackoverflow.com/questions/346659/…

Amy B 22.01.2009 00:59

Преобразование в varchar также происходит медленнее. См. этот пост для более подробной информации.

ErikE 13.09.2010 04:46

Будьте осторожны, если вы используете что-либо, кроме длинных строк 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', и вы получите поиск по индексу. См. этот пост для тестирования производительности различных методов удаления временной части.

ErikE 13.09.2010 04:38

@Emtucifor этот метод предлагает серьезную оптимизацию производительности, если вы хотите группироваться по дням. в противном случае я согласен, что решение SQLMenaces здесь отлично работает.

Sam Saffron 20.09.2010 10:47

Сэм, если нужно сгруппировать по значению, зачем превращать его в строку (которая также имеет лишние тире, занимающие дополнительное место без причины)? Используйте DateDiff () для выполнения работы, которая не только быстрее (что, как я понимаю, в данной ситуации менее проблематично), но и гораздо труднее ошибиться, о чем, похоже, свидетельствует ваше предупреждение в коде.

ErikE 20.09.2010 20:10

CONVERT(date, GETDATE()) и CONVERT(time, GETDATE()) работают с SQL Server 2008. Насчет 2005 года я не уверен.

Эти типы данных не существуют в SQL 2005.

ErikE 13.09.2010 04:50

В качестве альтернативы вы можете использовать

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

ErikE 13.09.2010 04:49

Как насчет этого?

SELECT DATEADD(dd, DATEDIFF(dd,0,GETDATE()), 0)

Другие вопросы по теме