如何在SQL Server中截断日期时间?


280

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

例如:

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

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

Answers:


494

即使在几年之后,这种情况仍会继续频繁地获得更多投票,因此我需要针对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的日期时间时间是当你需要组按天,甚至那么你或许应该有一个额外的列设置为计算列,保持在插入/更新时间,或维持在应用程序逻辑中。使您的数据库摆脱索引中断,繁重的工作。


6
根据我刚刚运行的基准测试,“快速方法”仍然是sql 2008最快的方法
Sam Saffron 2010年

3
仅供参考:stackoverflow.com/q/1177449/27535stackoverflow.com/q/133081/27535 dateadd / datediff“ wins ...”。对于一个变量,谁在乎呢,当然希望您已经计算了超过一百万行的列:-)
gbn

9
这种“正确”的方法只是偶然地起作用。它的编写方式好像DateAdd的语法是(间隔,日期,增量),但事实并非如此。它是(间隔,增量,日期)。当我尝试将日期截断为月初时,我偶然发现了这一点:SELECT DATEADD(m,0,DATEDIFF(m,0,GETDATE()))不起作用,但是SELECT DATEADD(m,DATEDIFF(m, 0,GETDATE()),0)可以。至少,这是我在2008R2中看到的。
凯利·克莱恩

1
@Kelly在2008R2中,为什么不cast(getdate() as date)呢?
Joel Coehoorn

2
它们在datetime列上工作。getdate()这是您可能拥有的任何日期时间源的替代品。
Joel Coehoorn 2015年

44

仅适用于SQL Server 2008

CAST(@SomeDateTime AS Date) 

然后,如果需要,将其投射回日期时间

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

好点:我仍然在2005年,所以对于2008年,这可能是新的“正确”方式,甚至可以与“快速”方式的性能相匹配。
Joel

1
这种新方法的性能甚至比“快速”方法更快。
ErikE

21

只是为了获得更完整的答案,这是一种截断日期部分(包括分钟)的工作方法(用截断日期代替GETDATE())。

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

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

请注意,在上面的表达式中,0是一年初(1900-01-01)的恒定日期。如果需要截断较小的部分(例如秒或毫秒),则需要采用一个恒定的日期,该日期应更接近于被截断的日期,以避免溢出。


1
这是非常有益的。我到处寻找一种截断日期时间的方法,该时间比全天低。
Michael-Clay Shirky在哪里,

1
@Michael,感谢您的反馈,很高兴知道它对您有所帮助!
Lucero 2012年

1
+1应该有更多的投票权,这是一个很好的答案,它扩展了所选的答案。
jtate 2014年

1
如此一来,互联网就知道了,您不必仅限于整个时段。这是一个使用整数除法的15分钟间隔的示例:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael-Clay Shirky的位置

7

我必须在网上找到的代码段是:

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

我在2005年,但我认为2008年对此有一些新功能?
KM。

2
整齐!我本来打算将日期部分拆开并使用字符串处理将它们放回原处。可能不相关,但是SQL2008具有纯日期数据类型,没有时间元素。
弗朗斯,

1
请注意,您混合使用了DateAdd操作数DateAdd(dd, DateDiff(...), 0)。如果您不小心,可能会咬伤您。
ErikE 2013年

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

如果您担心Microsoft对日期时间(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(),更不用说字符串连接了。


1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)


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不似乎从铸造FLOATDATETIME准确,所以这增加了3毫秒地板之前。


1
但是要注意由于浮点精度限制而导致的舍入错误。此外,这不适用于datetime2数据类型。
卢塞罗

对于小时,SELECT DATEADD(hour,DATEDIFF(hour,0,GETDATE()),0)也起作用。分钟也可以,但是第二个会导致溢出。
凯利·克莱恩

强制转换为float并返回日期时间无法正常工作
ErikE 2013年

0

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

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

希望这可以帮助!


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)

0

甲骨文:

TRUNC(SYSDATE, 'MONTH')

SQL Server:

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

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



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.