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

标签 sql-server datetime date-conversion

这是我使用的:

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 中截断时间最快的说法不一致,有些人甚至说他们进行了测试,但我的经验有所不同。所以让我们做一些更严格的测试,让每个人都有脚本,这样如果我犯了任何错误,人们可以纠正我。

    浮点数转换不准确

    首先,我不会转换 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
    

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

    此外,它甚至不是最快的方式!

    证明 – 性能测试

    如果您想自己执行一些测试以查看不同方法的实际叠加情况,那么您将需要此设置脚本来进一步运行测试:
    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;
    

    一些漫无边际的分析

    关于这个的一些注意事项。首先,如果只是执行一个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 毫秒 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 时间.
  • single 是单次转换为备用数据类型(具有删除时间部分的副作用)的 CPU 时间。
  • base 是从 single 减去的计算两次调用的区别:single - (round - single) .这是一个大概的数字,假设与该数据类型和 datetime 之间的转换。在任一方向上大致相同。看起来这个假设并不完美但很接近,因为所有值都接近 20000 毫秒,只有一个异常(exception)。

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

    结论

    所以看起来是单向varchar转换方法大约需要 1.8 μs,单向 DateDiff方法大约需要 0.18 微秒。在我对 25,920,000 行总共 18458 毫秒的测试中,我基于最保守的“基本 CPU”时间,因此 23218 毫秒/25920000 = 0.18 微秒。明显的 10 倍改进似乎很多,但坦率地说,在您处理数十万行(617k 行 = 1 秒节省)之前,它是非常小的。

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

    附加说明

    等我有时间再换 0.50000004'12:00:00.003'看看它是怎么做的。它转换为相同的 datetime值(value),我发现它更容易记住。

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

    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 - 如何删除日期时间值的时间部分(SQL Server)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2775/

    相关文章:

    c++ - 在 C++ 中将时间从一种环境转换为另一种环境

    php - 从奇怪的推特格式用 PHP 格式化日期

    MySQL 的 At 时区转换

    sql-server - SQL Server 存储过程 XML 无法正确获取节点

    sql-server - 如果 SQL Server USER_NAME() 是系统名,为什么它返回 NVARCHAR(256)?

    c# - 为什么 DateTime.Parse 这么慢?

    sql-server - 日期字符串到日期时间 SQL Server 的破折号分隔格式

    java - LocalDateTime 和 SQL Server JDBC 4.2 驱动程序

    SQL Server View ,是福还是祸?

    mysql - 毫秒还是日期类型?