哪一个:


datetime
datetime2

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

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

#1 楼

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


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


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

评论


虽然datetime2的精度提高了,但是某些客户端不支持date,time或datetime2并迫使您转换为字符串文字。如果您更关心兼容性而不是精度,请使用datetime

–FistOfFury
2014年6月25日20:49



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

– TamusJRoyce
17年5月8日在16:39

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

– Suncat2000
17年1月1日在12:51

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

–汤姆
17年7月10日在23:27

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

–汤姆
17年8月10日在23:59

#2 楼

DATETIME2的日期范围是“ 0001/01/01”到“ 9999/12/31”,而DATETIME类型仅支持1753-9999年。

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

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

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

另外:如果您只需要日期(无时间部分),请使用DATE-与DATETIME2一样好,并且还可以节省您的空间! :-)相同的只是时间-使用TIME。这就是这些类型的用途!

评论


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

– Triynko
2010-10-26 18:16



@marc_s-这不是null吗?

–JohnFx
2011年1月17日17:15

@JohnFX-有点晚了-但是您不会将datetime设置为null。您将使用Nullable 还是datetime?处理null就好了-在映射到proc时,只需执行param.value = someDateTime? DBValue.Null不幸的是,我们在数据类型后面附加了一个数字-看起来太“通用”了:)

–亚当·郁金香(Adam Tuliper)-MSFT
2011年6月1日18:25

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

– Triynko
2011-12-8 18:25



@marc_s Nullable 的目的不是吗?

– ChristW
15年1月6日在18:29

#3 楼

datetime2在大多数方面都具有优势,但(与旧应用程序兼容)


更大的值范围
更好的精度

更小的存储空间(如果用户指定了可选功能)




请注意以下几点


语法


> 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页

评论


感谢您为此显示统计信息+ 1,datetime2非常棒(获胜者)

–潘卡吉·帕卡(Pankaj Parkar)
16 Mar 25 '16 at 11:54

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

–汤姆
17年7月10日在23:41



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

–汤姆
17年7月10日在23:46



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

–代
19 Mar 27 '19在9:28

我遇到的一件事是,将DATETIME2(7)的MAX()转换为INT。因此,如果将一组行的原始值与该列的MAX进行比较,则在比较中仅能精确到分钟,而在寻找组中的最新条目时则无法获得预期的结果。那是...意想不到的。

–马克·弗里曼
11月12日19:48

#4 楼

我同意@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的重要原因。日期的隐式舍入显然会引起混乱:


SQL Server中奇怪的日期时间行为
http://bytes.com/topic/sql-server/answers/578416-weird- millisecond-part-datetime-data-sql-server-2000-a
SQL Server 2008和毫秒
http://improve.dk/archive/2011/06/16/getting-bit-by- datetime-rounding-or-why-235959-999-ltgt.aspx
http://milesquaretech.com/Blog/post/2011/09/12/DateTime-vs-DateTime2-SQL-is-Rounding-My -999毫秒!.aspx


评论


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

–亚伦·伯特兰(Aaron Bertrand)
2014年2月11日,19:55

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

– EBarr
14年2月13日在19:54

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

– ErikE
2015年9月23日在22:35



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

–汤姆
17年8月10日在23:52

#5 楼

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


PROS:

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

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

DateTime类型范围的范围(尽管值在目标类型的范围和精度之内(除非下面的Con#2.1除外,否则两者都可以使用特殊编码来回转换)。

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

1.5。如果将其配置为与DateTime相似(与1manisec不同,而不是Iman Abidi所称的“ 3.33 millisec”),则占用的空间更少(7个字节与8个字节),但是当然,您会丢失精确度好处,尽管可能是不必要的好处,但它可能是最受吹捧的两个好处之一(另一个是范围)。


缺点:

2.1。将参数传递给.NET SqlCommand时,如果您要传递的值可能超出SQL Server System.Data.SqlDbType.DateTime2的范围和/或精度,则必须指定DateTime,因为它的默认值为System.Data.SqlDbType.DateTime

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

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

2.2.2。为了计算“年龄”,请取两个日期时间之间的差。注意:您不能简单地改用SQL Server的DateDiff函数,因为它不能像大多数人期望的那样计算age,因为如果两个日期时间恰好跨越指定单位的日历/时钟日期时间边界,即使对于该单位的一小部分,它将返回该单位的1与0的差。例如,两个日期时间相隔仅1毫秒的DateDiff中的Day将返回1 vs. 0(天)日期时间在不同的日历天(即“ 1999-12-31 23:59:59.9999999”和“ 2000-01-01 00:00:00.0000000”)。如果移动了相同的1毫秒时差日期时间,以使它们不跨越日历日,则会在Day的0(天)中返回“ 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” />
当然,您也可以先从CastDateTime(并在必要时再次返回到DateTime2),但是您将失去DateTime2DateTime的精度和范围(都在1753年之前),这是q212079q的最大优势,也是同时产生最少的两个需求,这就是当您失去对加/减/“年龄”(与DateDiff)/ Avg进行的隐式/轻松转换为浮点数字(天数)的隐式/简易转换时为什么使用它的问题calcs收益对我来说是一个很大的经验。

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

评论


像逆向观点一样,它指出了等式的c#面。结合所有其他“优点”,人们将可以根据自己想承受的痛苦做出不错的选择。

– EBarr
17年8月11日15:47

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

–汤姆
17年8月12日在1:59

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

– RBerman
19年8月14日在15:06

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

–汤姆
19/12/11在18:20

#6 楼

下面的示例将向您显示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个字节。

#7 楼

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

角色一旦有所不同。希望对别人有帮助。

#8 楼

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

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设置时,将日期字符串对datetime2DATEFORMAT的解释也可能有所不同。例如,

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


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

datetime的行为似乎与SET DATEFORMAT的MSDN文档不一致,该文档指出:某些字符串格式(例如ISO 8601)被解释与DATEFORMAT设置无关。显然不是真的!

直到我被这咬伤为止,我一直以为yyyy-mm-dd日期将被正确处理,而不管语言/区域设置如何。

评论


不。对于ISO 8601,我认为您的意思是YYYYMMDD(无破折号)。设置语言法语; DECLARE @d DATETIME ='20130605'; SELECT @d;再用破折号再试一次。

–亚伦·伯特兰(Aaron Bertrand)
2014年2月11日19:57在

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

–理查德·福塞特(Richard Fawcett)
2014年2月11日在22:07

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

–亚伦·伯特兰(Aaron Bertrand)
2014年2月11日在22:17

#10 楼

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

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

#11 楼

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

评论


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

– Sam Rueby
15年11月13日在19:06

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

– jKlaus
15年11月17日在16:01

#12 楼

我只是偶然发现了DATETIME2的另一个优点:它避免了Python adodbapi模块中的错误,如果传递了标准库datetime值(对于DATETIME列具有非零微秒),则该错误会被炸毁,但是如果将该列定义为DATETIME2

#13 楼

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

#14 楼

Select ValidUntil + 1
from Documents


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

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

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

评论


就是这样,我们很清楚这里的datetime和datetime2数据类型都是在SQL Server 2008中引入的。您还会遇到Operand类型冲突:date与日期类型不兼容,因为int从日期点开始就存在。这三种数据类型都可以与dateadd(dd,1,...)一起很好地工作。

–始终学习
17年8月21日在4:35

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

– Paul McCarthy
17年8月21日在8:35