在SQL Server中删除日期时间的时间部分的最佳方法


514

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

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

要么

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

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

两者看起来都非常快,但是处理成千上万行或更多行时速度可能有所不同?

另外,是否有更好的方法可以消除SQL中日期时间的时间部分?


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

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

46
为什么SQL根本没有内置函数可以执行此操作?!
加里·麦吉尔

10
SQL 2008的新DATE数据类型将处理此问题。
菲利普·凯利2009年

Answers:


557

严格来说,方法a是最少的资源消耗:

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

事实证明,同一时间总计一百万行的CPU占用较少,占用的时间太多了:SQL Server中从date + time获取日期的最有效方法?

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

我更喜欢DATEADD / DATEDIFF,因为:

编辑,2011年10月

对于SQL Server 2008+,您可以CAST到dateie CAST(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优化程序版本,但通常这是一个坏主意...

编辑日期为2018年9月

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)

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

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

1
如果只需要日期,则强制转换为日期非常有用。但是,通常您需要在午夜的当前日期,以便随后进行一些进一步的日期操作。该DATE数据时间是在什么它会让你对于喜欢的东西DATEADD,DATEDIFF和互动与其他日期/时间数据类型做令人讨厌的限制。对于那些情况,这种DATEADD()方法将成为国王。
Xedni

并非在每个日期都有效。我输入02182018是错误的年份而不是年份,并且DATEDIFF您的声明的一部分抛出了异常。The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range datetime value尝试:select DATEDIFF(dd, 0, convert(datetime2(0), '0218-09-12', 120))
BernhardDöbler18年

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


52

当然,这是一个老话题,但为了使其完整。

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

SELECT CONVERT(DATE,GETDATE())

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

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

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


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

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

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

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

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

17

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

CAST(GetDate() as DATE)

要么

declare @Dt as DATE = GetDate()

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

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

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

8

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

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。


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

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

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

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

4
有效选项,可以。建议在此线程中多次。
Andriy M

老实说,这种解决方案最容易阅读。我的转到
BelgoCanadian

5

我很喜欢:

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

120格式的代码将迫使日到ISO 8601标准:

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

在dplyr(R)和pandas(Python)中超级好用!


3

谨防!

方法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调用的存储过程的参数),则可能发生这种情况。


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

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

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

2

首先,删除插入/更新上的时间。对于即时转换,没有什么能比用户定义的函数可维护性更好:

select date_only(dd)

的实现date_only可以是任何你喜欢-它抽象掉现在和调用代码是非常干净多了。


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

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

1
@MatBailie,我希望与众不同!UDF绝对不可保存,但Dateadd也不是Convert to float!WHERE DateAdd(DateDiff(Column)) = @DateValue不会使用索引。另一方面,WHERE Column >= dbo.UDF(@DateValue) AND Column < dbo.UDF(@DateValue + 1) SARGable。所以要小心。
ErikE 2013年

2

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

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


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

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

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

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

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

2

已经回答了,但是也把它丢掉了……应该可以很好地执行,但是它通过从浮点数中舍去小数点(存储时间)并仅返回整部分(即日期)来工作

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

第二次找到此解决方案... 我抓取了这段代码


1
转换为float 是不安全的
ErikE 2013年

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

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

我想这会快很多。


1
以浮法铸造是不安全的
ErikE 2013年


2

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


什么是您的建议在@ broslav的答案或从被确定为法中提到的方法的主要区别最慢这个线程(在接受的答案相同的链接)?
Andriy M 2014年

1

我想你是说 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

1

怎么select cast(cast my_datetime_field as date) as datetime)样 结果为同一日期,时间设置为00:00,但避免了转换为文本,也避免了任何显式的数字舍入。



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

第一个完全相同。
Mikael Eriksson 2014年

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

1

我认为,如果您严格遵守TSQL这一点,那么这是缩短时间的最快方法:

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

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

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

转换为float 是不安全的
ErikE 2013年

1

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

  • 第一个参数是要剥离的日期时间。
  • 第二个参数是一个字符:
    • s:四舍五入为秒;删除毫秒
    • 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 2015年

1

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

CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)
  • 在Adaptive Server 15.7上运行的I SQL v11中进行了测试

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

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

此外,它是没有意义的,当你转换到指定第三个参数转换datetimedate:那些都具有固有的格式。
Andriy M 2014年

0

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

0

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

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

我为“仅日期”和“仅时间”创建一个(尽管到目前为止,“仅日期”是这两个中使用最多的)。

以下是各种与日期相关的UDF的链接:

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

最后一个链接显示了不少于3种不同的方法来使日期仅成为datetime字段的一部分,并提到了每种方法的利弊。

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

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


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

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

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

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

-4

我会用:

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

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


2
为什么要这么做?您是否认为从datetime值中提取位,将其转换为字符串,将它们连接在一起并最终将结果转换回为datetime比在原始值上执行直接计算datetimeDATEADD/ DATEDIFF方法)更好?
Andriy M 2013年

另外,什么是MMDD?SQL Server中没有此类功能。
Andriy M 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.