Сравнение дат SQL с использованием только даты, а не времени

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

SELECT *
FROM Employee e
inner join OT_Hours o on o.Emp_ID=e.Emp_ID
inner join Position p on p.Position_ID=e.Position_ID
inner join Signup_Sheet s on s.Employee_ID=e.Emp_ID
WHERE e.Eligible_OT=1 and s.Day_Shift = 1 
and p.Position_Name = 'Controller' 
and Convert(Varchar(20),s.Date,101) = '07/26/2010'
and Convert(Varchar(20),o.Date,101) <='07/26/2010'
and Convert(Varchar(20),o.Date,101) > '07/26/2009'
and o.Quantity NOT IN(0.3) order by o.Date DESC

Я бы не получил результата, когда запустил этот запрос, но когда я удалил вторую последнюю строку, он вернул бы 12 результатов (‹=), а когда я удалил 3-ю последнюю строку, но оставил вторую последнюю, он вернул бы 6 результатов (>). После просмотра данных я увидел, что 4 из этих результатов должны были быть возвращены. Теперь о странной части. Ниже приведен код, который я использую в настоящее время.

SELECT DISTINCT o.Date, e.Emp_ID as Emp_ID, e.First_Name+ ' ' +e.Last_Name as Name, o.Quantity as Sum
FROM Employee e
left join OT_Hours o on o.Emp_ID=e.Emp_ID
left join Position p on p.Position_ID=e.Position_ID
left join Signup_Sheet s on s.Employee_ID=e.Emp_ID
WHERE e.Eligible_OT=1 and s.Day_Shift = 1 
and p.Position_Name = 'Controller' 
and Convert(Varchar(20),s.Date,101) = '07/26/2010'
and o.Date between '07/26/2009' and '07/26/2010'
and o.Quantity NOT IN(0.3) order by o.Date DESC

Этот запрос вернет результаты, но я также тестировал его, как и другой, когда o.Date был выше и ниже указанной даты. Когда дата была ‹= 16 результатов было возвращено, когда было возвращено> 8 результатов. Последний запрос дал 6 результатов. Сейчас я запрашиваю не производственную базу данных, и я единственный, кто ее использует, поэтому данные не изменились. Любое объяснение того, почему это происходило? Я предполагаю, что это было как-то связано с преобразованием его в varchar, и он не мог должным образом сравниваться, но это не объясняет, почему я получил 12 ‹=, 6>, а затем в итоге не получил никаких результатов. Также, если кто-то знает лучший способ реализовать это, дайте мне знать.


person Gage    schedule 27.07.2010    source источник
comment
Об этом уже спрашивали (в некотором смысле). Проверьте принятый ответ здесь ... stackoverflow.com/questions/2775/   -  person Wil P    schedule 27.07.2010


Ответы (5)


В третьей от последней строке вашего первого запроса вы сравниваете две строки.

Таким образом, 01/02/2009 больше 01/01/2010

Обычно я встречаюсь BETWEEN '01/02/2009 00:00:00.000' AND '01/01/2010 23:59:59.997', но будет интересно найти лучшее решение.

person Chris Diver    schedule 27.07.2010
comment
SQL Server выполняет неявное преобразование типа данных, переходя от строки к дате и времени, обеспечивая соответствие формата. - person OMG Ponies; 27.07.2010
comment
SELECT CASE WHEN '01/02/2009' > '01/01/2010' THEN 1 ELSE 0 END возвращает 1 в моем поле SQL. o.Date > '07/26/2009' будет неявно преобразован, но не два varchars согласно первому запросу. - person Chris Diver; 27.07.2010
comment
Не альтернатива, но вы ответили на мой вопрос о том, почему он не сравнивает даты должным образом. Не понял, что 01.01.2009 больше, чем 01.01.2010 lol - person Gage; 27.07.2010
comment
используйте dateiff только для сравнения дневной части: DATEDIFF (day, FieldDate, GETDATE ()) = 0 src: ссылка - person Michael Bahig; 23.02.2012

Два запроса не совпадают - это:

and o.Date between '07/26/2009' and '07/26/2010'

... эквивалент:

and o.Date >= '07/26/2009' 
and o.Date <= '07/26/2010'

BETWEEN - это стандарт ANSI, включающий все базы данных, с которыми я когда-либо сталкивался.

Имейте в виду, что если вы не укажете временную часть для DATETIMEs, значение по умолчанию будет начинаться в полночь дня - 00:00:00.

person OMG Ponies    schedule 27.07.2010

Я узнал об этом методе из журнала SQL Server Magazine за февраль 2007 г. («Расчет даты и времени» Ицика Бен-Гана). Таким образом, ваш 'between' будет работать независимо от того, находится ли дата в строке после полуночи, поскольку при этом сравнении все было нормализовано до полуночи:

select *
from someTable
where dateadd(day, datediff(day, 0, somedate), 0) between '07/26/2009' and '07/26/2010' 

DateUtils и dateadd работают вместе, чтобы убрать время и оставить дату. Затем вы можете сравнить его со строковыми литералами или другими датами, в которые были внесены такие же изменения. Я бы рекомендовал поместить это в функцию.

РЕДАКТИРОВАТЬ: на основе комментариев OMG Ponies. При этом не будет использоваться индекс в столбце даты. Альтернативой может быть использование техники удаления времени в дополнение к технике, упомянутой другими. Поэтому вместо того, чтобы делать это в столбце таблицы, сделайте это с последним аргументом 'between'. У вас может быть такая функция:

CREATE FUNCTION [dbo].[fn_enddate](@enddate datetime)
RETURNS datetime AS  
BEGIN
    DECLARE @endOfDay datetime
    set @endOfDay = dateadd(millisecond, -2, dateadd(day, datediff(day, 0, @enddate) + 1, 0))
    return @endOfDay
END

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

select *
from someTable
where somedate between '07/26/2009' and dbo.fn_enddate('07/26/2010')
person Ryan Ische    schedule 27.07.2010
comment
Я этого раньше не видел. Приятно знать. Они также говорили о 'CAST(FLOOR(CAST(' методе? - person Wil P; 27.07.2010
comment
Я не могу вспомнить. Он сравнил несколько, и этот оказался лучшим. - person Ryan Ische; 27.07.2010
comment
Я не рекомендую использовать это - использование функции в столбце делает индекс, если он существует, бесполезным. - person OMG Ponies; 27.07.2010
comment
@OMG Ponies - Интересно - определенно стоит рассмотреть. Значит, если оставить в строке, индекс будет использоваться? - person Ryan Ische; 27.07.2010
comment
@ Райан: Извините, я не понимаю, что строка слева? - person OMG Ponies; 27.07.2010
comment
@OMG Ponies - я имею в виду, если оставить, как в моем примере. - person Ryan Ische; 27.07.2010
comment
@ Райан: Извини - наоборот. Использование DATEADD(...somedate) означает, что если индекс существует (или включает) столбец somedate, оптимизатор не может использовать индекс, потому что данные изменились - нет никакой связи между значениями индекса и оцениваемым значением. - person OMG Ponies; 27.07.2010
comment
@OMG Ponies - нм, я понимаю, о чем вы говорите, никакого сканирования индекса из-за двух функций даты, которые он выполняет. Я думал о последней строке, которую я вставил о помещении всей манипуляции в функцию. Очень хороший момент. - person Ryan Ische; 27.07.2010

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

Кроме того, всегда сравнивайте даты с датами. Когда вы конвертируете их в строки и пытаетесь сравнить, вы сталкиваетесь с проблемами, как указывает Крис Дайвер.

В вашем случае я бы попробовал:

SELECT
    o.Date,
    e.Emp_ID as Emp_ID,
    e.First_Name+ ' ' +e.Last_Name as Name,
    o.Quantity as Sum
FROM
    Employee e
LEFT JOIN OT_Hours o ON o.Emp_ID = e.Emp_ID
LEFT JOIN Position p ON p.Position_ID = e.Position_ID
LEFT JOIN Signup_Sheet s ON s.Employee_ID = e.Emp_ID
WHERE
    e.Eligible_OT = 1 AND
    s.Day_Shift = 1 AND
    p.Position_Name = 'Controller' AND
    (s.Date >= @signup_date AND s.Date < DATEADD(dy, 1, @signup_date)) AND
    (o.Date >= @order_start_date AND o.Date < DATEADD(dy, 1, @order_end_date)) AND
    o.Quantity NOT IN (0.3)
ORDER BY
    o.Date DESC

Вам необходимо убедиться, что параметры или переменные всегда не содержат временной части.

person Tom H    schedule 27.07.2010

Если ваша база данных - это SQL Server, то, что я проделал достаточно хорошо, чтобы убрать время, выглядит примерно так ...

SELECT DISTINCT o.Date, e.Emp_ID as Emp_ID, e.First_Name+ ' ' +e.Last_Name as Name, o.Quantity as Sum 
FROM Employee e 
left join OT_Hours o on o.Emp_ID=e.Emp_ID 
left join Position p on p.Position_ID=e.Position_ID 
left join Signup_Sheet s on s.Employee_ID=e.Emp_ID 
WHERE e.Eligible_OT=1 and s.Day_Shift = 1  
and p.Position_Name = 'Controller'  
and CAST(FLOOR(CAST(s.Date AS FLOAT)) AS DATETIME) = '07/26/2010' 
and CAST(FLOOR(CAST(o.Date AS FLOAT)) AS DATETIME) between '07/26/2009' and '07/26/2010' 
and o.Quantity NOT IN(0.3) order by o.Date DESC 

В зависимости от того, как заданы ваши параметры «26.07.2010, 26.07.2009», вы можете сохранить их в переменных datetime и выполнить ту же операцию cast(floor(cast(@datevar as float)) as datetime).

Вроде репост. Проверьте принятый ответ здесь ... Как удалить временную часть значения datetime (SQL Server)?

person Wil P    schedule 27.07.2010