307

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

例如:

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

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

14 回答 14

543

这继续频繁地收集额外的选票,甚至几年后,所以我需要为现代版本的 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)

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

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

错误的方法:

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

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


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

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

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

我要说明的一点是,只要有可能,您应该在应用程序级别完成这项工作。您发现自己在 Sql Server 上截断日期时间的唯一一次是当您需要按天分组时,即便如此,您可能应该将额外的列设置为计算列,在插入/更新时维护,或维护在应用程序逻辑中。从您的数据库中获取这种破坏索引、占用大量 CPU 资源的工作。

于 2009-05-28T21:35:55.977 回答
46

仅适用于 SQL Server 2008

CAST(@SomeDateTime AS Date) 

如果需要,然后将其转换回日期时间

CAST(CAST(@SomeDateTime AS Date) As datetime)
于 2009-05-28T22:15:46.160 回答
25

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

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

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

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

于 2011-05-31T12:02:27.243 回答
7

当我不得不这样做时,我在网上找到的片段是:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
于 2009-05-28T21:30:17.843 回答
2
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
于 2009-11-18T19:19:51.357 回答
1

在 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”格式将它们组合在一起,然后将其转换回日期。此方法涉及 7 个方法调用,包括最后的 CAST(),更不用说字符串连接了。

于 2009-06-25T04:14:47.053 回答
0

对于那些来这里寻找一种方法来将 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)

(始终将 a.0放在末尾以隐式转换为浮点数。)


对于那些想知道(3.0/86400000)在我的计算中是什么的人,SQL Server 2005 似乎并没有准确地从FLOAT转换到DATETIME准确,所以这在铺平之前增加了 3 毫秒。

于 2010-11-26T15:09:23.543 回答
0

选择 cast(floor(cast(getdate() as float)) as datetime) 参考:http ://microsoftmiles.blogspot.com/2006/11/remove-time-from-datetime-in-sql-server.html

于 2011-11-27T12:34:28.177 回答
0

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

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

希望这可以帮助!

于 2012-05-23T15:34:04.593 回答
0

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

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

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

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
于 2012-11-07T07:36:41.990 回答
0

甲骨文:

TRUNC(SYSDATE, 'MONTH')

SQL 服务器:

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

可以类似地用于从日期截断分钟或小时。

于 2014-02-14T08:26:16.443 回答
0

你可以这样做(SQL 2008):

声明 @SomeDate 日期 = getdate()

select @SomeDate

2009-05-28

于 2014-08-01T15:02:10.583 回答
0

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

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;

输出:

在此处输入图像描述

于 2021-06-15T20:08:39.137 回答
-1

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

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

于 2011-11-27T12:16:56.277 回答