【问题标题】:DateTime2 vs DateTime in SQL ServerSQL Server 中的 DateTime2 与 DateTime
【发布时间】:2010-11-22 23:28:57
【问题描述】:

哪一个:

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

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

【问题讨论】:

    标签: sql sql-server tsql datetime datetime2


    【解决方案1】:

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

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

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

    【讨论】:

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

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

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

    请注意以下几点

    • 语法
      • datetime2[(小数秒精度=>查看存储大小下方)]
    • 精度、比例
      • 0 到 7 位,精度为 100ns。
      • 默认精度为 7 位。
    • 存储大小
      • 6 字节,精度小于 3;
      • 7 个字节用于精度 3 和 4。
      • 所有其他精度需要 8 个字节
    • DateTime2(3) 具有与 DateTime 相同的位数,但使用 7 字节存储而不是 8 字节 (SQLHINTS- DateTime Vs DateTime2)
    • datetime2(Transact-SQL MSDN article) 上查找更多信息

    图片来源: 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:哇,没那么快。您可能想查看我在 2017 年 7 月 10 日回答的缺点部分。
    • datetime2 如何比datetime 使用更少的存储空间,同时提供更大的范围和更高的精度?
    • @Dai 很确定答案解释了这一点。如果您使用datetime2(3),您可以为datetime 的模拟节省空间。如果您声明更高的精度(不是datetime 中的选项),您不会节省空间,但您确实获得了精度,natch。 TL;DR 节省空间是为了保证相同的精度。
    【解决方案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 的一个重要原因。日期的隐式四舍五入显然会导致混淆:

    【讨论】:

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

    几乎所有的答案和评论都重在优点,轻于缺点。这是迄今为止所有优点和缺点的回顾以及一些关键的缺点(在下面的#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。与 .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 字节),但那么当然,您将失去精度优势,这可能是最受吹捧的两个(另一个是范围)之一,尽管可能是不需要的优势)。

    1. 缺点:

    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 的差值。例如,Day 中的DateDiff 两个日期时间仅相隔 1 毫秒将返回 1如果这些日期时间在不同的日历日(即“1999-12-31 23:59:59.9999999”和“2000-01-01 00:00:00.0000000”),则为 0(天)。如果移动相同的 1 毫秒差异日期时间以使其不跨越日历日,则将在 Day 的 0(天)中返回“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

    当然,您也可以先将Cast 转换为DateTime(如有必要再返回DateTime2),但您将失去DateTime2 的精度和范围(所有在1753 年之前)优势与 DateTime 相比,它们是最大的 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 查询和语句的影响有关。
    • Re 2.2.1 -- 对日期进行算术运算被认为是不安全的做法,首选方法始终是使用 DateAdd 和相关函数。这是最佳实践。进行日期算术存在严重的责任,其中最重要的是它不适用于大多数日期类型。几篇:sqlservercentral.com/blogs/…sqlblog.org/2011/09/20/…
    • @RBerman:回复。 “不安全”:仅对某些日期类型不安全(例如我已经提到的DateTime2(由于溢出的可能性很高))。回覆。 “不适用于大多数日期类型”:您只需要使用它即可,并且大多数应用程序中的大多数日期可能永远不需要在其整个生命周期内转换为另一种日期类型(除了也许,就像我还提到的, DateTime2DateTime (例如做“日期算术”;P)。鉴于此,不值得在编程和临时研究查询中使用非算术友好日期进行所有额外编码类型。
    【解决方案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 迁移,并将所有日期时间字段作为 DateTime2 放入。用 Now() 附加一条记录作为被炸掉的值。 2012 年 1 月 1 日下午 2:53:04 可以,但 2012 年 1 月 10 日下午 2:53:04 不行。

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

      【讨论】:

        【解决方案8】:

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

        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。

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

        干杯

        【讨论】:

          【解决方案9】:

          当使用非美国 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 中,只有无破折号语法是安全的。
          【解决方案10】:

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

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

          【讨论】:

            【解决方案11】:

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

            【讨论】:

            • 明确地说,它与 SQL 日期时间的精度相同,而不是 .NET 日期时间。
            • 没错,我以为每个人都会理解上下文,但值得特别说明。
            【解决方案12】:

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

            【讨论】:

              【解决方案13】:

              正如其他答案显示的那样,推荐使用datetime2,因为它的尺寸更小,精度更高,但这里有一些关于why NOT to use datetime2 from Nikola Ilic 的想法:

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

              【讨论】:

                【解决方案14】:

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

                【讨论】:

                  【解决方案15】:

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

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

                  【讨论】:

                    【解决方案16】:
                    Select ValidUntil + 1
                    from Documents
                    

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

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

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

                    【讨论】:

                    • 我们很清楚,datetimedatetime2 数据类型都是在 SQL Server 2008 中引入的。您还可以从 date 类型中获得 Operand type clash: date is incompatible with int,该类型自此后一直存在天点。不过,所有三种数据类型都可以在 dateadd(dd, 1, ...) 上正常工作。
                    • 这个不清楚。我有一个 SQLServer 2005 数据库,其中包含一个日期时间字段。
                    猜你喜欢
                    • 1970-01-01
                    • 2016-11-30
                    • 1970-01-01
                    • 2019-10-07
                    • 2011-07-31
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-12-30
                    相关资源
                    最近更新 更多