SQL Server中的DateTime2 vs DateTime


762

哪一个:

SQL Server 2008+中存储日期和时间推荐方法是什么?

我知道精度(可能还有存储空间)上的差异,但是暂时忽略这些差异,是否有关于何时使用什么的最佳实践文档,或者也许我们应该只使用datetime2

Answers:


641

有关datetime的MSDN文档建议使用datetime2。这是他们的建议:

使用timedatedatetime2datetimeoffset数据类型的新工作。这些类型符合SQL标准。它们更便携。 timedatetime2datetimeoffset 提供更高的秒精度。 datetimeoffset为全球部署的应用程序提供时区支持。

datetime2具有较大的日期范围,较大的默认小数精度和可选的用户指定精度。另外,根据用户指定的精度,它可能会使用较少的存储空间。


46
尽管datetime2的精度有所提高,但某些客户端不支持日期,时间或datetime2,因此会迫使您转换为字符串文字。如果您更关心兼容性而不是精度,请使用datetime
FistOfFury 2014年

4
另一个选择是使用索引视图,将列转换为日期时间以实现兼容性。但是,您需要能够将应用程序指向视图。
TamusJRoyce '17

9
DATETIMEOFFSET支持的时区使用不当。它仅存储特定时间点(而不是时区)的UTC偏移量。
Suncat2000 '17

5
@Porad:由于是“ SQL Standard”,因此“具有更高的可移植性”在实践中到底有什么好处?除了使您编写更多的代码之外,对于“移植”到另一个RDBMS的“端口”来说,它们的可读性/可维护性也大大降低了。除了Microsoft提供的SQL Server工具和驱动程序(如果是偶数)以外,是否有任何应用程序实际上依赖于DateTime2类型的特定位级别表示(或其他任何SQL Server)?键入这个问题),请参阅缺点在我下面的17年7月10日回答我为什么问这个?
汤姆

2
@亚当·波拉德(Adam Porad):此外,所有这些好处可能都是不需要的(在工程或科学应用程序之外),因此不值得付出太多的损失,更可能需要的是:隐式/显式转换为(甚至考虑变通方法)更容易的能力(甚至考虑解决方法)一个浮点数字(加,减,最小值,最大值和平均值)的浮点数值(如果包括,则为天数,如果自最小日期时间起,则为小数天)。有关详细信息,请参见下面我的7/10/17答案中的缺点。
汤姆(Tom)

493

DATETIME2日期范围为“ DATETIME0001/01/01 ”到“ 9999/12/31 ”,而该类型仅支持1753-9999年。

另外,如果需要,DATETIME2可以在时间上更精确;DATETIME限制为3 1/3毫秒,而DATETIME2精确到100ns。

两种类型都映射到System.DateTime.NET中-在那里没有区别。

如果您有选择,我建议您DATETIME2尽可能使用。我看不到使用有什么好处DATETIME(除了向后兼容性以外),您的麻烦会更少(日期超出范围并且像这样麻烦)。

另外:如果您只需要日期(没有时间部分),请使用DATE-它既好DATETIME2又节省空间!:-)相同的只是时间-使用TIME。这就是这些类型的用途!


157
将.NET DateTime值作为参数添加到SqlCommand时要小心,因为它喜欢假定它是旧的datetime类型,并且如果您尝试写入超出1753-9999年范围的DateTime值,则会收到错误消息除非您为SqlParameter明确将类型指定为System.Data.SqlDbType.DateTime2。无论如何,datetime2很棒,因为它可以存储.NET DateTime类型中可以存储的任何值。
Triynko 2010年

10
@marc_s-这不是null吗?
JohnFx 2011年

8
@JohnFX-有点晚了-但是您不会将datetime设置为null。您将使用Nullable <datetime>还是datetime?处理null就好了-在映射到proc时,只需执行param.value = someDateTime?DBValue.Null其不幸我们坚持用数字的数据类型之后-只是似乎很“通用”:)
亚当Tuliper - MSFT

69
大声笑,在我意识到这是我自己的评论(一年多以前)之前,我只是试图对自己的评论(上面)进行评论。我仍在处理.NET框架的愚蠢设计决策,以默认方式在将所有DateTime值作为SqlParameters传递时将其截断,除非您将其显式设置为更精确的SqlDbType.DateTime2。自动推断正确的类型非常重要。确实,他们应该使更改透明化,替换精度较低,效率较低,范围有限的实现,并保留原始的“ datetime”类型名称。另请参见stackoverflow.com/q/8421332/88409
Triynko,2011年

5
@marc_s不是Nullable<DateTime>吗?
ChrisW

207

datetime2在大多数方面都胜出,但(旧应用程序兼容性)除外

  1. 更大的价值范围
  2. 精度更高
  3. 较小的存储空间 (如果指定了用户指定的可选精度)

SQL日期和时间数据类型比较-datetime,datetime2,date,TIME

请注意以下几点

  • 句法
    • datetime2 [(分数秒精度=>低于存储大小)
  • 精度,规模
    • 0至7位数字,精度为100ns。
    • 默认精度为7位数字。
  • 储存空间
    • 6个字节,精度小于3;
    • 7字节的精度3和4。
    • 所有其他精度要求8个字节
  • DateTime2(3)的位数与DateTime相同,但是使用7个字节的存储空间,而不是8个字节(SQLHINTS- DateTime与DateTime2
  • 查找有关datetime2的更多信息(Transact-SQL MSDN文章)

图像来源: MCTS自定进度培训套件(考试70-432):Microsoft®SQLServer®2008-实施和维护 第3章:表->第1课:创建表->第66页


7
感谢您为它的统计数据+1 datetime2
太好了

2
@Iman Abidi:根据Oskar Berggren于2014年9月10日下午3:51在您引用的“ SQLHINTS- DateTime VS DateTime2”文章上的评论:“ datetime2(3)与datetime不同。它们将具有相同的数字位数,但datetime的精度为3.33ms,而datetime2(3)的精度为1ms。”
汤姆

1
@PankajParkar:哇,不是那么快。您可能需要查看下面我的答案中日期为7/10/17的缺点部分。
汤姆

如何datetime2比使用更少的存储空间datetime却又提供更大的范围和更高的精度?

106

我同意@marc_s和@Adam_Poward-DateTime2是前进的首选方法。它具有更宽的日期范围,更高的精度,并使用相等或更少的存储量(取决于精度)。

但是,讨论错过了一件事……
@Marc_s指出:Both types map to System.DateTime in .NET - no difference there。这是正确的,但是,相反的说法不正确 ……它在进行日期范围搜索时很重要(例如“查找我在2010年5月5日修改的所有记录”)。

.NET的版本 Datetime具有与相似的范围和精度DateTime2。当映射.NET Datetime下到老SQL DateTime出现隐含四舍五入。旧的SQL DateTime精确到3毫秒。这意味着您将11:59:59.997尽可能接近一天的尽头。较高的取整至第二天。

尝试这个 :

declare @d1 datetime   = '5/5/2010 23:59:59.999'
declare @d2 datetime2  = '5/5/2010 23:59:59.999'
declare @d3 datetime   = '5/5/2010 23:59:59.997'
select @d1 as 'IAmMay6BecauseOfRounding', @d2 'May5', @d3 'StillMay5Because2msEarlier'

避免这种隐式舍入是移至DateTime2的重要原因。日期的隐式舍入显然会引起混乱:


13
您也可以通过不尝试寻找一天的“终点”来避免这种舍入。> = 5月5日且<5月6日更安全,并且可以在任何日期/时间类型上使用(当然,除了TIME)。还建议避免使用区域性,模棱两可的格式,例如m / d / yyyy。
亚伦·贝特朗

2
@AaronBertrand-完全同意,但是从问题的数量来看,这似乎值得描述。
EBarr 2014年

1
您为什么从切换201005055/5/2010?前一种格式适用于SQL Server中的任何区域。后者将中断:SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015')糟糕:2015-07-01 00:00:00.000
ErikE 2015年

1
@EBarr:对。“ DateTime2是向前发展的首选方法。日期范围更广,精度更高,并且使用相等或更少的存储空间(取决于精度):我非常不同意。请参阅下面我的答案的日期为7/10/17的“缺点”部分简而言之,这些好处可能是不需要的(工程/科学应用程序之外的),因此不值得失去更可能需要的好处,隐式/显式转换为浮点数字(天数,含+,-和平均数(如果适用,自最小日期时间以来的分数)
汤姆(Tom)

20

几乎所有的答案和评论都对赞成者表示沉重而对缺点表示反对。这是迄今为止所有优点和缺点的概述,以及一些关键的缺点(在下面的第2条中),我只看到过一次或根本没有提到过。

  1. 优点:

1.1。更加符合ISO标准(ISO 8601)(尽管我不知道这在实践中如何发挥作用)。

1.2。更大的范围(1/1/0001到12/31/9999与1/1 / 1753-12 / 31/9999)(尽管超出范围,除了ex。,在历史,天文,地质等应用中)。

1.3。完全匹配.NET的DateTimeType范围(尽管如果值在目标类型的范围和精度之内(除非下面的Con#2.1除外),则两者都可以进行无特殊编码的来回转换,否则会发生错误/舍入)。

1.4。更高的精度(100纳秒,也就是0.000,000,1秒与3.33毫秒,也就是0.003,33秒。)(尽管除了工程,科学应用程序外,可能不会使用额外的精度)。

1.5。当配置为类似时(如Iman Abidi所称,在1毫秒中而不是“相同”(在3.33毫秒中)),精度为DateTime,使用的空间更少(7个字节对8个字节),但是,当然,您会失去精确度收益,尽管这可能是不必要的收益,但它可能是最受吹捧的两者之一(另一个是范围)。

  1. 缺点:

2.1。在将参数传递到.NET时SqlCommand,必须指定System.Data.SqlDbType.DateTime2是否要传递SQL Server DateTime范围和/或精度之外的值,因为它的默认值为System.Data.SqlDbType.DateTime

2.2。无法隐式/轻松地将其转换为浮点数字(自最小日期时间起的天数)值,以在SQL Server表达式中使用数字值和运算符对其执行以下操作:

2.2.1。添加或减去天数或部分天数。注意:DateAdd如果您需要考虑日期时间的多个(如果不是全部)部分,那么使用Function作为解决方法并非易事。

2.2.2。为了计算“年龄”,请取两个日期时间之间的差。注意:您不能简单地使用SQL Server的DateDiffFunction来代替,因为它不能age像大多数人期望的那样进行计算,如果两个日期时间恰好越过了指定单位的日历/时钟日期时间边界,即使很小的一部分该单位的时间,则返回的差值为该单位的1与0。例如,两个日期时间的DateDiffin Day仅相差1毫秒,如果这些日期时间为,则返回1与0(天)。在不同的日历日(即“ 1999-12-31 23:59:59.9999999”和“ 2000-01-01 00:00:00.0000000”)。如果移动了相同的1毫秒时差日期时间,以使它们不跨越日历日,则将返回以Day0的天数表示的“ DateDiff” 。

2.2.3。Avg首先简单地转换为“浮点数”,然后再转换为,即可获取日期时间(在汇总查询中)DateTime

注意:要转换DateTime2为数字,您必须执行类似以下公式的操作,该公式仍假定您的值不小于1970年(这意味着您将丢失所有额外范围以及另外217年。)注意:无法简单地调整公式以允许更大的范围,因为您可能会遇到数值溢出问题。

25567 + (DATEDIFF(SECOND, {d '1970-01-01'}, @Time) + DATEPART(nanosecond, @Time) / 1.0E + 9) / 86400.0–来源:“ https://siderite.dev/blog/how-to-translate-t-sql-datetime2-to.html

当然,您也Cast可以DateTime先(如果有必要,再返回DateTime2),但是您会失去DateTime2vs. 的精度和范围(都在1753年之前)带来的好处,DateTime这是最大的2个,同时也是这是最不可能需要的2个问题,当您失去对加法/减法/“年龄”(vs。DateDiff)/ Avg计算收益的隐式/简单转换为浮点数字(天数)时的收益时,为什么要使用它是一个很大的问题在我的经验中。

顺便说一句,Avg日期时间是(或至少应该是)一个重要的用例。a)除了用于获取使用日期时间(因为有一个通用的基准日期时间)来表示持续时间(平均惯例)时的平均持续时间外,b)获取平均日期的仪表板类型统计信息也很有用,时间位于行/行组的日期时间列中。c)一个标准(或至少应该是标准)临时查询,以监视/排除可能不再有效/不再有效和/或可能不建议使用的列中的值,该值是为每个值列出出现次数和(如果可用)的MinAvgMax与该值相关联的日期的时间标记。


1
像逆向观点一样,它指出了等式的c#面。与所有其他“优点”相结合,它将使人们能够根据自己的痛苦情况做出不错的选择。
EBarr '17

1
@EBarr:只有我的“'contrarian view'”中的Cons#1部分“指出了等式的c#面”。其余的(缺点#2.2.1-2.2.3),就像我说的那样,是更可能需要的好处DateTime,它们都与对SQL Server查询和语句的影响有关。
汤姆(Tom)

Re 2.2.1-对日期进行算术被认为是不安全的做法,首选的方法始终是使用DateAdd和相关函数。这是最佳做法。对日期进行算术有很大的责任,其中至少有一点是它不适用于大多数日期类型。几篇文章: sqlservercentral.com/blogs/... sqlblog.org/2011/09/20/...
RBerman

@RBerman:对。“不安全”:只有某些日期类型才是不安全的(例如DateTime2我已经提到的(由于溢出的可能性很高))。回覆。“不适用于大多数日期类型”:您只需要将它与一个日期类型一起使用,并且大多数应用程序中的大多数日期在整个生命周期中都可能永远不需要转换为另一种日期类型(也许,除了我也提到过) ,DateTime2DateTime(如做“的日期算术”; P)鉴于这种情况,这是不值得所有的额外不仅编程,而且还特设研究查询使用非算术友好日期类型的编码。
汤姆

15

如果您是Access开发人员,试图将Now()写入相关字段,则DateTime2将造成严重破坏。只需执行Access-> SQL 2008 R2迁移,它将所有datetime字段都放入DateTime2中。用Now()追加一条记录,以炸毁该值。在1/1/2012 2:53:04 PM可以,但在1/10/2012 2:53:04 PM可以。

一旦角色变得与众不同。希望它能帮助到别人。


15

这是一个示例,它将向您显示smalldatetime,datetime,datetime2(0)和datetime2(7)之间的存储大小(字节)和精度的差异:

DECLARE @temp TABLE (
    sdt smalldatetime,
    dt datetime,
    dt20 datetime2(0),
    dt27 datetime2(7)
)

INSERT @temp
SELECT getdate(),getdate(),getdate(),getdate()

SELECT sdt,DATALENGTH(sdt) as sdt_bytes,
    dt,DATALENGTH(dt) as dt_bytes,
    dt20,DATALENGTH(dt20) as dt20_bytes,
    dt27, DATALENGTH(dt27) as dt27_bytes FROM @temp

哪个返回

sdt                  sdt_bytes  dt                       dt_bytes  dt20                 dt20_bytes  dt27                         dt27_bytes
2015-09-11 11:26:00  4          2015-09-11 11:25:42.417  8         2015-09-11 11:25:42  6           2015-09-11 11:25:42.4170000  8

因此,如果我想将信息存储到秒(而不是毫秒),如果我使用datetime2(0)而不是datetime或datetime2(7),则每个字节可以节省2个字节。


10

虽然datetime2的精度提高了,但是某些客户端不支持datetimedatetime2,并迫使您转换为字符串文字。具体地说,Microsoft提到了这些数据类型的“下级” ODBC,OLE DB,JDBC和SqlClient问题,并且有一个图表显示了每种数据如何映射该类型。

如果值兼容性超过精度,请使用datetime


10

旧问题...但是我想添加一些此处尚未有人说过的内容...(注意:这是我自己的观察,所以请不要提供任何参考)

在过滤条件中使用Datetime2更快。

TLDR:

在SQL 2016中,我有一个包含十万行的表和一个datetime列ENTRY_TIME,因为它需要存储准确的时间(秒)。在执行具有许多联接和子查询的复杂查询时,当我将where子句用作:

WHERE ENTRY_TIME >= '2017-01-01 00:00:00' AND ENTRY_TIME < '2018-01-01 00:00:00'

最初有数百行时查询很好,但是当行数增加时,查询开始出现此错误:

Execution Timeout Expired. The timeout period elapsed prior
to completion of the operation or the server is not responding.

我删除了where子句,但意外地,查询在1秒钟内运行,尽管现在已提取了所有日期的所有行。我使用where子句运行内部查询,这花费了85秒,而没有where子句则花费了0.01秒。

我在这里遇到过很多线程,因为日期时间过滤性能

我优化了查询。但是我得到的真正速度是通过将datetime列更改为datetime2。

现在,以前超时的同一查询只需不到一秒钟的时间。

干杯


9

使用非美国设置时,日期字符串对datetime和的解释datetime2也可以不同DATEFORMAT。例如

set dateformat dmy
declare @d datetime, @d2 datetime2
select @d = '2013-06-05', @d2 = '2013-06-05'
select @d, @d2

会传回2013-05-06(即5月6日)datetime和的2013-06-05(即6月5日)datetime2。但是,将dateformat设置为mdy,则将@d和都@d2返回2013-06-05

datetime行为似乎与MSDN文档SET DATEFORMAT状态不一致:某些字符串格式(例如ISO 8601)独立于DATEFORMAT设置进行解释。显然不是真的!

直到我对此感到吃惊,我一直认为yyyy-mm-dd,不管语言/区域设置如何,日期都将被正确处理。


2
不。对于ISO 8601,我认为您的意思是YYYYMMDD(无破折号)。SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;再用破折号再试一次。
2014年

1
该标准允许使用YYYY-MM-DD和YYYYMMDD格式表示日历日期。我认为MSDN应该更具体地说明ISO 8601规范的哪个子集是独立解释的!
理查德·福塞特

1
我知道,但是在SQL Server中只有无破折号语法是安全的。
亚伦·

6

根据本文,如果您希望使用DateTime2具有与DateTime相同的精度,则只需使用DateTime2(3)。这应该给您相同的精度,减少一个字节,并提供更大的范围。


需要明确的是,它与SQL datetime的精度相同,而不是.NET DateTime。
山姆·鲁比

没错,我认为每个人都会理解上下文,但是值得具体说明。
jKlaus

4

我只是偶然发现了另一个优点DATETIME2:它避免了Python adodbapi模块中的错误,如果datetime传递了标准库值(该DATETIME列的列值不为零),则该错误会爆炸,但是如果将列定义为,则效果很好DATETIME2


0
Select ValidUntil + 1
from Documents

上面的SQL不适用于DateTime2字段。它返回错误“操作数类型冲突:datetime2与int不兼容”

加1以获得第二天是开发人员多年来一直在处理日期的事情。现在,Microsoft有一个超级新的datetime2字段,无法处理此简单功能。

“让我们使用比旧类型更糟糕的新型”,我不这么认为!


2
就像这样,我们很清楚在这里datetimedatetime2SQL Server 2008中都引入了和数据类型。您还可以Operand type clash: date is incompatible with int从点号之后的date类型中获得数据。这三种数据类型都可以正常工作dateadd(dd, 1, ...)
AlwaysLearning

2
这还不清楚。我有一个带有日期时间字段的SQLServer 2005数据库。
Paul McCarthy

0

我认为这DATETIME2是更好的存储方式date,因为它比的效率更高DATETIME。在SQL Server 2008您可以使用的中DATETIME2,它存储日期和时间,bytes存储时间为6-8 ,精度为100 nanoseconds。因此,任何需要更高时间精度的人都会想要DATETIME2

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.