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

例如:

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

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

当前回答

在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

如果你担心微软的datetimes(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(),更不用说字符串连接了。

其他回答

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

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

为了得到更完整的答案,这里有一种截断到任何日期部分的工作方法,包括分钟(用要截断的日期替换GETDATE())。

这与公认的答案不同,因为你不仅可以使用dd (days),还可以使用任何日期部分(见这里):

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

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

这继续频繁地收集额外的投票,甚至几年后,所以我需要更新它的现代版本的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天。无论您的datetime是如何存储的,无论您的语言环境是什么,它都将工作。

最快捷的方法:

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

这是因为datetime列存储为8字节二进制值。将它们强制转换为浮动,将它们向下转换以删除分数,当您将值强制转换回datetime时,值的时间部分将消失。它只是位移位,没有复杂的逻辑,而且非常快。

请注意,这依赖于微软可以随时更改的实现细节,即使是在自动服务更新中。它也不是很便携。在实践中,这种实现不太可能很快改变,但如果您选择使用它,那么意识到其中的危险仍然很重要。既然我们可以选择约会,那就没什么必要了。

错误的方式:

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

错误的工作方式是转换为字符串,截断字符串,然后转换回日期时间。这是错误的,有两个原因:1)它可能不能在所有地区工作,2)这是最慢的方式……而且不是一点点;它比其他选项慢了一两个数量级。


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

在这两种情况下,您都希望编写查询以避免在一开始就需要这样做。在数据库上执行此工作的情况非常少。

In most places, the database is already your bottleneck. It's generally the server that's the most expensive to add hardware to for performance improvements and the hardest one to get those additions right (you have to balance disks with memory, for example). It's also the hardest to scale outward, both technically and from a business standpoint; it's much easier technically to add a web or application server than a database server and even if that were false you don't pay $20,000+ per server license for IIS or apache.

我试图说明的观点是,只要有可能,您就应该在应用程序级别上完成这项工作。在Sql Server上,你只会在需要按天分组时截断datetime,即使这样,你也应该将一个额外的列设置为计算列,在插入/更新时维护,或者在应用程序逻辑中维护。从数据库中删除这种破坏索引、cpu负荷大的工作。

您还可以使用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)

这个查询应该会给出与Oracle中的trunc(sysdate)相同的结果。

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

希望这能有所帮助!