如何删除date时间值(SQL Server)的时间部分?

以下是我使用的:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME) 

我想可能会有更好,更优雅的方式。

要求:

  • 它必须尽可能快(铸造越less越好)。
  • 最终结果必须是datetimetypes,而不是string。

SQL Server 2008及以上

在SQL Server 2008中,当然最快的方法是Convert(date, @date) 。 如有必要,这可以转换回datetimedatetime2

什么是真正最好的在SQL Server 2005和较旧?

我看到了一些关于从SQL Server中的date截断时间最快的不一致的说法,有些人甚至说他们做了testing,但是我的经验却有所不同。 所以让我们做一些更严格的testing,让每个人都有脚本,如果我犯了什么错误,人们可以纠正我。

浮动转换不准确

首先,我会远离datetime时间转换为float ,因为它不正确转换。 你可以准确地做出去除时间的事情,但是我认为使用它是一个坏主意,因为它隐含地告诉开发者这是一个安全的操作, 事实并非如此 。 看一看:

 declare @d datetime; set @d = '2010-09-12 00:00:00.003'; select Convert(datetime, Convert(float, @d)); -- result: 2010-09-12 00:00:00.000 -- oops 

这不是我们应该在我们的代码或在线例子中教人的东西。

另外,它甚至不是最快的方法!

certificate – 性能testing

如果你想自己执行一些testing,看看不同的方法是如何堆叠起来的,那么你将需要这个安装脚本来进一步运行testing:

 create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED); declare @d datetime; set @d = DateDiff(Day, 0, GetDate()); insert AllDay select @d; while @@ROWCOUNT != 0 insert AllDay select * from ( select Tm = DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm) from AllDay ) X where Tm < DateAdd(Day, 1, @d); exec sp_spaceused AllDay; -- 25,920,000 rows 

请注意,这将在您的数据库中创build一个427.57 MB的表,并且需要15-30分钟的时间才能运行。 如果你的数据库很小并且设置为10%的增长率,那么比起你足够大的规模来说,要花费更长的时间。

现在为实际的性能testing脚本。 请注意,不要将行返回给客户端是有目的的,因为这在2600万行上非常昂贵,并且会隐藏方法之间的性能差异。

性能结果

 set statistics time on; -- (All queries are the same on io: logical reads 54712) GO declare @dd date, @d datetime, @di int, @df float, @dv varchar(10); -- Round trip back to datetime select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms, elapsed time = 22301 ms. select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms. select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms. select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms. select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms. select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms, elapsed = 108236 ms. select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms. -- Only to another type but not back select @dd = Tm from AllDay; -- CPU time = 19891 ms, elapsed time = 20937 ms. select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms. select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms. select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms. select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms, elapsed = 67987 ms. select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms. GO set statistics time off; 

一些Rambling分析

关于这个的一些说明。 首先,如果只是执行GROUP BY或比较,则不需要转换回datetime 。 所以你可以通过避免这个来节省一些CPU,除非你需要显示的最终值。 您甚至可以GROUP BY未转换的值,并只将转换置于SELECT子句中:

 select Convert(datetime, DateDiff(dd, 0, Tm)) from (select '2010-09-12 00:00:00.003') X (Tm) group by DateDiff(dd, 0, Tm) 

此外,看看数值转换只需要稍微多一点的时间转换回datetime ,但varchar转换几乎翻倍? 这揭示了在查询中用于计算date的CPU部分。 有部分CPU使用率不涉及date计算,并且在上述查询中似乎接近19875毫秒。 然后转换需要一些额外的金额,所以如果有两个转换,那么这个金额大约用了两次。

更多的检查表明,与Convert(, 112)相比, Convert(, 101)查询有一些额外的CPU开销(因为它使用更长的varchar ?),因为第二次转换的成本不像初始转换为varchar ,但Convert(, 112)更接近相同的20000 ms CPU基本成本。

以下是我用于上述分析的CPU时间的计算结果:

  method round single base ----------- ------ ------ ----- date 21324 19891 18458 int 23031 21453 19875 datediff 23782 23218 22654 float 36891 29312 21733 varchar-112 102984 64016 25048 varchar-101 123375 65609 7843 
  • round是往返datetime的CPU时间。

  • 单一是单次转换到备用数据types(具有删除时间部分副作用的那个)的CPU时间。

  • base是计算single减去两个调用之间的差异: single - (round - single) 。 这是一个大概的数字,假定从这个数据types和datetime的转换在任何一个方向上大致相同。 看起来这个假设并不完美,但是很接近,因为这些值都接近20000毫秒,只有一个例外。

另一个更有趣的事情是,基本成本几乎等于单一的Convert(date)方法(这几乎成本为零,因为服务器可以从datetime数据的前四个字节中内部提取整数date部分types)。

结论

所以看起来单向的varchar转换方法大约需要1.8μs,而单向的DateDiff方法大约需要DateDiff 。 我基于最保守的“基本CPU”时间,在testing总共18,584毫秒的25,920,000行时,因此23218毫秒/ 25920000 = 0.18微秒。 看起来10倍的提高似乎很多,但直到你处理成千上万的行(617k行= 1秒的节约),这是非常小的。

即使给了这个小小的绝对改进,我认为, DateAdd方法是赢得的,因为它是性能和清晰度的最佳组合。 需要一个“幻数”为0.50000004的答案0.50000004会咬某人(五个零或六个),再加上它更难理解。

补充笔记

当我得到一些时间,我要改变0.50000004'12:00:00.003' ,看看它是如何做的。 它被转换为相同的datetime值,我发现它更容易记住。

对于那些感兴趣的人,上面的testing运行在服务器上@@版本返回以下内容:

Microsoft SQL Server 2008(RTM) – 10.0.1600.22(Intel X86)Jul 9 2008 14:43:34 Copyright(c)1988-2008 Microsoft Corporation Standard Edition on Windows NT 5.2(Build 3790:Service Pack 2)

SQL Server 2008有一个新的date数据types ,这简化了这个问题:

 SELECT CAST(CAST(GETDATE() AS date) AS datetime) 

在DATETIME计算中的 Itzik Ben-Gan ,第1部分 (SQL Server Magazine,2007年2月)显示了执行这种转换的三种方法(从最慢到最快 ;第二和第三种方法之间的差别很小):

 SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime) SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime) 

读者可以在杂志的四月刊中find你的技巧(铸造浮动 )。 据他介绍,它的性能可与上面介绍的第二种技术相媲美。

您的CASTFLOORCAST似乎已经成为最佳方式,至less在MS SQL Server 2005上。

我见过的一些其他的解决scheme有一个string转换,比如Select Convert(varchar(11), getdate(),101) ,它们减慢了10倍。

请尝试:

 SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME] 

SQL2005:我推荐使用cast而不是dateadd。 例如,

 select cast(DATEDIFF(DAY, 0, datetimefield) as datetime) 

在我的数据集上平均 10%左右

 select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0) 

(并铸造smalldatetime更快)