在SQL Server中从datetime字段中删除时间部分时,哪种方法提供了最佳性能?
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
or
b) select cast(convert(char(11), getdate(), 113) as datetime)
第二种方法确实发送了更多的字节,但这可能没有转换速度那么重要。
两者看起来也都非常快,但在处理数十万行或更多行的时候,速度可能会有所不同。
此外,是否可能有更好的方法来消除SQL中datetime的时间部分?
就我个人而言,如果处理SQL Server 2005(或更低版本),几乎总是使用用户定义函数,然而,应该注意的是,使用UDF有特定的缺点,特别是如果将它们应用于WHERE子句(参见下面和对这个答案的评论了解更多细节)。如果使用SQL Server 2008(或更高版本)-请参见下面。
事实上,对于我创建的大多数数据库,我都是在一开始就添加这些UDF,因为我知道早晚有99%的可能性会用到它们。
我为“仅限日期”和“仅限时间”创建了一个(尽管“仅限日期”是迄今为止使用最多的一个)。
以下是一些与日期相关的UDF的链接:
基本SQL Server日期,时间和DateTime函数
只获取日期函数
最后一个链接显示了不少于3种获取datetime字段部分日期的不同方法,并提到了每种方法的优缺点。
If using a UDF, it should be noted that you should try to avoid using the UDF as part of a WHERE clause in a query as this will greatly hinder performance of the query. The main reason for this is that using a UDF in a WHERE clause renders that clause as non-sargable, which means that SQL Server can no longer use an index with that clause in order to improve the speed of query execution. With reference to my own usage of UDF's, I'll frequently use the "raw" date column within the WHERE clause, but apply the UDF to the SELECTed column. In this way, the UDF is only applied to the filtered result-set and not every row of the table as part of the filter.
当然,最好的方法是使用SQL Server 2008(或更高版本)并分离出您的日期和时间,因为SQL Server数据库引擎随后会本地提供单独的日期和时间组件,并且可以有效地独立查询这些组件,而不需要UDF或其他机制来从复合datetime类型中提取日期或时间部分。
严格来说,方法a是资源密集度最低的:
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
经过证明,在相同的总持续时间内,对于那些有太多时间的人来说,更少的CPU密集型:SQL Server中从日期+时间获得日期的最有效方法?
我在其他地方看到了类似的测试,结果也类似。
我更喜欢DATEADD/DATEDIFF,因为:
Varchar受语言/日期格式问题的影响
示例:为什么我的CASE表达式是非确定性的?
Float依赖于内部存储
它扩展到通过改变“0”基数来计算每月的第一天、明天等
编辑,2011年10月
对于SQL Server 2008+,您可以CAST到日期,即CAST(getdate() AS date)。或者只是使用日期数据类型,所以没有时间删除。
编辑,2012年1月
这是多么灵活的一个工作示例:需要在sql server中按四舍五入的时间或日期图进行计算
编辑,2012年5月
不要在WHERE子句和类似的地方使用这个:向列添加函数或CAST会使索引使用无效。参见第2条SQL编程常见错误
现在,这确实有一个SQL Server优化器版本正确管理CAST到日期的例子,但通常这将是一个坏主意…
编辑,2018年9月,datetime2
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)
这是来自另一个重复问题的另一个答案:
SELECT CAST(CAST(getutcdate() - 0.50000004 AS int) AS datetime)
这个魔数方法比DATEADD方法执行得稍微快一些。(看起来像~10%)
百万记录数轮的CPU时间:
DATEADD MAGIC FLOAT
500 453
453 360
375 375
406 360
但请注意,这些数字可能无关紧要,因为它们已经非常快了。除非我有100,000或更多的记录集,否则我甚至无法让CPU时间读数高于零。
考虑到DateAdd就是为了这个目的,而且更健壮,我建议使用DateAdd。
如果可能的话,对于这样的特殊情况,我喜欢使用CLR函数。
在这种情况下:
[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;
}
我想你的意思是
将(floor(Cast (getdate()as float))转换为datetime
Real只有32位,可能会丢失一些信息
这是最快的
Cast (getdate()+x-0.5 as int)as datetime
...虽然只快了约10% (CPU约0.49微秒vs. 0.58微秒)
这是推荐的,并且在我刚才的测试中花费了相同的时间:
DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
在SQL 2008中,SQL CLR函数比使用SQL函数快5倍,1.35微秒比6.5微节,这表明SQL CLR函数比简单的SQL UDF函数调用开销要低得多。
在SQL 2005中,根据我的测试,SQL CLR函数比这个慢函数快16倍:
create function dateonly ( @dt datetime )
returns datetime
as
begin
return cast(floor(cast(@dt as float))as int)
end
我认为如果你严格使用TSQL,这是截断时间的最快方法:
select convert(datetime,convert(int,convert(float,[Modified])))
我发现这种截断方法比DateAdd方法快5%左右。这可以很容易地修改为四舍五入到最近的一天,像这样:
select convert(datetime,ROUND(convert(float,[Modified]),0))
小心!
方法a)和b)并不总是有相同的输出!
select DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0)
输出:2014-01-01 00:00:00.000
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调用的存储过程的参数),则会发生这种情况。
在这里,我做了一个函数来删除SQL Server的日期时间的某些部分。用法:
第一个参数是要剥离的datetime。
第二个参数是char类型:
S:转到秒;删除毫秒
M:数到分钟;删除秒和毫秒
H:转到小时;删除分钟、秒和毫秒。
D:轮到天;删除小时、分钟、秒和毫秒。
返回新的日期时间
创建函数dbo。uf_RoundDateTime(@dt作为datetime, @part作为char)
返回日期时间
作为
开始
如果CHARINDEX(@part, 'smhd',0) = 0返回@dt;
返回演员(
案例@part
当's'时,convert(varchar(19), @dt, 126)
当'm'时,则convert(varchar(17), @dt, 126) + '00'
当'h'时,则convert(varchar(14), @dt, 126) + '00:00'
当'd'时,convert(varchar(14), @dt, 112)
结束为datetime)
结束