删除SQL Server中datetime时间部分的最佳方法

Best approach to remove time part of datetime in SQL Server

从SQL Server中的日期时间字段中删除时间部分时,哪种方法提供最佳性能?

1
a) SELECT DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

1
b) SELECT CAST(CONVERT(CHAR(11), getdate(), 113) AS datetime)

第二种方法确实会以任何方式发送更多的字节,但这可能不如转换速度重要。

两者看起来都很快,但处理数十万或更多行时,速度可能会有所不同?

此外,是否可能有更好的方法来消除SQL中日期时间的时间部分?


严格地说,方法a是资源最不密集的:

1
a) SELECT DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

事实证明,在相同的总持续时间内,有人占用了太多的时间,占用了一百万行,这是SQL Server中从日期+时间获取日期的最有效方法?

我在其他地方也看到了类似的测试结果。

我喜欢dateadd/datediff,因为:

  • varchar受制于语言/日期格式问题示例:为什么我的case表达式是非确定性的?
  • 浮动依赖于内部存储
  • 它通过更改"0"基数扩展到每月的第一天、明天等。

编辑,OCT 2011

对于SQL Server 2008+,可以强制转换到date。或者只是使用date,所以没有时间移除。

编辑2012

这有多灵活:需要在SQL Server中按四舍五入的时间或日期数字计算

编辑,2012年5月

不要在WHERE子句之类的语句中不加思考地使用它:向列中添加函数或强制转换会使索引的使用无效。请参阅下面的第2条:http://www.simple-talk.com/sql/t-sql-programming/ten-common-sql-programming-errors/

现在,这里有一个稍后的SQL Server优化程序版本的例子,它正确地管理到目前为止的强制转换,但一般来说这是一个坏主意…

编辑,2018年9月,日期时间2

1
2
3
4
DECLARE @datetime2value datetime2 = '02180912 11:45' --this is deliberately within datetime2, year 0218
DECLARE @datetime2epoch datetime2 = '19000101'

SELECT DATEADD(dd, DATEDIFF(dd, @datetime2epoch, @datetime2value), @datetime2epoch)


在SQL Server 2008中,可以使用:

1
CONVERT(DATE, getdate(), 101)


当然,这是一条旧线,但为了使它完整。

在SQL 2008中,您可以使用日期数据类型,因此您只需执行以下操作:

1
SELECT CONVERT(DATE,GETDATE())

1
SELECT CAST(FLOOR(CAST(getdate() AS FLOAT)) AS DATETIME)


在SQL Server 2008中,有一个日期日期类型(也是时间数据类型)。

1
CAST(GetDate() AS DATE)

1
DECLARE @Dt AS DATE = GetDate()


下面是另一个答案,来自另一个重复的问题:

1
SELECT CAST(CAST(getutcdate() - 0.50000004 AS INT) AS datetime)

此幻数方法的执行速度略快于dateadd方法。(看起来大约10%)

几轮一百万条记录的CPU时间:

1
2
3
4
5
DATEADD   MAGIC FLOAT
500       453
453       360
375       375
406       360

但请注意,这些数字可能不相关,因为它们已经非常快了。除非我有100000或更多的记录集,否则我甚至无法得到CPU时间来读取零以上的数据。

考虑到dateadd就是为了这个目的,而且更健壮,我想说使用dateadd。


1
SELECT CAST(CAST(GETDATE() AS DATE) AS DATETIME)


我真的很喜欢:

1
[DATE] = CONVERT(VARCHAR(10), GETDATE(), 120)

120格式代码将强制日期进入ISO 8601标准:

1
'YYYY-MM-DD' OR '2017-01-09'

超级易用在dplyr(R和pandas(Python中)!


1
CAST(round(CAST(getdate()AS REAL),0,1) AS datetime)

此方法不使用字符串函数。Date基本上是一个实数据类型,小数点前的数字是一天的小数。

我想这比很多都快。


请参阅此问题:如何在SQL Server中截断日期时间?

无论您做什么,都不要使用字符串方法。这是你做这件事最糟糕的方式。


对于我来说,下面的代码始终是赢家:

1
SELECT CONVERT(DATETIME, FLOOR(CONVERT(FLOAT,GETDATE())));


已经回答了,但我也会把这个扔到外面去…这也非常好地实现了,但它的工作原理是从浮点中丢弃小数(存储时间),只返回整个部分(即日期)。

1
2
3
4
 CAST(
FLOOR( CAST( GETDATE() AS FLOAT ) )
AS DATETIME
)

我第二次找到这个解决方案…我把这个密码拿走了


首先去除插入/更新的时间。至于动态转换,没有什么能比得上用户定义的函数的可维护性:

1
SELECT date_only(dd)

date_only的实现可以是任何你喜欢的东西——现在它被抽象掉了,调用代码更干净了。


选择convert(char(10),getDate(),126)


当心!

方法a)和b)并不总是具有相同的输出!

1
SELECT DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0)

输出:2014-01-01 00:00:00.000

1
SELECT CAST(CONVERT(CHAR(11), '2013-12-31 23:59:59.999', 113) AS datetime)

输出:2013-12-31 00:00:00.000

(在MS SQL Server 2005和2008 R2上测试)

编辑:根据Adam的注释,如果从表中读取日期值,则不会发生这种情况,但如果将日期值作为文本提供,则可能发生这种情况(例如:作为通过ADO.NET调用的存储过程的参数)。


我想你是说cast(floor(cast(getdate()as float))as datetime)

real只有32位,可能会丢失一些信息

这是最快的cast(cast(getdate()+x-0.5 as int)as datetime)

…虽然只有大约10%的速度加快了cx1〔3〕。

这是推荐的,并且在我刚才的测试中花费了相同的时间:DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

在SQL 2008中,与使用SQL函数相比,使用SQL函数的速度大约快5倍,即1.35微秒,而使用SQL函数的速度为6.5微秒,这表明与使用简单的SQL UDF相比,SQL CLR函数的函数调用开销要低得多。

在SQL 2005中,根据我的测试,SQL CLR函数的速度是这个慢函数的16倍:

1
2
3
4
5
6
CREATE FUNCTION dateonly (  @dt datetime )
RETURNS datetime
AS
BEGIN
RETURN CAST(FLOOR(CAST(@dt AS FLOAT))AS INT)
END

我认为,如果你严格遵守TSQL,这是缩短时间的最快方法:

1
 SELECT CONVERT(datetime,CONVERT(INT,CONVERT(FLOAT,[Modified])))

我发现这种截断方法比DateAdd方法快5%。这可以很容易地修改为四舍五入到最近的一天,如下所示:

1
SELECT CONVERT(datetime,ROUND(CONVERT(FLOAT,[Modified]),0))


埃多克斯1〔9〕怎么样?这将导致相同的日期,时间设置为00:00,但避免任何文本转换,也避免任何显式的数字舍入。


以防有人在这里寻找Sybase版本,因为上面的几个版本不起作用

1
CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)
  • 在运行在自适应服务器15.7上的I SQL v11中测试


在这里,我创建了一个函数来删除SQL Server的日期时间的某些部分。用途:

  • 第一个参数是要除去的日期时间。
  • 第二个参数是一个字符:
    • s:舍入到秒;删除毫秒
    • m:舍入到分钟;删除秒和毫秒
    • H:四舍五入为小时;删除分钟、秒和毫秒。
    • D:四舍五入为天;删除小时、分钟、秒和毫秒。
  • 返回新的日期时间

create function dbo.uf_RoundDateTime(@dt as datetime, @part as char)
returns datetime
as
begin
if CHARINDEX( @part, 'smhd',0) = 0 return @dt;
return cast(
Case @part
when 's' then convert(varchar(19), @dt, 126)
when 'm' then convert(varchar(17), @dt, 126) + '00'
when 'h' then convert(varchar(14), @dt, 126) + '00:00'
when 'd' then convert(varchar(14), @dt, 112)
end as datetime )
end


我个人认为,在处理SQL Server 2005(或更低版本)时,几乎总是使用用户定义的函数,但是,应该注意,使用UDF有一些特定的缺点,特别是在将它们应用于WHERE子句时(请参阅下面以及有关此答案的注释以了解更多详细信息)。如果使用SQL Server 2008(或更高版本)-请参阅下面的内容。

事实上,对于我创建的大多数数据库,我都在刚开始的时候添加了这些UDF,因为我知道99%的机会我迟早会需要它们。

我只为"仅日期"和"仅时间"创建了一个(尽管"仅日期"一个是迄今为止使用最多的两个)。

以下是与日期相关的各种UDF的链接:

基本的SQL Server日期、时间和日期时间函数只获取日期函数

最后一个链接显示了获取日期时间字段中只包含日期的部分的不少于3种不同方法,并提到了每种方法的一些优缺点。

如果使用UDF,应该注意,您应该尽量避免将UDF用作查询中WHERE子句的一部分,因为这将极大地阻碍查询的性能。这主要是因为在WHERE子句中使用UDF会使该子句不可解析,这意味着SQL Server不能再将索引与该子句一起使用,以提高查询执行的速度。关于我自己对UDF的使用,我将经常在WHERE子句中使用"原始"日期列,但将UDF应用于所选列。这样,UDF只应用于筛选的结果集,而不是作为筛选的一部分的表的每一行。

当然,实现这一点的最佳方法是使用SQL Server 2008(或更高版本)并分离日期和时间,因为SQL Server数据库引擎本身就提供了各个日期和时间组件,并且可以独立地有效地查询这些组件,而无需使用UDF或其他机制来提取日期或时间。复合日期时间类型的IME部分。


如果可能的话,对于这种特殊的事情,我喜欢使用clr函数。

在这种情况下:

1
2
3
4
5
6
7
8
9
10
11
12
[Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDateTime DateOnly(SqlDateTime INPUT)
    {
        IF (!INPUT.IsNull)
        {
            SqlDateTime dt = NEW SqlDateTime(INPUT.Value.Year, INPUT.Value.Month, INPUT.Value.Day, 0, 0, 0);

            RETURN dt;
        }
        ELSE
            RETURN SqlDateTime.Null;
    }

我会用:

1
2
3
4
CAST
(
CAST(YEAR(DATEFIELD) AS VARCHAR(4)) + '/' CAST(MM(DATEFIELD) AS VARCHAR(2)) + '/' CAST(DD(DATEFIELD) AS VARCHAR(2)) AS datetime
)

从而有效地从已有的日期字段创建一个新字段。