如何在SQL Server中只比较日期而非时间?

96
Select * from [User] U
where  U.DateCreated = '2014-02-07'     

但是在数据库中,用户是在 2014-02-07 12:30:47.220创建的,当我只输入 '2014-02-07'时,它不显示任何数据。

6个回答

101

不要被诱惑做这样的事情:

Select * from [User] U where convert(varchar(10),U.DateCreated, 120) = '2014-02-07'

这是更好的方法:

Select * from [User] U 
where U.DateCreated >= '2014-02-07' and U.DateCreated < dateadd(day,1,'2014-02-07')

参见:什么是“SARGable”?

编辑 +

  1. 大多数情况下,在where子句(或join条件)中对数据使用函数来过滤或连接会导致优化器无法访问该字段上的索引,从而使查询变慢(或者成本更高)。
  2. 另一个原因是,对于涉及的每行数据,至少执行一个计算。这可能会添加数百、数千甚至数百万个计算到查询中,以便我们可以将其与单一标准(如2014-02-07)进行比较。更有效的方法是修改标准以适应数据。

“根据数据修改标准”是我描述“使用SARGABLE谓词”的方式


也不要使用between。

处理日期和时间范围的最佳实践是避免使用BETWEEN,并始终使用以下形式:

WHERE col >= '20120101' AND col < '20120201'。该形式适用于所有类型和所有精度,无论时间部分是否适用。

http://sqlmag.com/t-sql/t-sql-best-practices-part-2(Itzik Ben-Gan)


2
如果您有时间,应该解释为什么一个比另一个更好(索引使用),这将使答案更好。 - Serpiton
1
如果你使用 cast(datetime as date),仍然会使用datetime字段上的索引。不妨试试看。 - Steve Ford
@SteveFord 你说得没错,但在我看来,在学习特殊情况之前,应该先学习一般情况。一般情况是,在索引列上使用 CAST 会从优化器中删除索引,此外,答案中指出的第二个原因在处理大型数据集时可能会很麻烦。 - Serpiton
唯一需要记住的是 SQL Server 中的日期格式为 'YYYYMMDD' 或 'YYYY-MM-DD'。 - V.J.
2
最安全的“日期字面量”格式是“YYYYMMDD”。数据类型:日期或日期时间或时间或datetime2或smalldatetime没有固定的格式,因为它们存储为数字。对于日期字面量,“YYYY-MM-DD”并不完全安全,因为有一种语言环境设置可能会误解该序列为YYYY-DD-MM。 - Paul Maxwell
显示剩余3条评论

90

如果您使用的是SQL Server 2008或更高版本,您可以使用日期数据类型:

SELECT *
FROM [User] U
WHERE CAST(U.DateCreated as DATE) = '2014-02-07'

需要注意的是,如果日期列已经被索引,那么这仍然会利用索引并且是可搜索的(SARGable)。这是日期和时间的特殊情况。

enter image description here

你可以看到SQL Server实际上将其转换为一个 > 和 < 子句:

enter image description here

我刚刚在一个大表格上尝试了这个,具有日期列上的辅助索引,就像@kobik的评论一样,并且索引仍然被使用。但是对于使用 BETWEEN 或 >= 和 < 的示例则不是这种情况:

SELECT *
FROM [User] U
WHERE CAST(U.DateCreated as DATE) = '2016-07-05'

展示辅助索引的索引使用情况


可能看起来我在针对这个答案,但事实并非如此,因为我们几乎同时发布了回答,我不知道你的回答。但是为了减少你再次增加图像大小的需要,我已经更改了我的“不要做”的示例,也许这会有所帮助? - Paul Maxwell
这个问题是在问“AaronsBad”数据库目前的情况如何? - Pure.Krome
@Pure.Krome,你可以看出那是一个临时数据库,现在已经不存在了。 - Steve Ford
1
需要注意的是,如果日期列被索引,那么这仍然会利用索引并且是SARGable。这适用于DateCreated是主键或聚集索引的情况。对于非聚集索引,则需要进行全表扫描。 - kobik
@kobik 实际上,我刚刚做了一个测试,这个解决方案使用了一个大表的索引。其他解决方案没有使用。 - Steve Ford

3
根据您的查询,SQL Server正在比较精确的日期和时间,即(比较2014-02-07 12:30:47.2202014-02-07 00:00:00.000是否相等)。这就是比较结果为false的原因。因此,在比较日期时,您需要考虑时间。您可以使用Select * from [User] U where U.DateCreated BETWEEN '2014-02-07' AND '2014-02-08'

2
不幸的是,SQL使用等于号(>=和<=)来定义“between”,例如“select * from x where col_1 between 'A' and 'M'”包括A和M(当然还有B到L)。这种包容性的“between”对于像文档索引这样的东西非常好;但对于日期/时间范围来说,它却是一场灾难,因为它会创建重叠。我知道使用“between”看起来很棒,但实际上,“SQL between”在处理日期范围时并不能正常工作。始终使用组合的>=和<,例如“U.DateCreated >='2014-02-07' and U.DateCreated < '2014-02-08'”。 - Paul Maxwell
@Used_By_Already 我同意你的观点,但是对于这个特定的情况(在问题中),between 是可以的。你提出的使用 >=<between 更可取,用于日期范围。我只想说,在比较日期时,我们也应考虑时间。 - imdzeeshan

2
当然,这是一个旧的帖子,但为了使其完整。
从SQL 2008开始,您可以使用DATE数据类型,因此您可以简单地执行以下操作:
SELECT CONVERT(DATE,GETDATE())

OR

Select * from [User] U
where  CONVERT(DATE,U.DateCreated) = '2014-02-07' 

-2
请尝试这个。这个查询可以用于日期比较。
select * from [User] U where convert(varchar(10),U.DateCreated, 120) = '2014-02-07'

-3

您可以使用LIKE语句代替=。但是,如果要对DateStamp执行此操作,则需要先将其转换为VARCHAR:CONVERT

SELECT * 
FROM [User] U
WHERE CONVERT(VARCHAR, U.DateCreated, 120) LIKE '2014-02-07%'

1
+1 因为这是一个可行的解决方案,可以在许多场景中使用,请不要因为您认为它不是一个好的实践而对工作答案进行负面评价。请提供一个可靠的文章或文档,然后您可以更好地说服我们。 - amd
4
将字符串转换后再进行比较的成本比日期比较要高。因此,对此进行投反对票是合理的,因为这不是实现 OP 想要的功能的好方法。 - mbomb007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接