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

以下是我使用的内容:

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

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

要求:

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

  • SQL Server 2008及更高版本

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

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

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

    浮动转换不准确

    首先,我会远离将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
    

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

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

    证明 - 性能测试

    如果您想亲自执行一些测试以查看不同方法真正堆叠的方式,那么您需要使用此安装脚本来进一步运行测试:

    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
    

    请注意,这将在您的数据库中创建一个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;
    

    一些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转换几乎翻倍? 这揭示了在查询中用于计算日期的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
    
  • round是往返datetime的CPU时间。

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

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

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

    结论

    因此,单向方向的varchar转换方法大约需要1.8μs,而单向的DateDiff方法大约需要DateDiff 。 我基于最保守的“基本CPU”时间,在我测试25,920,000行的18458 ms总时间内,因此23218 ms / 25920000 = 0.18μs。 看起来10倍的改进看起来很多,但直到你处理数十万行(617k行= 1秒的节省)之前,它显得非常小。

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

    补充笔记

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

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

    Microsoft SQL Server 2008(RTM) - 10.0.1600.22(Intel X86)Jul 9 2008 14:43:34 Copyright(c)1988-2008 Windows NT 5.2(Build 3790:Service Pack 2)上的Microsoft Corporation标准版


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

    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)
    

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

    链接地址: http://www.djcxy.com/p/16889.html

    上一篇: How to remove the time portion of a datetime value (SQL Server)?

    下一篇: SQL select only rows with max value on a column