834

哪一个:

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

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

4

16 回答 16

709

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

对新工作 使用timedate和数据类型。这些类型符合 SQL 标准。它们更便携。 ,并 提供更多的秒精度。 为全球部署的应用程序提供时区支持。datetime2datetimeoffsettimedatetime2datetimeoffsetdatetimeoffset

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

于 2009-12-10T21:17:57.087 回答
548

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

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

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

如果你有选择,我建议DATETIME2尽可能使用。我没有看到使用的任何好处DATETIME(除了向后兼容性) - 你会遇到更少的麻烦(日期超出范围和这样的麻烦)。

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

于 2009-08-26T11:56:52.297 回答
234

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

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

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

请注意以下几点

  • 句法
    • 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 页

于 2012-09-11T06:52:33.820 回答
113

我同意@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发生隐式舍入。旧的 SQLDateTime精确到 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 的一个重要原因。日期的隐式四舍五入显然会导致混淆:

于 2012-03-08T18:00:51.077 回答
27

几乎所有的答案和评论都对优点很重,对缺点很轻。这是迄今为止所有优点和缺点的回顾以及一些关键的缺点(在下面的#2中),我只见过一次或根本没有提到过。

  1. 优点:

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

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

1.3. 完全匹配 .NETDateTime类型范围的范围(尽管如果值在目标类型的范围和精度内,则在没有特殊编码的情况下来回转换,除了下面的 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。将 Parameter 传递给 .NETSqlCommand时,您必须指定System.Data.SqlDbType.DateTime2是否可以传递 SQL ServerDateTime范围和/或精度之外的值,因为它默认为System.Data.SqlDbType.DateTime.

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

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

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

2.2.3。通过Avg简单地先转换为“Float”然后再转换回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 ”</p>

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

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

于 2017-07-10T19:00:32.237 回答
16

下面是一个示例,它将向您展示 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 个字节。

于 2015-09-11T17:29:53.293 回答
15

如果您是 Access 开发人员,尝试将 Now() 写入相关字段,则 DateTime2 会造成严重破坏。刚刚进行了 Access -> SQL 2008 R2 迁移,并将所有日期时间字段作为 DateTime2 放入。用 Now() 附加一条记录作为被炸掉的值。2012 年 1 月 1 日下午 2:53:04 可以,但 2012 年 1 月 10 日下午 2:53:04 不行。

一旦性格产生了影响。希望它可以帮助某人。

于 2012-02-13T19:52:43.347 回答
11

老问题......但我想在这里添加一些没有人说过的东西......(注意:这是我自己的观察,所以不要要求任何参考)

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。

现在,之前超时的相同查询只需要不到一秒的时间。

干杯

于 2018-02-09T05:08:27.687 回答
10

使用非美国设置时,日期字符串的解释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(即 5 月 6 日)datetime2013-06-05(即 6 月 5 日)datetime2。但是,dateformat设置为mdy, both@d@d2return 2013-06-05

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

在我被这个咬住之前,我一直认为yyyy-mm-dd日期会被正确处理,而不管语言/区域设置如何。

于 2013-06-21T13:11:19.763 回答
10

虽然datetime2提高了精度,但某些客户端不支持datetimedatetime2并强制您转换为字符串文字。具体来说,Microsoft 提到了与这些数据类型有关的“下层”ODBC、OLE DB、JDBC 和 SqlClient 问题,并有一张图表显示了每种数据类型如何映射。

如果重视兼容性而不是精度,请使用日期时间

于 2014-06-25T20:56:15.143 回答
6

根据这篇文章,如果您想使用 DateTime2 获得与 DateTime 相同的精度,您只需使用 DateTime2(3)。这应该为您提供相同的精度,占用更少的字节,并提供扩展的范围。

于 2014-08-18T17:48:04.597 回答
4

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

于 2017-11-04T15:31:26.223 回答
1

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

  • 缺乏(简单)对日期进行基本数学运算的可能性,例如GETDATE()+1
  • 每次与DATEADDor进行比较时DATEDIFF,都会将隐式数据转换为datetime
  • SQL Server 无法正确使用 Datetime2 列的统计信息,因为存储数据的方式会导致非最佳查询计划,从而降低性能
于 2021-06-22T10:14:01.347 回答
0

I think DATETIME2 is the better way to store the date, because it has more efficiency than the DATETIME. In SQL Server 2008 you can use DATETIME2, it stores a date and time, takes 6-8 bytes to store and has a precision of 100 nanoseconds. So anyone who needs greater time precision will want DATETIME2.

于 2013-11-20T09:06:54.133 回答
-1

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

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

于 2021-01-11T16:27:08.397 回答
-2
Select ValidUntil + 1
from Documents

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

加 1 得到第二天是开发人员多年来一直在做的事情。现在微软有一个超级新的 datetime2 字段不能处理这个简单的功能。

“就用这种比旧款还差的新型吧”,我不这么认为!

于 2017-05-25T09:17:38.337 回答