哪一个:

日期时间日期时间2

在SQL Server 2008+中存储日期和时间的建议方法是什么?

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


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。这就是这些类型的原因!


MSDN datetime文档建议使用datetime2。以下是他们的建议:

使用time、date、datetime2和新的datetimeoffset数据类型工作这些类型与SQL一致标准它们更便于携带。time、datetime2和datetimeoffset提供更多的秒精度。datetimeoffset提供时区支持全球部署应用。

datetime2具有更大的日期范围、更大的默认小数精度和可选的用户指定精度。还取决于用户指定的精度,它可能使用更少的存储空间。


如果您是Access开发人员,试图将Now()写入相关字段,则DateTime2会造成严重破坏。刚刚执行了Access->SQL 2008 R2迁移,它将所有日期时间字段作为DateTime2放入。当值爆炸时,用Now()追加一条记录。2012年1月1日下午2:53:04没事,但2012年10月1日凌晨2:53:44没有。

一旦性格改变了。希望它能帮助到某人。


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

然而,讨论中遗漏了一件事。。。@Marc_s状态:这两种类型都映射到.NET中的System.DateTime-没有区别。这是正确的,然而,相反的是不正确的。。。在进行日期范围搜索时很重要(例如“查找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-aSQL Server 2008和毫秒http://improve.dk/archive/2011/06/16/getting-bit-by-datetime-rounding-or-why-235959-999-ltgt.aspxhttp://milesquaretech.com/Blog/post/2011/09/12/DateTime-vs-DateTime2-SQL-is-Rounding-My-999-Milliseconds!.aspx公司


datetime2在除(旧应用兼容性)之外的大多数方面都获胜

较大的值范围更好的准确性较小的存储空间(如果指定了可选的用户指定精度)

请注意以下几点

语法datetime2[(小数秒精度=>低于存储大小)]精度,刻度0至7位,精度为100ns。默认精度为7位数。存储大小精度小于3时为6字节;精度3和4为7字节。所有其他精度要求8字节。DateTime2(3)的位数与DateTime相同,但使用7字节的存储,而不是8字节(SQLHINTS-DateTime Vs DateTime2)查找有关datetime2的详细信息(Transact-SQL MSDN文章)

图像来源:MCTS自学训练套件(考试70-432):Microsoft®SQL Server®2008-实施和维护第3章:表格->第1课:创建表格->第66页


使用非US DATEFORMAT设置时,将日期字符串解释为datetime和datetime2也可能不同。例如。

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

对于datetime,返回2013-05-06(即5月6日),对于datetime2,返回2013-06-05(即6月5日)。然而,当dateformat设置为mdy时,@d和@d2都返回2013-06-05。

日期时间行为似乎与MSDN的SETDATEFORMAT文档不一致,后者指出:某些字符串格式(例如ISO8601)的解释与DATEFORMAT设置无关。显然不是真的!

直到我被这个问题困扰,我一直认为yyyy-mm-dd日期会被正确处理,不管语言/语言环境设置如何。


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


虽然datetime2提高了精度,但有些客户端不支持date、time或datetime2,并强制您转换为字符串文本。微软特别提到了ODBC、OLE DB、JDBC和SqlClient这些数据类型的“底层”问题,并有一个图表显示了它们如何映射类型。

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


根据本文,如果您希望使用DateTime2获得与DateTime相同的精度,则只需使用DateTime1(3)。这将为您提供相同的精度,占用更少的字节,并提供扩展的范围。


下面的示例将向您展示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或datetime 2(7),我可以每个存储2个字节。


Select ValidUntil + 1
from Documents

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

添加1以获得第二天,这是开发人员多年来一直在使用日期的做法。现在微软有了一个超级新的datetime2字段,无法处理这个简单的功能。

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


几乎所有的答案和评论都是赞成者居多,反对者较少。这里是到目前为止所有优点和缺点的总结,以及一些关键缺点(在下面的#2中),我只见过一次或根本没有提到。

赞成的意见:

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

1.2.更多的范围(1/1/0001至12/31/9999与1/1/175-32/31/9999)(尽管1753年之前的额外范围可能不会被使用,但历史、天文、地质等应用程序除外)。

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

1.4更高的精度(100纳秒,即0.0000000,1秒,而3.33毫秒,即0.003,33秒)(尽管除了工程/科学应用程序外,可能不会使用额外的精度)。

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

欺骗:

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

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

2.2.1增加或减少天数或部分天数。注意:当您需要考虑日期时间的多个部分(如果不是全部的话)时,使用DateAdd函数作为一种解决方法并不简单。

2.2.2.为了计算“年龄”,取两个日期时间之间的差值。注意:您不能简单地使用SQL Server的DateDiff函数,因为它不会像大多数人预期的那样计算年龄,因为如果两个日期时间恰好跨越指定单位的日历/时钟日期时间边界,即使是该单位的一小部分,它也会返回该单位的1与0之间的差值。例如,如果两个日期时间在不同的日历日(即“1999-12-31 23:59:59.9999999”和“2000-01-01 00:00:00.0000000000”),则仅相隔1毫秒的日期时间的Day中的DateDiff将返回1与0(天)。如果移动相同的1毫秒差的日期时间,使其不跨越日历日,则Day中返回0(天”的“DateDiff”。

2.2.3.通过先转换为“浮点”,然后再转换为DateTime,获取日期时间的平均值(在聚合查询中)。

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

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

当然,您也可以先转换为DateTime(如果需要,再转换为DateTime2),但您会失去DateTime2与DateTime2相比的精度和范围(在1753年之前)的优势。DateTime是最大的2个,同时也是最不可能需要的2个。这就引出了这样一个问题:当您丢失了对浮点数字(天数)的隐式/简单转换时,为什么要使用它进行加法/减法/“年龄”(与DateDiff相比)/平均计算,这在我的经验中是一个很大的好处。

顺便说一句,日期时间的平均值是(或者至少应该是)一个重要的用例。a) 除了在使用日期时间(由于通用基准日期时间)表示持续时间(一种常见做法)时用于获取平均持续时间外,b)还可以获得一个仪表板类型的统计信息,该统计信息显示行范围/行组的日期时间列中的平均日期时间。c) 一个标准(或至少应该是标准)的特殊查询,用于监视/排除列中可能不再有效和/或可能需要弃用的值,即列出每个值的出现计数以及与该值关联的最小、平均和最大日期时间戳(如果可用)。


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


老问题。。。但我想补充一些这里没有人说过的话。。。(注:这是我自己的观察结果,所以不要要求任何参考)

在筛选条件中使用Datetime2更快。

TLDR:

在SQL 2016中,我有一个包含十万行和日期时间列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。

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

干杯


接受的答案很好,只需知道,如果您向前端发送DateTime2,它将四舍五入到正常的DateTime等效值。

这给我带来了一个问题,因为在我的解决方案中,我必须在重新提交时将发送的内容与数据库中的内容进行比较,而我的简单比较“==”不允许舍入。所以必须添加它。


正如其他答案所示,由于尺寸更小、精度更高,建议使用datetime2,但以下是Nikola Ilic关于为什么不使用datetime1的一些想法:

缺少(简单)对日期进行基本数学运算的可能性,如GETDATE()+1每次使用DATEADD或DATEDIFF进行比较时,都会完成到datetime的隐式数据转换SQL Server无法正确使用Datetime2列的统计信息,因为存储数据的方式会导致非最佳查询计划,从而降低性能


datetime2更好

日期时间范围:1753-01-01到9999-12-31,日期时间2范围:0001-01-01到9999-12/31datetime精度:0.0033秒,datetime2精度:100纳秒datetime获取8个字节,datetime2获取6到8个字节取决于精度(精度小于3时为6字节,精度3或4时为7字节,所有其他精度要求8字节,单击并查看下图)