如何消除的时间部分datetime值(SQL服务器)?
-
08-06-2019 - |
题
这里就是我的使用:
SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)
我想可能有一个更美好和更优雅的方式。
要求:
- 它必须尽可能快的(少铸好)。
- 最终结果是一个
datetime
类型,不一串。
解决方案
SQL服务器2008年
在SQL服务器2008年,当然最快的方法是 Convert(date, @date)
.这个可以转换回 datetime
或 datetime2
如果有必要的。
什么是真正的最佳SQL服务器2005年和老?
我见过的不一致要求关于什么是最快的截断的时间从一个日期在SQL服务器,有些人甚至说他们做了试验,但我的经验已经不同。因此,让我们做一些更严格的测试并让每个人都有剧本所以,如果我犯任何错误,人们可以纠正我。
浮动转换并不准确
首先,我要远离转换 datetime
要 float
, 因为它不转换成正确的。你可能得到与做的时去除的事情准确的,但我认为这是个坏主意使用它,因为它隐含地进行通信,以开发,这是一个安全操作和 它不是.看一看:
declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops
这是不是我们应该教导人们在我们的码或在我们的实例在线。
还有,它甚至不是最快的方式!
证明性能的测试
如果你要执行一些试验自己,看看如何在不同的方法真的做堆起来,然后你会需要这个安装脚本运行测试远下:
create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
insert AllDay
select * from (
select Tm =
DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
from AllDay
) X
where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay; -- 25,920,000 rows
请注意,这将创建一个427.57MB表,在你的数据库,并将采取什么样的15-30分钟的运行。如果你的数据库是小和设置10%的增长,它将需要更长的时间超过如果你的大小足够大的第一个。
现在的实际性能测试脚本。请注意,这是有目的的不返回的行回客户,因为这是疯狂的昂贵月26亿行,并将隐藏的业绩之间的差异的方法。
性能的结果
set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
@dd date,
@d datetime,
@di int,
@df float,
@dv varchar(10);
-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms, elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms, elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.
-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms, elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms, elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;
一些散漫的分析
一些说明这一点。首先,如果只是执行一个集团或一个比较,就没有必要转换回来 datetime
.这样你可以节省一些CPU通过避免的是,除非你需要的最终价值显示的目的。你甚至可以组的未转化价值,并把换只有在选择条款:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
此外,看到数字转换,只需要稍微更多的时间转换回来 datetime
, 但 varchar
转换几乎翻倍?这揭示了该部分的CPU是专门讨论日期计算在查询。有部分占用,不涉及日期计算,这似乎是一些接近19875ms在上述查询。然后转换需要一些额外的金额,所以如果有两个转换,这一数额是用了大约两倍。
更多的审查显示,相比于 Convert(, 112)
, , Convert(, 101)
查询有一些额外的CPU费用(因为它使用一个更长的 varchar
?), 因为第二次转换回来 date
成本不尽最初的转换 varchar
, 但与 Convert(, 112)
它是更接近于同20000ms CPU基础的成本。
这里是这些计算的CPU时间,我用的上述分析:
method round single base
----------- ------ ------ -----
date 21324 19891 18458
int 23031 21453 19875
datediff 23782 23218 22654
float 36891 29312 21733
varchar-112 102984 64016 25048
varchar-101 123375 65609 7843
圆 是的CPU时间返回
datetime
.单 是CPU时间为一个单一的转换到备用数据的类型(一个具有副作用的除去的时间部分)。
基地 是的计算,减去从
single
两者之间的差异调用:single - (round - single)
.这是一个大概的数字,假定的转换,数据的类型和datetime
大约是相同的。出现这种假设是不完美的,但是接近,因为价值是所有靠近20000ms只有一个例外。
一个更有趣的事情是基本成本几乎等于单 Convert(date)
方法(其中有几乎0成本,因为服务器,可以在内部提取整天部右侧的第一个四字节的 datetime
数据类型)。
结论
那么是什么它看起来像是单方向 varchar
转化方法需要大约1.8微秒和用单方向 DateDiff
方法需要大约0.18μs。我基本上最保守的"基CPU"时间在我的测试18458ms总25,920,000行,因此23218ms/25920000=0.18μs。明显的10倍的改善似乎很多,但是坦率地说相当小,直到您正在处理成千上万的排(617k行=1第二节余)。
甚至给这个小的绝对改善,在我看来, DateAdd
方法的胜利,因为它是最佳组合的业绩和透明度。答案,需要一"神奇数字"的 0.50000004
是要咬人的某一天(零五或六个???), 再加上很难理解。
附加说明
当我得到一些时间我要改变 0.50000004
要 '12:00:00.003'
看看它是如何做。这是转换成相同的 datetime
值和我发现它很容易记得.
对于那些有兴趣,上述试验是在服务器上运行哪里@@版本的回报如下:
Microsoft SQL Server2008(题)-10.0.1600.22(英特尔X86)Jul9 2008 14:43:34Copyright(c)1988-2008微软公司的标准版本的Windows NT5.2(建立3790:Service Pack2)
其他提示
SQL服务器2008年有一个新的 日期 数据类型 这简化了这个问题:
SELECT CAST(CAST(GETDATE() AS date) AS datetime)
伊茨克Ben-Gan在 DATETIME计算,第1部分 (SQL服务器杂志》,2007年)显示了三种方法进行这样转换(最慢的速度最快的;之间的差别的第二和第三种方法是较小的):
SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)
SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)
SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)
你的技术(铸造 漂浮)是建议由一个读者在四月的问题的杂志。根据他的说法,它具有性能相当于第二技术提出上述。
你的 CAST
-FLOOR
-CAST
似乎已经是最佳的方式,至少在MS SQL Server2005年。
一些其他的解决方案,我看到的有一串转换,喜欢 Select Convert(varchar(11), getdate(),101)
在他们的,这是速度较慢的10倍。
请尝试:
SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
SQL2005:我建议铸造而不是dateadd.例如,
select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)
平均约10% 速度更快 在我的数据集,于
select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)
(铸造成smalldatetime快仍)