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

Nat*_*ord 81 sql-server datetime date-conversion

这是我使用的:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)
Run Code Online (Sandbox Code Playgroud)

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

要求:

  • 它必须尽可能快(铸造越少越好).
  • 最终结果必须是datetime类型,而不是字符串.

Eri*_*ikE 114

SQL Server 2008及更高版本

在SQL Server 2008及更高版本中,当然最快的方法是Convert(date, @date).这可以归还给datetimedatetime2必要时.

在SQL Server 2005及更早版本中最适合的是什么?

我已经看到关于从SQL Server中的日期截断时间最快的说法不一致,有些人甚至说他们做了测试,但我的经验却不同.所以,让我们做一些更严格的测试,让每个人都有脚本,所以如果我犯了任何错误,人们可以纠正我.

浮动转换不准确

首先,我会从转换走就走datetimefloat,因为它不正确地转换.你可以准确地完成时间去除工作,但我认为使用它是一个坏主意,因为它隐含地向开发人员传达这是一个安全的操作而事实并非如此.看一看:

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
Run Code Online (Sandbox Code Playgroud)

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

而且,它甚至不是最快的方式!

证明 - 性能测试

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

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
Run Code Online (Sandbox Code Playgroud)

请注意,这会在您的数据库中创建一个427.57 MB的表,并且需要15-30分钟才能运行.如果您的数据库很小并且设置为10%增长,则需要的时间比首先大小足够大.

现在进行实际的性能测试脚本.请注意,不会将行返回给客户端是有目的的,因为这在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;
Run Code Online (Sandbox Code Playgroud)

一些漫步分析

关于此的一些注释.首先,如果只是执行GROUP BY或比较,则无需转换回datetime.因此,除非您需要最终值用于显示目的,否则可以通过避免这种情况来节省一些CPU.您甚至可以将GROUP转换为未转换的值,并仅将转换放在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)
Run Code Online (Sandbox Code Playgroud)

另外,看看数字转换如何只需要稍多的时间转换回来datetime,但varchar转换几乎翻倍?这揭示了用于查询中日期计算的CPU部分.CPU使用的某些部分不涉及日期计算,在上述查询中,这似乎接近19875毫秒.然后转换需要一些额外的金额,因此如果有两次转换,则该金额大约用完两次.

更多的检查显示,与查询相比Convert(, 112),Convert(, 101)查询有一些额外的CPU费用(因为它使用的时间更长varchar?),因为第二次转换回来的date费用并不像初始转换那么多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
Run Code Online (Sandbox Code Playgroud)
  • round是往返回程的CPU时间datetime.

  • single是单次转换为备用数据类型的CPU时间(具有删除时间部分的副作用的那种).

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

另一个有趣的事情是基本成本几乎等于单个Convert(date)方法(必须几乎为0成本,因为服务器可以在内部从datetime数据类型的前四个字节中提取整数日部分).

结论

所以看起来是单向varchar转换方法需要大约1.8μs,单向DateDiff方法大约需要0.18μs.我基于最保守的"基本CPU"时间来测试18458 ms总共25,920,000行,因此23218 ms/25920000 =0.18μs.显而易见的10倍改进似乎很多,但坦率地说,直到你处理成千上万行(617k行= 1秒节省)时,它实际上相当小.

即使考虑到这种小的绝对改进,在我看来,该DateAdd方法也获胜,因为它是性能和清晰度的最佳组合.需要"神奇数字"的答案0.50000004有一天会咬人(五个零或六个???),而且更难理解.

补充说明

当我得到了一段时间我就准备换0.50000004'12:00:00.003',看看它是如何做.它被转换为相同的datetime值,我发现它更容易记住.

对于那些感兴趣的人,上述测试是在服务器上运行的,其中@@ Version返回以下内容:

Microsoft SQL Server 2008(RTM) - 10.0.1600.22(Intel X86)2008年7月9日14:43:34版权所有(c)1988-2008 Microsoft Corporation在Windows NT 5.2上的标准版(Build 3790:Service Pack 2)

  • @Roman如果您正在使用SQL Server 2008及更高版本,是的,转换为`date`数据类型的速度最快,如上面的测试中所示. (3认同)
  • +1我喜欢浮点数转换不准确的观点 (2认同)

Mar*_*icz 30

SQL Server 2008具有新的日期数据类型,这将此问题简化为:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)
Run Code Online (Sandbox Code Playgroud)


Mar*_*icz 18

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)
Run Code Online (Sandbox Code Playgroud)

你的技术(铸造浮动)是由该杂志4月刊的读者提出的.据他介绍,它具有与上述第二种技术相当的性能.

  • 这在SQL 2008中现在最快:`Convert(date,GetDate())`. (6认同)
  • 如果我们要使用此方法,则我更喜欢使用SELECT CAST(CAST(GETDATE()-'12:00:00.003'AS int)AS datetime)`,因为这对我来说很有意义,而且imo更容易要记住。 (2认同)

Mic*_*tum 12

您的CAST- FLOOR- CAST似乎已经是最佳方式,至少在MS SQL Server 2005上.

我见过的其他一些解决方案有一个字符串转换,就像Select Convert(varchar(11), getdate(),101)在它们中一样,速度慢了10倍.

  • 这不是最佳方式,相当多.请参阅[我的回答](http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql-server/3696991 #3696991)在同一页面上. (3认同)