如何在SQL Server中仅将日期时间与日期进行比较


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

但在数据库中创建用户上2014-02-07 12:30:47.220,当我只放'2014-02-07'

它不显示任何数据

Answers:


81

不要试图做这样的事情:

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”一词的真正含义是什么?

编辑+有两个基本的原因可以避免在where子句(或联接条件)中对数据使用函数。

  1. 在大多数情况下,对数据使用函数进行过滤或联接会导致优化器无法访问该字段上的索引,从而使查询速度变慢(或更加“昂贵”)
  2. 另一个是,对于涉及的每一行数据,至少要执行一次计算。这可能会向查询添加数百,数千或数百万个计算,以便我们可以与单个条件进行比较2014-02-07。更改标准以适合数据的效率要高得多。

“描述标准以适合数据”是我描述“使用SARGABLE谓词”的方式


并且不要在两者之间使用。

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

col> ='20120101'和col <'20120201'不管时间部分是否适用,此表格均适用于所有类型和所有精度。

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


2
如果您有时间,请解释一下为什么一个比另一个更好(索引使用情况),这将是一个更好的答案
Serpiton 2014年

说起来容易做起来难,有几种数据类型(date,datetime,datetime2,smalldatetime)以及要考虑的不同mssql版本。一个答案不能满足我所有的恐惧。但我要加一个音符。
Used_By_Already 2014年

1
如果您使用cast(datetime作为date),它将仍然在datetime字段上使用索引。尝试一下。
史蒂夫·福特

@Steve Ford,我同意-在后来的mssql版本中,这就是为什么我说“说起来容易做起来难”的原因;在所有情况下都很难做到正确。但是,即使优化程序can有时会进行补偿,使用可控谓词也是一个好习惯。
Used_By_Already 2014年

2
最安全的date literal格式是“ YYYYMMDD”。数据类型:日期或日期时间或时间或日期时间2或smalldatetime都不具有全部格式,因为它们存储为数字。对于日期字面量'YYYY-MM-DD'并不完全安全,有一种语言环境设置会误解该序列为YYYY-DD-MM
Used_By_Already

69

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

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

应当注意,如果对日期列进行了索引,则该列仍将使用索引并且可以保存。这是日期和日期时间的特殊情况。

在此处输入图片说明

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

在此处输入图片说明

我刚刚在一个大表上尝试过,按照@kobik的注释在date列上有一个二级索引,并且该索引仍在使用,对于使用BETWEEN或> =和<的示例而言,情况并非如此

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

显示二级索引的索引使用情况


似乎我瞄准的是这个答案,但事实并非如此,因为我们几乎同时发布了该消息,但我没有意识到您的答案。但是为了减少您再次增加图像大小的需要,我更改了“不做”示例,也许这会有所帮助?
Used_By_Already

AaronsBad这些天数据库进展如何?
Pure.Krome

您可以说@ Pure.Krome是一个不再存在的临时db。
史蒂夫·福特

“应该注意,如果对date列进行索引,那么它将仍然使用索引并且可以保存” -当DateCreated为PK或聚集索引时,这是正确的。对于非聚集索引,有一个全表扫描。
kobik '16

@kobik实际上我刚刚进行了测试,并且该解决方案使用了一张大表的索引。其他解决方案则没有。
史蒂夫·福特

3

根据您的查询 Select * from [User] U where U.DateCreated = '2014-02-07'

SQL Server在比较确切的日期和时间,即(比较2014-02-07 12:30:47.220具有2014-02-07 00:00:00.000相等)。这就是为什么比较结果为假

因此,在比较日期时,您还需要考虑时间。您可以使用
Select * from [User] U where U.DateCreated BETWEEN '2014-02-07' AND '2014-02-08'


2
不幸的是,SQLbetween在两端都用等号(> =和<=)定义,例如select * from x where col_1 between 'A' and 'M' 包括A和M(当然加上B到L)。的包容性between对于诸如文档索引之类的东西而言是极好的。但是对于日期/时间范围,这是一场灾难,因为它会产生重叠。我知道在两者之间使用看起来不错,但实际上SQL between对于日期范围来说,just不能正常工作。始终使用> =和<的组合,如U.DateCreated >='2014-02-07' and U.DateCreated < '2014-02-08'
Used_By_Already

@Used_By_Already同意您的意见,但是对于这种特殊情况(有问题),between可以。您的建议,使用>=<无疑是最好过between的日期范围。我只想说比较日期时也应该考虑时间。
imdzeeshan

0

当然,这是一个旧线程,但要使其完整。

在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

您可以使用LIKEstatement而不是=。但是要使用DateStamp做到这一点,您CONVERT首先需要将其添加到VARCHAR中:

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

1
+1(因为这是可行的解决方案,并且可以在许多情况下使用),请不要因为您认为这不是一个好习惯而拒绝投票,为我们提供可靠的文章或文档,然后再说服我们
2015年

3
@Ahmad字符串转换后进行比较比日期比较昂贵。因此,减少投票是必要的,因为这不是执行OP想要执行的操作的好方法。
mbomb007 '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.