【问题标题】:How can I truncate a datetime in SQL Server?如何在 SQL Server 中截断日期时间?
【发布时间】:2010-10-29 17:26:52
【问题描述】:

在 SQL Server 2008 中截断日期时间值(如删除小时、分钟和秒)的最佳方法是什么?

例如:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000

【问题讨论】:

    标签: sql-server sql-server-2008 datetime truncate


    【解决方案1】:

    这继续频繁地收集额外的选票,甚至几年后,所以我需要为现代版本的 Sql Server 更新它。对于 Sql Server 2008 及更高版本,这很简单:

    cast(getDate() As Date)
    

    请注意,靠近底部的最后三段仍然适用,您通常需要退后一步,首先找到避免演员表的方法。

    但也有其他方法可以做到这一点。以下是最常见的。

    正确的方法(自 Sql Server 2008 以来的新方法):

    cast(getdate() As Date)
    

    正确方法(旧):

    dateadd(dd, datediff(dd,0, getDate()), 0)
    

    这个现在比较老了,但它仍然值得了解,因为它还可以轻松适应其他时间点,例如月、分、小时或年的第一时刻。

    这种正确的方法使用了作为 ansi 标准的一部分并保证可以工作的文档化函数,但它可能会慢一些。它的工作原理是找出从第 0 天到当天有多少天,并将该天数加回到第 0 天。无论您的日期时间如何存储,也无论您的语言环境是什么,它都会起作用。

    快捷方式:

    cast(floor(cast(getdate() as float)) as datetime)
    

    之所以有效,是因为日期时间列存储为 8 字节二进制值。将它们转换为浮点数,将它们降低以删除分数,当您将它们转换回日期时间时,值的时间部分就消失了。这一切都只是位移,没有复杂的逻辑,而且它非常快。

    请注意,这依赖于 Microsoft 可以随时更改的实施细节,即使是在自动服务更新中也是如此。它也不是很便携。在实践中,这种实现不太可能很快改变,但如果你选择使用它,意识到危险仍然很重要。现在我们可以选择投射为日期,这几乎没有必要。

    错误的方式:

    cast(convert(char(11), getdate(), 113) as datetime)
    

    错误的方法是转换为字符串、截断字符串并转换回日期时间。这是错误,有两个原因:1)它可能无法在所有语言环境中工作,2)它可能是最慢的方法......而且不仅仅是一点点;它比其他选项慢一个或两个数量级。


    更新这最近得到了一些投票,所以我想补充一点,自从我发布这个以来,我已经看到了一些非常确凿的证据,表明 Sql Server 将优化两者之间的性能差异“正确”的方式和“快速”的方式,这意味着你现在应该倾向于前者。

    在任何一种情况下,您都希望编写查询以避免一开始就需要这样做。您很少会在数据库上进行这项工作。

    在大多数地方,数据库已经是您的瓶颈。通常,为提高性能而添加硬件最昂贵的服务器,以及最难正确添加硬件的服务器(例如,您必须平衡磁盘和内存)。从技术和商业角度来看,它也是最难向外扩展的;从技术上讲,添加 Web 或应用程序服务器比添加数据库服务器要容易得多,即使这是错误的,您也无需为 IIS 或 apache 的每个服务器许可证支付 20,000 美元以上。

    我想说的是,只要有可能,您就应该在应用程序级别进行这项工作。 唯一你应该发现自己在 Sql Server 上截断日期时间的时间是你需要按天分组的时候,即使那样你也应该有一个额外的列设置为计算列,维护在插入/更新时间,或在应用程序逻辑中维护。从您的数据库中移除这个破坏索引、占用大量 CPU 资源的工作。

    【讨论】:

    • 根据我刚刚运行的基准测试,“快速方式”仍然是 sql 2008 的最快方式
    • 仅供参考:stackoverflow.com/q/1177449/27535stackoverflow.com/q/133081/27535 dateadd/datediff“胜出...”。对于单个变量,当然谁在乎,并且希望您计算的列或超过一百万行:-)
    • 这种“正确”的方式只是偶然地起作用。它的编写方式就像 DateAdd 的语法是(间隔、日期、增量),但事实并非如此。它是(间隔、增量、日期)。当我试图将日期截断到月初时,我偶然发现了这一点: SELECT DATEADD( m, 0, DATEDIFF( m, 0, GETDATE( ) ) ) 不起作用,但是 SELECT DATEADD( m, DATEDIFF( m, 0, GETDATE( ) ), 0 ) 可以。至少,这是我在 2008R2 中看到的。
    • @Kelly 在 2008R2,为什么不只是 cast(getdate() as date)
    • 他们在日期时间列上工作。 getdate() 这里是您可能拥有的任何日期时间源的替身。
    【解决方案2】:

    仅适用于 SQL Server 2008

    CAST(@SomeDateTime AS Date) 
    

    然后根据需要将其转换回日期时间

    CAST(CAST(@SomeDateTime AS Date) As datetime)
    

    【讨论】:

    • 好点:我还在 2005 年,所以对于 2008 年,这可能是新的“正确”方式,甚至可能与“快速”方式的性能相匹配。
    • 这种新方式的性能比“快”方式还要快。
    【解决方案3】:

    为了获得更完整的答案,这是一种将日期部分向下截断并包括分钟的工作方法(将 GETDATE() 替换为要截断的日期)。

    这与公认的答案不同,因为您不仅可以使用dd(天),还可以使用任何日期部分(请参阅here):

    dateadd(minute, datediff(minute, 0, GETDATE()), 0)
    

    请注意,在上面的表达式中,0 是一年开始的固定日期 (1900-01-01)。如果您需要截断到更小的部分,例如秒或毫秒,则需要取一个更接近要截断的日期的常量日期,以避免溢出。

    【讨论】:

    • 这非常有用。我到处寻找一种在低于全天的地方截断日期时间的方法。
    • @Michael,感谢您的反馈,很高兴知道它对您有所帮助!
    • +1 这应该有更多的赞成票,这是一个很好的答案,可以扩展所选​​答案。
    • 正如互联网所知,您不必被限制在完整的日期部分期间。这是一个 15 分钟间隔的示例,使用整数除法:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
    【解决方案4】:

    当我不得不这样做时,我在网上找到的 sn-p 是:

     dateadd(dd,0, datediff(dd,0, YOURDATE))
     e.g.
     dateadd(dd,0, datediff(dd,0, getDate()))
    

    【讨论】:

    • 我在 2005 年,但我认为 2008 年对此有一些新功能??
    • 整洁!我会求助于拆分日期部分并使用字符串处理将它们重新组合在一起。可能不相关,但 SQL2008 具有纯日期数据类型,没有时间元素。
    • 请注意,您混淆了 DateAdd 操作数,它是 DateAdd(dd, DateDiff(...), 0)。如果你不小心,这可能会咬你。
    【解决方案5】:
    CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
    

    【讨论】:

      【解决方案6】:

      在 SQl 2005 中,您的 trunc_date 函数可以这样编写。

      (1)

      CREATE FUNCTION trunc_date(@date DATETIME)
      RETURNS DATETIME
      AS
      BEGIN
          CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
      END
      

      第一种方法更简洁。它只使用了 3 个方法调用,包括最后的 CAST() 并且不执行字符串连接,这是一个自动加号。此外,这里没有巨大的类型转换。如果您可以想象可以表示日期/时间戳,那么将日期转换为数字再转换回日期是一个相当简单的过程。

      (2)

      CREATE FUNCTION trunc_date(@date DATETIME)
      RETURNS DATETIME
      AS
      BEGIN
            SELECT CONVERT(varchar, @date,112)
      END
      

      如果您担心微软对日期时间的实现 (2) 或 (3) 可能没问题。

      (3)

      CREATE FUNCTION trunc_date(@date DATETIME)
      RETURNS DATETIME
      AS
      BEGIN
      SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
      ) AS DATETIME
      END
      

      第三,更详细的方法。这需要将日期分解为其年、月和日部分,以“yyyy/mm/dd”格式将它们放在一起,然后将其转换回日期。这个方法涉及到包括最后的 CAST() 在内的 7 个方法调用,更不用说字符串连接了。

      【讨论】:

        【解决方案7】:

        选择 cast(floor(cast(getdate() as float)) 作为日期时间) 参考这个:http://microsoftmiles.blogspot.com/2006/11/remove-time-from-datetime-in-sql-server.html

        【讨论】:

        【解决方案8】:

        对于那些来这里寻找将 DATETIME 字段截断为少于一整天(例如每分钟)的方法的人,您可以使用:

        SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)
        

        所以如果今天是2010-11-26 14:54:43.123,那么这将返回2010-11-26 14:54:00.000

        要更改其截断的间隔,请将 1440.0 替换为一天中的间隔数,例如:

        24hrs          =   24.0  (for every hour)
        24hrs / 0.5hrs =   48.0  (for every half hour)
        24hrs / (1/60) = 1440.0  (for every minute)
        

        (始终在末尾添加 .0 以隐式转换为浮点数。)


        对于那些想知道(3.0/86400000) 在我的计算中的用途的人来说,SQL Server 2005 似乎无法准确地从FLOAT 转换为DATETIME,因此这会在铺平之前增加 3 毫秒。

        【讨论】:

        • 请注意由于浮点精度限制导致的舍入误差...此外,这不适用于 datetime2 数据类型。
        • 对于小时,SELECT DATEADD(hour, DATEDIFF(hour, 0, GETDATE()), 0) 也有效。 Minute 也是,但 Second 会导致溢出。
        • 转换为浮动并返回到日期时间doesn't work correctly
        【解决方案9】:

        此查询应为您提供与 Oracle 中的 trunc(sysdate) 等效的结果。

        SELECT  * 
        FROM    your_table
        WHERE   CONVERT(varchar(12), your_column_name, 101)
              = CONVERT(varchar(12), GETDATE(), 101)
        

        希望这会有所帮助!

        【讨论】:

          【解决方案10】:

          您还可以从 datetime 变量中提取日期 using Substring 并转换回 datetime 将忽略时间部分。

          declare @SomeDate datetime = '2009-05-28 16:30:22'
          SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 
          

          此外,您可以访问部分日期时间变量并将它们合并到构造截断日期,如下所示:

          SELECT cast(DATENAME(year, @Somedate) + '-' + 
                 Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
                 DATENAME(day, @Somedate) 
                 as datetime)
          

          【讨论】:

            【解决方案11】:

            甲骨文:

            TRUNC(SYSDATE, 'MONTH')
            

            SQL 服务器:

            DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)
            

            同样可以用于截断日期的分钟或小时。

            【讨论】:

              【解决方案12】:

              你可以这样做(SQL 2008):

              声明@SomeDate date = getdate()

              select @SomeDate
              

              2009-05-28

              【讨论】:

                【解决方案13】:

                在使用分析时,您可能需要大量的日期\时间截断。所以我做了一个小函数来帮助解决这个问题:

                CREATE FUNCTION TRUNC_DATE
                (
                    @datetime datetime, -- datetime to be truncated
                    @level VARCHAR(10)  -- truncation level: year, month, day, hour and minute
                )
                RETURNS DATETIME
                AS
                BEGIN
                
                    IF (UPPER(@level) = 'YEAR')
                       RETURN DATEADD(YEAR,   DATEDIFF(YEAR, 0, @datetime), 0)
                    ELSE IF (UPPER(@level) = 'MONTH')
                        RETURN DATEADD(MONTH,   DATEDIFF(MONTH, 0, @datetime), 0)
                    ELSE IF(UPPER(@level) = 'DAY')
                       RETURN DATEADD(DAY,   DATEDIFF(DAY, 0, @datetime), 0)
                    ELSE IF (UPPER(@level) = 'HOUR')
                       RETURN DATEADD(HOUR,   DATEDIFF(HOUR, 0, @datetime), 0)
                    ELSE IF (UPPER(@level) = 'MINUTE')
                       RETURN DATEADD(MINUTE,   DATEDIFF(MINUTE, 0, @datetime), 0)
                
                    RETURN @datetime
                END
                GO
                

                评估函数(用你更改 GETDATE() 列):

                SELECT DBO.TRUNC_DATE(GETDATE(), 'YEAR')   YEAR;
                SELECT DBO.TRUNC_DATE(GETDATE(), 'MONTH')  YEAR_MONTH;
                SELECT DBO.TRUNC_DATE(GETDATE(), 'DAY')    YEAR_MONTH_DAY;
                SELECT DBO.TRUNC_DATE(GETDATE(), 'HOUR')   YEAR_MONTH_DAY_HOUR;
                SELECT DBO.TRUNC_DATE(GETDATE(), 'MINUTE') YEAR_MONTH_DAY_HOUR_MINUTE;
                

                输出:

                【讨论】:

                  【解决方案14】:

                  TRUNC(aDate, 'DD') 将截断分钟、秒和小时

                  SRC:http://www.techonthenet.com/oracle/functions/trunc_date.php

                  【讨论】:

                  • 这看起来像是 Oracle 的解决方案,而不是 SQL Server。
                  猜你喜欢
                  • 2021-06-11
                  • 2014-02-28
                  • 1970-01-01
                  • 2011-07-25
                  • 2018-04-24
                  • 2014-08-29
                  • 2021-04-06
                  • 2020-01-06
                  • 1970-01-01
                  相关资源
                  最近更新 更多