ChatGPT解决这个技术问题 Extra ChatGPT

SQL Server 中的 DateTime2 与 DateTime

哪一个:

约会时间

日期时间2

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

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


E
Evan Carroll

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

对新工作使用时间、日期、日期时间 2 和日期时间偏移数据类型。这些类型符合 SQL 标准。它们更便携。 time、datetime2 和 datetimeoffset 提供更高的秒精度。 datetimeoffset 为全球部署的应用程序提供时区支持。

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


虽然使用 datetime2 提高了精度,但某些客户端不支持日期、时间或 datetime2 并强制您转换为字符串文字。如果您更关心兼容性而不是精度,请使用 datetime
另一种选择是使用索引视图,并将列转换为日期时间以实现兼容性。但是,您需要能够将应用程序指向视图。
使用 DATETIMEOFFSET 支持时区是用词不当。它只存储特定时刻的 UTC 偏移量,而不是时区。
@Porad:由于是“SQL 标准”,在实践中“更便携”的好处到底是什么?除了让你编写更多的代码,这些代码对于另一个 RDBMS 的“端口”来说可读性/可维护性显着降低在该代码的生命周期内可能永远不会发生。除了 Microsoft 提供的 SQL Server 工具和驱动程序(如果有的话)之外,是否有任何应用程序实际上依赖于 DateTime2 类型的特定位级表示(或任何其他 SQL Server 类型)?请参阅下面我的 2017 年 7 月 10 日答案中的缺点,了解我问的原因。
@Adam Porad:此外,所有这些好处可能都是不需要的(在工程或科学应用程序之外),因此不值得损失太多的好处,更可能需要:更容易(即使考虑变通方法)隐式/显式转换为的能力用于加法、减法、最小值、最大值和平均值的浮点数值(天数,如果适用,则为自最小日期时间以来的小数天数)。有关详细信息,请参阅下面我的 7/10/17 答案中的缺点。
M
M4N

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 类型中的值。
@marc_s - 这不是 null 的用途吗?
@JohnFX - 这里有点晚了 - 但你不会将日期时间设置为空。你会使用 Nullable 还是 datetime?它可以很好地处理 null - 并且在映射到 proc 时只需执行 param.value = someDateTime?? DBValue.Null 不幸的是,我们被一个带有数字的数据类型困住了——看起来很“通用”:)
大声笑,在我意识到这是我自己的评论(一年多前)之前,我只是试图对我自己的评论(上图)进行投票。我仍在处理 .NET 框架的愚蠢设计决策,即在作为 SqlParameters 传递时默认截断所有 DateTime 值,除非您将其显式设置为更精确的 SqlDbType.DateTime2。自动推断正确的类型就这么多。确实,他们应该使更改变得透明,替换不太精确、效率较低、范围有限的实现,并保留原始的“日期时间”类型名称。另请参阅stackoverflow.com/q/8421332/88409
@marc_s 这不是 Nullable<DateTime> 的用途吗?
I
Iman

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

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

https://i.stack.imgur.com/hNRsj.png

请注意以下几点

语法 datetime2[(小数秒精度=> 看下面的存储大小)]

datetime2[(小数秒精度=> 看下面的存储大小)]

精度,刻度为 0 到 7 位,精度为 100ns。默认精度为 7 位。

0到7位,精度100ns。

默认精度为 7 位。

存储大小 6 字节,精度小于 3;精度 3 和 4 需要 7 个字节。所有其他精度需要 8 个字节。

精度小于 3 时为 6 个字节;

精度 3 和 4 为 7 个字节。

所有其他精度需要 8 个字节。

DateTime2(3) 的位数与 DateTime 相同,但使用 7 个字节的存储空间而不是 8 个字节(SQLHINTS- DateTime Vs DateTime2)

在 datetime2(Transact-SQL MSDN 文章)上查找更多信息

图片来源:MCTS Self-Paced Training Kit (Exam 70-432): Microsoft® SQL Server® 2008 - Implementation and Maintenance 第 3 章:表格 ->第 1 课:创建表格 ->第 66 页


感谢您显示统计数据 +1,datetime2 很棒(获胜者)
@Iman Abidi:根据 Oskar Berggren 于 2014 年 9 月 10 日下午 3:51 对您引用的“SQLHINTS-DateTime Vs DateTime2”文章的评论:“datetime2(3) 与 datetime 不同。它们将具有相同的数字位数,但 datetime 的精度为 3.33ms,而 datetime2(3) 的精度为 1ms。"
@PankajParkar:哇,没那么快。您可能想查看下面日期为 7/10/17 的我的答复的缺点部分。
datetime2 如何比 datetime 使用更少的存储空间,同时提供更大的范围和更高的精度?
@Dai 很确定答案解释了这一点。如果您使用 datetime2(3),您可以为 datetime 的模拟节省空间。如果您声明更高的精度(不是 datetime 中的选项),您不会节省空间,但确实会获得精度,natch。 TL;DR 节省空间是为了保证相同的精度。
C
Community

我同意@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-Milliseconds!.aspx


您也可以通过不尝试找到一天的“结束”来避免这种舍入。 >= May 5 AND < May 6 更安全,适用于任何日期/时间类型(当然除了 TIME)。还建议避免使用区域性、模棱两可的格式,例如 m/d/yyyy。
@AaronBertrand - 完全同意,但看看我们有多少问题,这件事似乎值得描述。
为什么从 20100505 切换到 5/5/2010?前一种格式适用于 SQL Server 中的任何区域。后者将中断:SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015') 哎呀:2015-07-01 00:00:00.000
@EBarr:回复。 “DateTime2 是向前推进的首选方法。它具有更广泛的日期范围、更高的精度,并且使用相同或更少的存储空间(取决于精度”:我非常不同意。请参阅下面 2017 年 7 月 10 日我的回答的缺点部分. 简而言之,这些好处可能是不需要的(在工程/科学应用程序之外),因此不值得失去更多可能需要的好处,更容易(即使考虑变通方法)隐式/显式转换为浮点数字的能力( # 天数,包括如果应用,从最小日期时间开始的分数)+、- 和 avg 的值。
S
Siderite Zackwehdex

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

优点:

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

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

1.3.与 .NET 的 DateTime 类型的范围完全匹配(尽管如果值在目标类型的范围和精度内,则在没有特殊编码的情况下来回转换,但下面的 Con # 2.1 除外,否则将发生错误/舍入)。

1.4.更高的精度(100 纳秒又名 0.000,000,1 秒对 3.33 毫秒又名 0.003,33 秒)(尽管除了工程/科学应用程序之外,可能不会使用额外的精度)。

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

缺点:

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

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

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

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

2.2.3。通过简单地先转换为“Float”然后再转换回 DateTime 来获取日期时间的 Avg(在聚合查询中)。

注意:要将 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),但您会失去 DateTime2 与 { 2} 这是最大的 2 个,同时也是最不可能需要的 2 个,这就引出了一个问题,当您失去对浮点数字(天数)的隐式/简单转换以进行加法/减法 / 时,为什么要使用它“年龄”(相对于 DateDiff)/Avg 计算收益,在我的经验中这是一个很大的收益。

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


就像逆向观点一样 - 它指出了等式的 c# 方面。结合所有其他“专业人士”,它将允许人们根据他们想要承受的痛苦做出一个好的选择。
@EBarr:我的“'逆势观点'”中只有缺点#1部分“指出了等式的c#方面”。其余的(Cons #'s 2.2.1 - 2.2.3),就像我所说的(DateTime 的)更可能需要的好处,都与对 SQL Server 查询和语句的影响有关。
关于 2.2.1 -- 对日期进行算术运算被认为是不安全的做法,首选方法始终是使用 DateAdd 和相关函数。这是最佳实践。进行日期算术存在严重的责任,其中最重要的是它不适用于大多数日期类型。几篇文章:sqlservercentral.com/blogs/… sqlblog.org/2011/09/20/…
@RBerman:回复。 “不安全”:仅对某些日期类型不安全(例如我已经提到的 DateTime2(由于溢出的可能性很高))。回覆。 “不适用于大多数日期类型”:您只需要使用它即可,并且大多数应用程序中的大多数日期可能永远不需要在其整个生命周期内转换为另一种日期类型(除了也许,就像我还提到的, DateTime2DateTime (例如,做“日期算术”;P)。鉴于此,使用非算术友好日期不仅是编程的而且是临时研究查询中的所有额外编码都是不值得的类型。
A
Adam Porad

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


R
Rhett A Brown

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

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


R
Richard Fawcett

使用非美国 DATEFORMAT 设置时,将日期字符串解释为 datetimedatetime2 也可能不同。例如

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

datetime 行为似乎与 SET DATEFORMATMSDN documentation 不一致,其中指出:某些字符串格式(例如 ISO 8601)的解释独立于 DATEFORMAT 设置。显然不是真的!

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


没有。对于 ISO 8601,我认为您的意思是 YYYYMMDD(没有破折号)。 SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d; 用破折号再试一次。
该标准允许 YYYY-MM-DD 和 YYYYMMDD 格式用于日历日期表示。我认为 MSDN 应该更具体地说明 ISO 8601 规范的哪个子集是独立解释的!
我知道,但在 SQL Server 中,只有无破折号语法是安全的。
M
MJ Khan

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

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 filtering performance

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

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

干杯


F
FistOfFury

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

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


s
skrrgwasme

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


需要明确的是,它与 SQL 日期时间的精度相同,而不是 .NET 日期时间。
这是正确的,我假设每个人都会理解上下文,但值得特别说明。
B
Bob Kline

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


r
robotik

正如其他答案所示,由于尺寸更小且精度更高,建议使用 datetime2,但这里有一些关于 why NOT to use datetime2 from Nikola Ilic 的想法:

缺乏(简单)对日期进行基本数学运算的可能性,例如 GETDATE()+1

每次与 DATEADD 或 DATEDIFF 进行比较时,都会完成到日期时间的隐式数据转换

SQL Server 无法正确使用 Datetime2 列的统计信息,因为存储数据的方式会导致非最佳查询计划,从而降低性能


> “SQL Server 无法正确使用 Datetime2 列的统计信息,因为存储数据的方式会导致非最佳查询计划,从而降低性能”需要引用
@Milney 引用自提到的文章(最后第 3 段)-towardsdatascience.com/…
M
Muhammad Waheed

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


F
Feargal Kavanagh

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

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


P
Paul McCarthy
Select ValidUntil + 1
from Documents

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

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

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


在这里我们很清楚 datetimedatetime2 数据类型都是在 SQL Server 2008 中引入的。您还可以从自 dot 开始的 date 类型中获得 Operand type clash: date is incompatible with int。尽管如此,所有三种数据类型都可以与 dateadd(dd, 1, ...) 一起正常工作。
这还不清楚。我有一个 SQLServer 2005 数据库,其中包含一个日期时间字段。