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

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)




b) select cast(convert(char(11), getdate(), 113) as datetime)


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

两者看上去都非常快,但是可能会有一个

还可能有更好的方法来消除SQL中日期时间的时间部分吗?

评论

我已经在一张生产表中的100万条记录上进行了尝试,但无论哪种方式,我都无法准确了解性能。但这两个方法返回的数据量完全相同。

在18,000,000行中,这就是我发现的情况(SQL Server 2008):方法b比方法a慢24%。 CAST(FLOOR(CAST(getdate()AS FLOAT))AS DATETIME)比方法a慢3.5%。方法a似乎在性能方面是赢家。谢谢大家的出色回答。

为什么SQL仍然没有内置函数来执行此操作?!

SQL 2008的新DATE数据类型将处理此问题。
SQL Server中从日期+时间获取日期的最有效方法的可能重复项?

#1 楼

严格来说,方法a占用的资源最少:

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)


在某个总持续时间为一百万行的情况下,事实证明,CPU占用的时间更少,花费了很多时间:在SQL Server中从date + time获取日期的最有效方法?

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

我更喜欢DATEADD / DATEDIFF,因为:


varchar受到语言/日期格式问题的约束
示例:为什么我的CASE表达式是不确定的?

float依赖内部存储
通过更改“ 0”基数,可以扩展到每月的第一天,明天等

编辑,2011年10月

对于SQL Server 2008+,您可以CAST到dateCAST(getdate() AS date)。还是只使用date数据类型,这样就没有时间删除。

编辑,2012年1月

一个灵活的示例:需要通过四舍五入的时间或日期来计算sql server

编辑,2012年5月

不要在WHERE子句等中使用它,而无需考虑:向列中添加函数或CAST会使索引使用无效。请参阅此处的数字2:http://www.simple-talk.com/sql/t-sql-programming/ten-common-sql-programming-mistakes/

现在,确实有一个示例正确管理CAST的更高版本的SQL Server optimiser的最新版本,但通常这是一个坏主意...

编辑,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)


评论


@David Sopko用于2011年10月的编辑,则代码为:选择cast(GETDATE()作为日期)

–Choco Smith
2014年12月15日上午8:51

对于SQL的最新版本,使用date而不是datetime可以避免处理小时。使用以下示例:声明noTime date = getdate(),withTime datetime = getdate()选择@ noTime,@ withTime

– ozkary
17年1月19日在23:28

如果只需要日期,则按日期转换是很棒的。但是,通常您需要将当前日期设置为午夜,因此您可以进行一些进一步的日期操作。 DATE数据时间令人讨厌地限制了它对dateadd,datediff以及与其他日期/时间数据类型进行交互等操作的限制。对于这些情况,DATEADD()方法为王。

– Xedni
17年7月11日在17:02



并非在每个日期都有效。我错误地输入了0218而不是2018年作为年份,并且语句的DATEDIFF部分引发异常将datetime2数据类型转换为datetime数据类型导致日期时间值超出范围尝试:选择DATEDIFF(dd, 0,convert(datetime2(0),'0218-09-12',120))

– BernhardDöbler
'18 Sep 12'在16:03

@BernhardDöbler在2009年7月,当我回答时,“ 0218”将是一个有效的日期,因此您不会走那么远。同样,对于datetime2,“ 0”不会转换为19000101。试试这个选择SELECT DATEDIFF(dd,'19000101',convert(datetime2(0),'0218-09-12',120))

– gbn
18/09/13在9:34



#2 楼

在SQL Server 2008中,可以使用:

 CONVERT(DATE, getdate(), 101)
 


评论


从日期时间转换为日期时,第三个参数绝对与结果无关,因此您的解决方案有效地归结为仅CONVERT(DATE,getdate()),这已被多次提出。

– Andriy M
13-10-26在19:46

只需使用CAST(GETDATE()AS DATE)或严格来说是ANSI CAST(CURRENT_TIMESTAMP AS DATE),我认为这毫无用处。呆在第一个。

–伊万津尼奥
18/09/5在21:10

#3 楼

当然,这是一个旧线程,但要使其完整。

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

SELECT CONVERT(DATE,GETDATE())


#4 楼

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


...根据下面的评论,这不是一个好的解决方案。

我将删除此答案,但我将其保留为反例,因为我认为评论者对为什么不是一个好主意的解释仍然有用。

评论


看到GBN的答案,许多人对此进行了调查。 DATETIME不存储为浮点数,因此使用DATEADD / DATEDIFF可以避免对类型之间的CAST进行数学处理。

–MatBailie
09年7月24日在13:29

我可以接受,因为您描述的原因,您可能希望避免从DATETIME转换为FLOAT,但是在那种情况下,OPs选项(a)中从零的隐式转换也不是问题吗?嗯...我想在那种情况下它不是FLOAT,并且服务器可能足够聪明,可以丢弃时间信息。好吧,我承认:-)

–加里·麦吉尔
09年7月24日在14:16

0的确是从数字类型(我想是INT)到DATETIME的隐式转换。因为它是一个常数表达式,所以优化程序可以在编译时为存储过程执行此操作,并且只需要执行一次即可动态执行SQL。简而言之,这样做只有一个时间开销,基于FLOAT的查询的每一行都有相同的开销。

–MatBailie
09年7月26日在15:03

转换为浮动非常不精确。该答案应删除。没有人应该使用此代码。

–usr
2012年6月3日18:57

更不用说将其强制转换为浮点数并返回日期时间是不安全的-浮点数没有足够的精度。因此,我认为完全不能推荐它。有关更多详细信息,请参见这篇文章。

– ErikE
13年10月29日在4:25

#5 楼

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

CAST(GetDate() as DATE)




declare @Dt as DATE = GetDate()


评论


这就是我使用过的并且效果很好。似乎是最简单的答案。与CONVERT结合使用是否有任何弊端?

– joelmdev
18年1月29日在18:16

CAST和CONVERT在功能上等效。区别在于CAST是ANSI标准的一部分,而CONVERT是T-SQL特有的。因此,请尽可能使用CAST。

– Troy
18-09-5 18:40

@troy我使用CAST,因为我可以保存3个键入字母,并且语法比CONVERT更清晰,ANSI Standard部分毫无价值

–伊万津尼奥
18-09-5在21:13

#6 楼

这是另一个重复的问题给出的另一个答案:

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。

评论


太恐怖了我绝对不会像这样冒着风险。谁知道这对所有日期时间是否正确,而不仅仅是您测试的日期时间正确。

–usr
2012年6月3日18:58

@usr哦,是的,这只是一个神奇的数字,因此不应该使用。如果要检查其正确性,只需将一天中所有可能的日期塞入表格中并检查结果即可!另请参阅此帖子以获取更多信息。

– ErikE
13-10-29在4:28

@ErikE好点。您的答案提供了使用“ 12:00:00.003”的可能性,不过我认为更好。

–usr
13-10-29在7:12

#7 楼

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


评论


有效选项,是的。建议在此线程中多次。

– Andriy M
13年7月4日在11:51

#8 楼

我真的很喜欢:

[date] = CONVERT(VARCHAR(10), GETDATE(), 120)


120格式代码会将日期强制转换为ISO 8601标准:

'YYYY-MM-DD' or '2017-01-09'


超级好用的dplyr(R)和熊猫(Python)!

#9 楼

小心!

方法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上测试)

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

评论


.999不能存储在SQL Server的DATETIME列中。最高可用值是.997。发件人:msdn.microsoft.com/zh-cn/library/ms187819.aspx,您将看到将这些值四舍五入为具有千分之一的0、3或7。OP将看不到他们表中的测试值。

–亚当·温格(Adam Wenger)
2014年1月20日14:40

你是对的。我并不是想将其发布为OP问题的答案,而是将其发布给其他人查看,但我只有11个信誉点,而评论则需要15个。

– Broslav
2014年1月22日13:42

在您的第一个代码段中,字符串常量隐式转换为日期时间,在您的第二个代码段中,字符串常量保留为字符串(而113则被忽略)。

– Andriy M
2014年1月28日在16:11



#10 楼

首先在插入/更新上删除时间。对于即时转换,没有什么能比用户定义的函数更能打动用户了:

select date_only(dd)


date_only的实现可以随心所欲-现在抽象出来,调用代码要干净得多。

评论


我曾经设计一个触发器来从选定的列中清除时间。如果数据可以是坏数据,则不必清除它。

–菲利普·凯利(Philip Kelley)
09年7月24日在14:21

UDF方法有一个缺点,即它们不可行。如果在JOINs或WHERE子句中使用,则优化器无法使用INDEXes来提高性能。但是,使用DATEADD / DATEDIFF方法可以节省成本,并且可以从INDEX中受益。 (显然,FLOAT方法也是SARGable的)

–MatBailie
09年7月29日在11:55

@MatBailie,我希望与众不同! UDF绝对不可保存,但是Dateadd也不是Convert to float!在哪里DateAdd(DateDiff(Column))= @DateValue将不使用索引。另一方面,WHERE列> = dbo.UDF(@DateValue)和列
– ErikE
13-10-29在4:34

#11 楼

看到这个问题:如何在SQL Server中截断日期时间?

无论做什么,都不要使用字符串方法。那是最糟糕的方法。

评论


谢谢,我认为必须先问这个。奇怪的是,尽管我的实验指出,在SQL Server 2008上,float方法实际上比dateadd(dd,0,datediff(dd,0,getDate()))方法慢3.5%。对于每种方法,我确实运行了很多次测试,而当时的任何其他数据库服务器都未使用。

–斯蒂芬·佩雷尔森(Stephen Perelson)
09年7月24日在13:48

只能说,我对那些没有证明自己作为工作一部分定期且以非常科学的方式进行基准测试的人所做出的基准测试表示怀疑。当您查看它时,甚至gbn链接中的Thomas基准也存在一些明显的问题。这不一定会导致错误,只是不确定性。投射/地板/浇铸方法是很长时间以来公认的最快方法,我怀疑它曾经是毋庸置疑的。也就是说,我开始重新考虑它;特别是对于sql server 2008,无论如何都完全没有必要。

–乔尔·科恩(Joel Coehoorn)
09年7月24日在14:44

字符串方法非常易于使用,阅读和记忆。这些是我认为您低估的非常重要的因素!

–本
2012年1月31日12:36

@JoelCoehoorn,将转换样式121称为“ ODBC Canonical”。它不会随排序规则或区域设置而变化。字符串技巧也很容易概括为年,年+月,日,小时或分钟。

–本
2012年1月31日18:58



@Ben字符串技巧教给开发人员使用字符串转换。它们可以工作,但是日期数学远比其优越得多,这有很多原因,其中最重要的不是速度,而是原因,更多的是,学习如何使用数字作为日期可以使开发人员及其思维能力得到提高。通过代码中的数字操作变得流畅。

– ErikE
13-10-29在4:27

#12 楼

已经回答了,但也把它扔掉了。。。据说它也能很好地执行,但它的工作原理是从浮点数中舍去小数点(存储时间),然后只返回整部分(即日期)。

 CAST(
FLOOR( CAST( GETDATE() AS FLOAT ) )
AS DATETIME
)


第二次找到此解决方案...我从这段代码中抢了

评论


转换为float是不安全的。

– ErikE
13-10-29在4:35

#13 楼

CAST(round(cast(getdate()as real),0,1) AS datetime)


此方法不使用字符串函数。 Date基本上是一种实际的数据类型,其数字在小数点之前是一天的一部分。

我想这会快很多。

评论


以浮点形式投放是不安全的。

– ErikE
13-10-29在4:28

#14 楼

对我而言,以下代码始终是赢家:

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


评论


基本上与@Gary McGill的建议相同。

– Andriy M
2012-12-21 18:12



以浮点形式投放是不安全的。

– ErikE
13-10-29在4:29

#15 楼

选择CONVERT(char(10),GetDate(),126)

评论


您的建议与@broslav答案中提到的方法或该线程中确定为最慢的方法(与接受的答案中的链接相同)的主要区别是什么?

– Andriy M
2014年3月24日15:35

#16 楼

我认为您的意思是
cast(floor(cast(getdate()as float))as datetime)

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

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

...虽然仅快约10%(about 0.49 microseconds CPU vs. 0.58)

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

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

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

create function dateonly (  @dt datetime )
returns datetime
as
begin
return cast(floor(cast(@dt as float))as int)
end


#17 楼

select cast(cast my_datetime_field as date) as datetime)怎么样?这将导致日期相同,时间设置为00:00,但避免任何转换为​​文本,也避免任何显式的数字舍入。

评论


在这些答案中已经提出了建议:stackoverflow.com/a/17449578/569436 stackoverflow.com/a/10451347/569436 stackoverflow.com/a/19541838/569436 stackoverflow.com/a/1177529/569436

– Mikael Eriksson
2014年3月24日8:58



它们不一样。其他答案建议将其强制转换为没有时间成分的日期,然后再保留该日期。我的发布将其设置为日期时间,该时间为午夜的时间。有一个很大的不同;尝试导出到MS Excel,您会发现它处理日期时间比日期要好得多。

–德鲁博士
2014年3月25日在9:22

第一个完全相同。

– Mikael Eriksson
2014年3月25日在9:38

好的,是的,我确实看到了那个。如有必要,我很乐意将答案重复删除。

–德鲁博士
2014年3月25日在11:07

#18 楼

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

 select convert(datetime,convert(int,convert(float,[Modified])))


我发现这种截断方法要比截断方法快约5%。 DateAdd方法。可以很容易地将其修改为四舍五入到最近的日期,例如:

select convert(datetime,ROUND(convert(float,[Modified]),0))


评论


转换为float是不安全的。

– ErikE
13-10-29在4:31



#19 楼

在这里,我做了一个删除SQL Server日期时间某些部分的功能。用法:


第一个参数是要剥离的日期时间。
第二个参数是一个字符:


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

评论


谢谢安德里!我不知道我的建议没有那么有效。至少可以,但是您是对的。

–马克斯·瓦尔加斯(Max Vargas)
15年6月26日在20:53

#20 楼

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

CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)



在运行I SQL v11的环境中进行了测试Adaptive Server 15.7


评论


这更适合作为对已接受答案的编辑。有了其他20个答案,这将被掩埋并且几乎无法确定。公认的答案也提到了使用强制转换:对于SQL Server 2008+,您可以使用CAST进行更新。或者只是使用日期,所以没有时间删除。

–EWit
2014-09-24 13:03

最好将其发布为对等价Sybase问题的答案。如果没有这样的问题,您可以自由创建一个(并自己回答)。

– Andriy M
2014-09-25 5:36

此外,在将日期时间转换为日期时,将第三个参数指定为CONVERT是没有意义的:这两个参数都不具有固有格式。

– Andriy M
2014-09-25 5:40

#21 楼

如果可能的话,对于诸如此类的特殊事情,我喜欢使用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;
    }


#22 楼

我个人而言,如果处理SQL Server 2005(或更低版本),几乎总是为此使用用户定义的函数,但是,应注意的是,使用UDF存在特定的缺点,尤其是将其应用于WHERE子句时(请参见下文和有关此答案的评论,以获取更多详细信息)。如果使用的是SQL Server 2008(或更高版本),请参见下文。

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

我为“仅日期”和“仅时间”创建一个(尽管到目前为止,“仅日期”是这两个中使用最多的)。 br />
以下是指向各种与日期相关的UDF的一些链接:

SQL Server的基本日期,时间和DateTime函数仅获取日期函数

最后一个链接显示了至少3种不同的方式来获取日期时间字段中的日期,并提及了每种方法的利弊。

如果使用UDF,则应注意避免在查询中将UDF用作WHERE子句的一部分,因为这将大大妨碍查询的性能。这样做的主要原因是,在WHERE子句中使用UDF会使该子句成为不可保留的,这意味着SQL Server不能再在该子句中使用索引来提高查询执行的速度。关于我自己对UDF的使用,我将经常使用WHERE子句中的“原始”日期列,但将UDF应用于SELECTed列。这样,UDF仅应用于过滤的结果集,而不是表的每一行都作为过滤器的一部分。

当然,绝对最佳的方法是使用SQL Server 2008(或更高版本)并分离出日期和时间,因为SQL Server数据库引擎将本地提供各个日期和时间组件,并可以独立地有效查询这些组件。无需UDF或其他机制即可从复合datetime类型提取日期或时间部分。

评论


在某些情况下(例如,清理参数时),使用UDF可能会很好。但是在大多数情况下,这是一个糟糕的解决方案-为每行运行一次UDF是一种无需任何查询就可以杀死查询性能的方法!

– ErikE
13年10月29日在4:30

@ErikE-我不同意,Erik,UDF是性能杀手,这就是为什么我这么说,如果您可以使用SQL Server 2008或更高版本并使用为您执行此操作的内置数据类型,那将是最佳解决方案(既要达到要求,又要达到性能)。如果您坚持使用本机不支持此功能的SQL Server的较早版本,则将放弃某些东西以满足您的要求。

– CraigTP
13-10-29在7:12



真正。如果数据库引擎为我们提供了可以保存但又易于表达的东西,那就太好了。同时,如果您要寻找一整天中任何时间的值,这仍然是最佳的解决方案(至少对于SQL的旧版本而言):WHERE DateColumn> = {TimeTruncatingExpression}(@ DateValue)AND DateColumn < {TimeTruncatingExpression}(@ DateValue +1)。我觉得我不得不说些什么,因为您说“我几乎总是使用UDF”没有说明任何缺点,也没有说明使仅日期查询SARGable的方法。

– ErikE
13-10-29在18:12



@ErikE-不用担心,Erik。当我使用UDF时,要么是在处理性能不是最重要的小型数据集,要么是我已经针对“原始”日期字段过滤查询(以确保可保存性),但选择了该列使用UDF。由于这些通常是经过过滤的小型数据集,因此在这少量记录上运行UDF不会对性能造成影响。也就是说,您确实提出了一个很好的观点,我已经更新了我的答案以反映这一点。

– CraigTP
13年10月30日在9:16

#23 楼

我会使用:

CAST
(
CAST(YEAR(DATEFIELD) as varchar(4)) + '/' CAST(MM(DATEFIELD) as varchar(2)) + '/' CAST(DD(DATEFIELD) as varchar(2)) as datetime
) 


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

评论


为什么要这么做?您是否认为从datetime值中提取位,将其转换为字符串,将它们连接在一起并最终将结果转换回datetime优于例如在原始日期时间上执行直接计算(DATEADD / DATEDIFF方法)?

– Andriy M
2013年12月26日上午9:46

另外,MM和DD是什么? SQL Server中没有此类功能。

– Andriy M
2013年12月26日上午9:46