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

例如:

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

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

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

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

这继续频繁地收集额外的投票,甚至几年后,所以我需要更新它的现代版本的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负荷大的工作。

仅适用于SQL Server 2008

CAST(@SomeDateTime AS Date) 

然后将其转换回datetime

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

在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(),更不用说字符串连接了。

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

对于那些来到这里寻找一种方法来截断一个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毫秒。

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

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

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

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

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

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

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

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

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

希望这能有所帮助!

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

SQL服务器:

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

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

你可以这样做(SQL 2008):

@SomeDate date = getdate()

select @SomeDate

2009-05-28

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

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

计算函数(用you列更改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;

输出:

SQL Server 2022有DATETRUNC

DECLARE @d datetime2 = '2021-12-08 11:30:15.1234567';
SELECT 'Year', DATETRUNC(year, @d);
SELECT 'Quarter', DATETRUNC(quarter, @d);
SELECT 'Month', DATETRUNC(month, @d);
SELECT 'Week', DATETRUNC(week, @d); -- Using the default DATEFIRST setting value of 7 (U.S. English)
SELECT 'Iso_week', DATETRUNC(iso_week, @d);
SELECT 'DayOfYear', DATETRUNC(dayofyear, @d);
SELECT 'Day', DATETRUNC(day, @d);
SELECT 'Hour', DATETRUNC(hour, @d);
SELECT 'Minute', DATETRUNC(minute, @d);
SELECT 'Second', DATETRUNC(second, @d);
SELECT 'Millisecond', DATETRUNC(millisecond, @d);
SELECT 'Microsecond', DATETRUNC(microsecond, @d);
Year        2021-01-01 00:00:00.0000000
Quarter     2021-10-01 00:00:00.0000000
Month       2021-12-01 00:00:00.0000000
Week        2021-12-05 00:00:00.0000000
Iso_week    2021-12-06 00:00:00.0000000
DayOfYear   2021-12-08 00:00:00.0000000
Day         2021-12-08 00:00:00.0000000
Hour        2021-12-08 11:00:00.0000000
Minute      2021-12-08 11:30:00.0000000
Second      2021-12-08 11:30:15.0000000
Millisecond 2021-12-08 11:30:15.1230000
Microsecond 2021-12-08 11:30:15.1234560

https://learn.microsoft.com/en-us/sql/t-sql/functions/datetrunc-transact-sql?view=sql-server-ver16