关于根据日期范围获取记录的问题

时间:2010-10-10 03:17:23

标签: sql sql-server sql-server-2005 tsql sql-server-2008

SELECT COUNT(td.timeSheetID) FROM TimeSheet t 
INNER JOIN TimeSheetDetail td ON t.timeSheetID = td.timeSheetID 
WHERE (CONVERT(DateTime, t.setDate, 23) BETWEEN '2012-08-10' AND '2012-08-12') 
AND t.employeeID = 300

上面的代码返回我正在寻找的值。但是,当我执行以下操作时,我什么也得不到:

    SELECT COUNT(td.timeSheetID) FROM TimeSheet t INNER JOIN TimeSheetDetail td ON t.timeSheetID = td.timeSheetID 
    WHERE (CONVERT(DateTime, t.setDate, 23) = '2012-08-11') 
    AND t.employeeID = 300

表中的setDate值显示为:

2012-08-11 18:00:19.000

我的问题:

  1. 为什么第一个场景中的代码工作正常,而第二个场景中的代码不起作用?

  2. 为什么当我把实际范围设为BETWEEN'2012-08-11'EN'2012-08-11'时,第一个场景中的代码没有返回值...我怎样才能解决问题,我没有时间在范围/过滤期间考虑。我只被考虑约会。

2 个答案:

答案 0 :(得分:2)

  • 不要编写使用列上的函数与常量进行比较的查询。这使得它们无法进行SARG,并且无法使用setDate列上的最终索引。
  • 不要在可以使用参数
  • 的地方使用文字
  • 如果您必须将文字用于日期时间,请使用unseparated string format,因为它不依赖于语言,始终解释为ymd

总结:

SELECT COUNT(td.timeSheetID) FROM TimeSheet t 
INNER JOIN TimeSheetDetail td ON t.timeSheetID = td.timeSheetID 
WHERE t.setDate BETWEEN @dateFrom AND @dateTo 
AND t.employeeID = @employeeId;

或(更糟):

SELECT COUNT(td.timeSheetID) FROM TimeSheet t 
INNER JOIN TimeSheetDetail td ON t.timeSheetID = td.timeSheetID 
WHERE t.setDate BETWEEN '20120810' AND '20120812'
AND t.employeeID = 300;

至于你的问题,在日期时间范围内扫描“日期”的最佳方法是将日期表示为范围:

SELECT COUNT(td.timeSheetID) FROM TimeSheet t 
INNER JOIN TimeSheetDetail td ON t.timeSheetID = td.timeSheetID 
WHERE t.setDate >= '20120811' AND t.setDate < '20120812'
AND t.employeeID = 300;

这将涵盖20120811的所有时间。当然,参数而不是文字仍然更好。

最后,真正的问题是如果你有这个查询的覆盖索引。时间序列,就像'时间表'表一样,几乎总是通过日期范围插入,因此(employeeID, setDate)的聚簇索引可能是一个不错的选择。第二个最佳选择是同一个非聚集索引。

答案 1 :(得分:1)

使用CONVERT并将值更改为DATETIME时,不会删除时间。因此与日期值的比较将不匹配。尝试以下内容以了解会发生什么

SELECT CONVERT(DateTime, t.setDate, 23)
FROM TimeSheet t

要从datetime中删除时间,请更改CONVERT表达式,以便将结果转换为varchar数据类型

CONVERT(varchar, t.setDate, 23)