如何删除日期时间值(SQL Server)的时间部分?

以下是我使用的内容:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

我想可能会有更好更优雅的方式。

要求:

  • 它必须尽可能快(铸造越少越好)。
  • 最终结果必须是 datetime 类型,而不是字符串。
0
额外 编辑
意见: 3

6 答案

您的 CAST - FLOOR - CAST 似乎已成为最佳方式,至少在MS SQL Server 2005上。

我见过的其他一些解决方案有一个字符串转换,比如 Select Convert(varchar(11),getdate(),101),它们会减慢10倍。

0
额外
我们使用Michael Stum在我们的产品中提出的方法,它的作用就像魅力。
额外 作者 Chris Roberts,
这不是最佳的方式,相当多。请参阅我的回答在同一页上。
额外 作者 ErikE,

SQL Server 2008有一个新的 日期数据类型这简化了这个问题:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)
0
额外

Itzik Ben-Gan在 DATETIME Calculations,Part 1 (SQL Server Magazine,2007年2月)中显示执行这种转换的三种方法(最慢​​到最快;第二种和第三种方法之间的差异很小):

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)

读者可以在杂志的四月号中找到你的技术(铸造成 float )。据他介绍,它的性能可与上面介绍的第二种技术相媲美。

0
额外
@Emtucifor我同意第三种方法由于<0.50000004 值而非常模糊,但它是最快的一种,您的测试证实了这一点。。因此,它满足尽可能快的要求。
额外 作者 Marek Grzenkowicz,
@Emtucifor此外,这里就是我挂文章说,有关 0.50000004 值:虽然这种表达是短(高效,因为稍后我会证明),我不得不说我对此感到不安。我不确定我可以把自己的手指放在原因上?也许是因为它太过技术性,而且看不到与日期时间相关的逻辑。
额外 作者 Marek Grzenkowicz,
在我看来,铸造浮动并不是最好的。请查看我的回答
额外 作者 ErikE,
如果我们要使用这个方法,我宁愿选择CAST(CAST(GETDATE() - '12:00:00.003'AS int)AS datetime),因为它对我来说意味着什么并且更加易于记忆。
额外 作者 ErikE,
这在SQL 2008中是最快的: Convert(date,GetDate())
额外 作者 ErikE,

SQL Server 2008及更高版本

在SQL Server 2008及更高版本中,当然最快的方法是 Convert(date,@date)。如有必要,这可以转换回 datetimedatetime2

在SQL Server 2005和更早版本中,什么是最好的?

我已经看到关于从SQL Server中的日期截断时间最快的一些不一致的主张,有些人甚至说他们测试过,但我的经历却不同。所以让我们做一些更严格的测试,让每个人都有脚本,如果我犯了任何错误,人们可以纠正我。

浮动转换不准确

首先,我会远离将 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

Please note that this creates a 427.57 MB table in your database and will take something like 15-30 minutes to run. If your database is small and set to 10% growth it will take longer than if you size big enough first.

现在用于实际的性能测试脚本。请注意,不要将行返回给客户端是有目的的,因为这在2600万行上非常昂贵,并且会隐藏方法之间的性能差异。

效果结果

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;

一些Rambling分析

关于这个的一些说明。首先,如果只是执行GROUP BY或比较,则不需要转换回 datetime 。所以你可以通过避免这种方式来保存一些CPU,除非你需要显示的最终值。您甚至可以GROUP BY未转换的值并仅将转换置于SELECT子句中:

select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)

Also, see how the numeric conversions only take slightly more time to convert back to datetime, but the varchar conversion almost doubles? This reveals the portion of the CPU that is devoted to date calculation in the queries. There are parts of the CPU usage that don't involve date calculation, and this appears to be something close to 19875 ms in the above queries. Then the conversion takes some additional amount, so if there are two conversions, that amount is used up approximately twice.

More examination reveals that compared to Convert(, 112), the Convert(, 101) query has some additional CPU expense (since it uses a longer varchar?), because the second conversion back to date doesn't cost as much as the initial conversion to varchar, but with Convert(, 112) it is closer to the same 20000 ms CPU base cost.

以下是我用于上述分析的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
  • round is the CPU time for a round trip back to datetime.

  • single is CPU time for a single conversion to the alternate data type (the one that has the side effect of removing the time portion).

  • base is the calculation of subtracting from single the difference between the two invocations: single - (round - single). It's a ballpark figure that assumes the conversion to and from that data type and datetime is approximately the same in either direction. It appears this assumption is not perfect but is close because the values are all close to 20000 ms with only one exception.

另一个更有趣的事情是,基本成本几乎等于单一的 Convert(date)方法(它的成本几乎为0,因为服务器可以从内部提取整数日部分) datetime 数据类型的前四个字节)。

结论</强>

So what it looks like is that the single-direction varchar conversion method takes about 1.8 ?s and the single-direction DateDiff method takes about 0.18 ?s. I'm basing this on the most conservative "base CPU" time in my testing of 18458 ms total for 25,920,000 rows, so 23218 ms / 25920000 = 0.18 ?s. The apparent 10x improvement seems like a lot, but it is frankly pretty small until you are dealing with hundreds of thousands of rows (617k rows = 1 second savings).

即使给了这个小小的绝对改进,在我看来, DateAdd 方法是赢的,因为它是性能和清晰度的最佳组合。需要一个“幻数” 0.50000004 的答案会在某天(五个零或六个???)咬人,再加上它很难理解。

其他说明

当我得到一些时间时,我会将 0.50000004 更改为 '12:00:00.003',看看它是如何工作的。它被转换为相同的 datetime 值,我发现它更容易记住。

对于那些感兴趣的人,上述测试运行在服务器上@@版本返回以下内容:

Microsoft SQL Server 2008(RTM) - 10.0.1600.22(Intel X86)2008年7月9日14:43:34 Copyright(c)1988-2008 Microsoft Corporation Standard Edition on Windows NT 5.2(Build 3790:Service Pack 2)
0
额外
@加贝谢谢,修复。 Char看起来和varchar完全一样。
额外 作者 ErikE,
@ user3341592请提出一个新问题。随意评论一个链接。
额外 作者 ErikE,
Downvoter请评论?笑!
额外 作者 ErikE,
@Roman如果你正在使用SQL Server 2008或更高版本,是的,转换为 date 数据类型是最快的,如上面我的测试所示。
额外 作者 ErikE,
@ErikE,完成。请参阅 stackoverflow.com/questions/40193719/…
额外 作者 user3341592,
时间部分为“HH:MM:SS”和“HH:MM”变体,如果这样会产生差异?
额外 作者 user3341592,
知道相反的事情会很有趣:什么是保持时间的最佳方式?
额外 作者 user3341592,
它看起来像在表格中向后单个圆形。另外,如果使用 char 而不是 varchar ,那么在时间上有什么区别吗?
额外 作者 Gabe,
@Denis,在Oracle中有简单的trunc()函数。如果你使用round,我想你会发现中午12点之后的时间会四舍五入到第二天而不是当前日期。
额外 作者 Rick,
+1个很好的答案,但是没有将日期工作转换成更快的日期?
额外 作者 Roman Pekar,
顺便问一下,你测试过这个版本的SQL Server吗?
额外 作者 Martin Smith,
+1我喜欢浮点转换不准确的观点
额外 作者 A-K,
@瑞克,谢谢!事实上,你在日期时间内对这个关键词的含义睁开了眼睛。
额外 作者 Denis Valeev,
在Oracle中有来自双重的select round(sysdate),我们在Sql Server中肯定需要这一点。
额外 作者 Denis Valeev,

请尝试:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
0
额外

SQL2005:我推荐使用cast而不是dateadd。例如,

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

在我的数据集上的平均速度大约为10%

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(并且铸造成smalldatetime的速度更快)

0
额外