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中日期时间的时间部分?
严格地说,方法
1 | a) SELECT DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0) |
事实证明,在相同的总持续时间内,有人占用了太多的时间,占用了一百万行,这是SQL Server中从日期+时间获取日期的最有效方法?
我在其他地方也看到了类似的测试结果。
我喜欢dateadd/datediff,因为:
- varchar受制于语言/日期格式问题示例:为什么我的case表达式是非确定性的?
- 浮动依赖于内部存储
- 它通过更改"0"基数扩展到每月的第一天、明天等。
编辑,OCT 2011
对于SQL Server 2008+,可以强制转换到
编辑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) |
1 | 'YYYY-MM-DD' OR '2017-01-09' |
超级易用在dplyr(
1 | CAST(round(CAST(getdate()AS REAL),0,1) AS datetime) |
此方法不使用字符串函数。
我想这比很多都快。
请参阅此问题:如何在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) |
选择convert(char(10),getDate(),126)
当心!
方法a)和b)并不总是具有相同的输出!
1 | SELECT DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0) |
输出:
1 | SELECT CAST(CONVERT(CHAR(11), '2013-12-31 23:59:59.999', 113) AS datetime) |
输出:
(在MS SQL Server 2005和2008 R2上测试)
编辑:根据Adam的注释,如果从表中读取日期值,则不会发生这种情况,但如果将日期值作为文本提供,则可能发生这种情况(例如:作为通过ADO.NET调用的存储过程的参数)。
我想你是说
real只有32位,可能会丢失一些信息
这是最快的
…虽然只有大约10%的速度加快了cx1〔3〕。
这是推荐的,并且在我刚才的测试中花费了相同的时间:
在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 |
我认为,如果你严格遵守
1 | SELECT CONVERT(datetime,CONVERT(INT,CONVERT(FLOAT,[Modified]))) |
我发现这种截断方法比
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:四舍五入为天;删除小时、分钟、秒和毫秒。
- 返回新的日期时间
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 ) |
从而有效地从已有的日期字段创建一个新字段。