DATETIME2
的日期范围是从"0001/01/01"到"9999/12/31",而DATETIME
类型只支持1753年至9999年。
此外,如果需要的话,DATETIME2
在时间上可以更精确;DATETIME
只能精确到3 1/3毫秒,而DATETIME2
可以精确到100纳秒。
这两种类型在.NET中都映射到System.DateTime
- 没有区别。
如果可以选择,我建议尽可能使用DATETIME2
。我没有看到使用DATETIME
的任何好处(除了向后兼容性)- 这样你就不会遇到日期超出范围等问题。
另外:如果只需要日期(不包含时间部分),请使用DATE
- 它和DATETIME2
一样好,还可以节省空间!:-) 同样适用于只需要时间的情况 - 使用TIME
。这就是这些类型的用途!
Nullable<DateTime>
不就是为了这个吗? - ChrisWdatetime2在大多数方面都胜过(旧应用兼容性方面的不足)
请注意以下几点
图片来源: MCTS自学教程(考试70-432):Microsoft® SQL Server® 2008实现和维护 第3章:表 -> 第1课:创建表 -> 第66页
datetime2
真是太棒了(获胜者)。 - Pankaj Parkardatetime2
如何比datetime
占用更少的存储空间,同时提供更大的范围和更高的精度? - Daidatetime2(3)
,你可以为datetime
节省空间。如果你声明更高的精度(在datetime
中不是一个选项),你不会节省空间,但你会获得更高的精度。总之,简而言之,空间节省是为了相等的精度。 - ruffin这两种类型在.NET中都映射到System.DateTime - 没有区别
。这是正确的,然而,反过来就不成立...并且在进行日期范围搜索时很重要(例如,“找到所有在2010年5月5日修改的记录”)。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'
20100505
改成了 5/5/2010
?前者格式能在 SQL Server 的任何区域正常运行。后者将会出错:SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015')
出错了:2015-07-01 00:00:00.000
。 - ErikE几乎所有的回答和评论都强调了优点,而对缺点并未特别提及。以下是迄今为止所有优点和缺点的总结以及一些至关重要的缺点(在下面第2点中),我只见过一次或者根本没有提到。
1.1 更符合ISO标准(ISO 8601)(尽管我不知道这在实践中如何发挥作用)。
1.2 范围更大(1/1/0001到12/31/9999,与1/1/1753-12/31/9999相比)(尽管额外范围,即1753年之前的所有内容,可能不会被使用,除非在历史、天文、地质等应用程序中)。
1.3 正好匹配.NET的DateTime
类型的范围(尽管两者互相转换不需要特殊编码,如果值在目标类型的范围和精度内,除了第2个缺点1之外,否则会出现错误/舍入)。
1.4 更高的精度(100纳秒,即0.000,000,1秒,与3.33毫秒,即0.003,33秒相比)(尽管额外的精度可能不会被使用,除非在工程/科学应用程序中)。
1.5 当配置为与DateTime
类似的(例如1毫秒而不是Iman Abidi所声称的3.33毫秒)精度时,使用更少的空间(7 vs. 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。例如,在不同日历日期上(即“1999-12-31 23:59:59.9999999”和“2000-01-01 00:00:00.0000000”)只相差1毫秒的两个日期时间的DateDiff
以天为单位将返回1而不是0(天)。如果移动相同的1毫秒差异日期时间,使其不跨越日历日,则DateDiff
的天数为0(天)。
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”Cast
为 DateTime
(如果必要,再转回 DateTime2
),但这样就会失去精度和范围(1753 年之前的所有内容)与 DateTime
相比的好处,它们可能是两个最大的优点,同时也可能是最不可能需要的两个优点,这引出了一个问题:为什么要使用它,当您失去对浮点数数字(天数)进行加法/减法/"年龄"(与 DateDiff
不同)/ Avg
计算的隐式/简单转换的好处,这在我的经验中是一个很大的优势。顺便说一下,Avg
的日期时间(或者至少应该是)一个重要的用例。a)除了用于获取平均持续时间之外,当日期时间(自一个共同的基础日期时间)用于表示持续时间时(这是一个常见的做法),b)它还有用于在一组行的日期时间列中获取仪表板类型统计的平均日期时间。c)监视/故障排除列中可能永远无效/不再有效并且/或需要弃用的值的标准(或至少应该是标准)即为列出每个值的发生计数和(如果可用)关联该值的Min
、Avg
和Max
日期时间戳。
DateTime
的好处。 - TomDateTime2
类型,由于溢出的可能性较高)下不安全。关于“对大多数日期类型都不起作用”的问题:你只需要它能够适用于其中一个日期类型,在大多数应用程序中,大多数日期类型可能永远不需要转换为另一种日期类型(除了可能像我之前提到的那样,从 DateTime2
转换为 DateTime
(例如进行“日期算术” ;))。考虑到这一点,在编写非算术友好型日期类型的额外代码以及针对性的研究查询方面,这并不值得。 - Tom这里有一个示例,它将向您展示smalldatetime、datetime、datetime2(0)和datetime2(7)之间的存储大小(字节)和精度差异:
以下是示例中的查询:
CREATE TABLE #dateTable (
SmallDateTimeCol SMALLDATETIME,
DateTimeCol DATETIME,
DateTime2_0Col DATETIME2(0),
DateTime2_7Col DATETIME2(7)
);
INSERT INTO #dateTable VALUES ('2015-01-01 12:12:12', '2015-01-01 12:12:12', '2015-01-01 12:12:12', '2015-01-01 12:12:12');
SELECT
CONVERT(VARCHAR(24),SmallDateTimeCol,121) AS Smalldatetime
,CONVERT(VARCHAR(24),DateTimeCol,121) AS Datetime
,CONVERT(VARCHAR(24),DateTime2_0Col,121) AS 'Datetime2(0)'
,CONVERT(VARCHAR(24),DateTime2_7Col,121) AS 'Datetime2(7)'
,DATALENGTH(SmallDateTimeCol) AS 'SmallDateTime_Size'
,DATALENGTH(DateTimeCol) AS 'DateTime_Size'
,DATALENGTH(DateTime2_0Col) AS 'DateTime2_0_Size'
,DATALENGTH(DateTime2_7Col) AS 'DateTime2_7_Size'
FROM #dateTable;
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个字节。
如果你是Access开发人员,试图向相关字段写入Now()的话,DateTime2将会给你带来麻烦。我刚进行了一次Access -> SQL 2008 R2迁移,结果所有datetime字段都作为DateTime2放置。尝试使用Now()作为值添加记录时失败了。在2012年1月1日下午2:53:04是可以的,但在2012年1月10日下午2:53:04就不行了。
只有一个字符的区别。希望对某些人有所帮助。
关于一个老问题......但是我想补充一些其他人没有提到的内容......(注意:这是我的个人观察,所以不要要求任何参考资料)
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过滤性能的帖子
我稍微优化了查询。但真正加快速度的是将datetime列更改为datetime2。
现在之前超时的相同查询只需要不到一秒就能完成。
干杯
当使用非美国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
。
datetime
的行为似乎与SET DATEFORMAT
的MSDN文档相矛盾,后者指出:某些字符格式(例如ISO 8601)将独立于DATEFORMAT设置进行解释。显然这并不正确!
在遇到这个问题之前,我一直认为无论语言/区域设置如何,yyyy-mm-dd
日期都会得到正确处理。
SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;
请使用破折号再试一次。 - Aaron Bertrand