SQL DateTime数据类型注意事项和限制

介绍 (Introduction)

In this article, we will explorethe SQL Date Time data type and its limitations.

在本文中,我们将探讨SQL日期时间数据类型及其限制。

As a DBA or SQL developer, we are used to dealing with data for many kinds of applications. In the life of a DBA, it’s common to collect values for miscellaneous aspects (like CPU, RAM or disk usage) and store these values into a dedicated table designed with a column that refers to the moment of collection.

作为DBA或SQL开发人员,我们习惯于处理许多应用程序的数据。 在DBA的一生中,通常会收集其他方面(例如CPU,RAM或磁盘使用情况)的值,并将这些值存储到专用表中,该表设计有引用收集时刻的列。

Here is an example of such a table:

这是此类表格的示例:

CollectionId CollectionTime CPU Usage RAM Usage Disk Reads Disk Writes Disk I/O

CollectionId 收集时间 CPU使用率 RAM使用 磁盘读取 磁盘写入 磁盘I / O

These timed collections provide the ability to create a set of charts with X axis as time and Y axis as the collected value or an aggregation of these values. Based on the frequency of collection, we might need to keep track of either the date only (for daily collection) or a more complete format till millisecond (or lower) because we would want to correlate that information with data from another collection.

这些定时集合提供了创建一组图表的功能,这些图表的X轴为时间,Y轴为收集的值或这些值的集合。 根据收集的频率,我们可能需要跟踪仅日期(对于每日收集)或更完整的格式,直到毫秒(或更短),因为我们希望将该信息与另一个收集的数据相关联。

So, based on the aim of the process, we have different date storage needs. That’s the reason why Microsoft provides different time data types that we can CONVERT to a string data type like VARCHAR in order to store or display it in a readable format.

因此,根据过程的目的,我们有不同的日期存储需求。 这就是为什么Microsoft提供不同的时间数据类型,我们可以将其转换为VARCHAR之类的字符串数据类型,以便以可读格式存储或显示它的原因。

You will find below a list of those data types:

您将在下面找到这些数据类型的列表:

Data type Comments
Time Only keeps track of nanoseconds in a day. There is no reference to a date.
Is stored using between 3 and 5 bytes.
Date Only keeps track of days, starting 01/01/01 to 31/12/9999.
Takes 3 bytes for storage.
SmallDateTime Can be used to store dates between 01/01/1900 and 06/06/2079.
Also allow to keep track of time information till seconds.
Takes 4 bytes for storage.
DateTime Is used to store date and time between 01/01/1753 to 31/12/9999.
Minimum unit of time it keeps is milliseconds with an accuracy of 3.33 ms.
Takes 8 bytes for storage.
DateTime2 Is the most complete data type that is a real combination of Date and Time data types.
For this reason, it takes between 6 and 8 bytes for storage.
DateTimeOffset Can be seen as a DateTime2 data type, but that takes timezone into account.
数据类型 注释
时间 每天仅记录纳秒。 没有提及日期。
使用3到5个字节存储。
日期 仅跟踪从01/01/01到31/12/9999的日期。
占用3个字节进行存储。
SmallDateTime 可用于存储01/01/1900和06/06/2079之间的日期。
也允许跟踪时间信息直到几秒钟。
占用4个字节进行存储。
约会时间 用于存储01/01/1753至31/12/9999之间的日期和时间。
它保持的最小时间单位为毫秒,精度为3.33毫秒。
占用8个字节进行存储。
日期时间2 是最完整的数据类型,是日期和时间数据类型的真实组合。
因此,它需要6到8个字节来存储。
DateTimeOffset 可以看作是DateTime2数据类型,但这要考虑时区。

So, as you can see above, Microsoft offers many possibilities and depending on our needs, we might use one in preference to the others.

因此,正如您在上面看到的那样,Microsoft提供了许多可能性,并且根据我们的需求,我们可能会优先使用一种。

Most of us would choose SQL DateTime as a suitable candidate because we might think that keeping down to the millisecond is enough for our collections. Those who step to that conclusion and don’t go to check its documentation page on Microsoft’s website won’t stand a chance to read following warning:

我们大多数人会选择SQL DateTime作为合适的候选者,因为我们可能认为对于我们的集合而言,毫秒级就足够了。 那些得出该结论并且不去查看Microsoft网站上的文档页面的人将没有机会阅读以下警告:

SQL DateTime data type

So, while this data type is still available, it can be considered as depreciated and other data types should be preferred to that one.

因此,尽管此数据类型仍然可用,但可以将其视为已折旧,并且应优先选择其他数据类型。

Despite this warning, SQL DateTime data type seems to be (still) very often used by developers, even in newer applications, scripts or stored procedures (and I was not apart from them). The only reason that could sound a good excuse for this is to keep compatibility with SQL Server 2005 but, except in some situations (migration tool), it’s a false good reason because SQL Server 2005 is not supported anymore by Microsoft. Attentive guys would also notice that Microsoft even keeps using this data type as input to its built-in functions as DATEADD or DATEPART.

尽管有此警告,但开发人员似乎仍然经常使用SQL DateTime数据类型,即使是在较新的应用程序,脚本或存储过程中(我也不例外)。 听起来不错的借口的唯一原因是保持与SQL Server 2005的兼容性,但是,在某些情况下(迁移工具),这是一个错误的好的理由,因为Microsoft不再支持SQL Server 2005。 细心的人还会注意到,Microsoft甚至继续使用此数据类型作为其内置函数DATEADDDATEPART的输入

I agree that it is not because Microsoft fires a note telling to use another data type (and does not so) that we might follow his advises but my recent experience made me write this article to provide other reasons, so that every reader actually understand the fundamental reasons why Microsoft almost depreciated SQL DateTime data type.

我同意这不是因为Microsoft发出了一条纸条,告诉我们使用另一种数据类型(并非如此),我们可能会按照他的建议进行操作,但是我最近的经历使我写这篇文章是为了提供其他原因,以便每个读者都能真正理解Microsoft几乎折旧SQL DateTime数据类型的根本原因。

Actually, if you look closely at DateTime definition, you see an accuracy of 0.003 seconds. While this simple statement does not seem harmful, it can cause you headaches during development when you your application, script or stored procedure tackles the field of milliseconds…

实际上,如果仔细查看DateTime定义,则可以看到0.003秒的精度。 虽然这个简单的语句似乎没有什么害处,但是当您的应用程序,脚本或存储过程处理毫秒级的时间时,它可能会在开发过程中引起您的头痛……

This article will present some limitations that can be encountered when using DateTime data type and we will even see a trial to bypass these limitations that finally leads to the same conclusion: DateTime should be avoided in new developments.

本文将介绍使用DateTime数据类型时可能遇到的一些限制,我们甚至会看到绕过这些限制的试验,最终得出了相同的结论:在新的开发中应避免使用DateTime。

In the following sections, we will first review the definition of SQL DateTime data type and how it’s stored internally. Then we will present the situation that made me willing to share my experience and so write this article and review the steps I made to come to the final conclusion to change to DateTime2 data type.

在以下各节中,我们将首先回顾SQL DateTime数据类型的定义及其在内部的存储方式。 然后,我们将介绍使我愿意分享经验的情况,因此写这篇文章并回顾为得出DateTime2数据类型的最终结论而采取的步骤。

SQL DateTime数据类型注意事项 (SQL DateTime data type considerations)

Definition and storage overview

定义和存储概述

If we go on technet, Microsoft defines it as a date combined with a time of day with fractional seconds that is based on a 24-hour clock.

如果我们继续使用technet, Microsoft会将其定义为日期和一天中的时间(以24小时制为基础的小数秒)相结合

As explained above, this data type allows us to store dates from 01/01/1753 to 31/12/9999 and time information from 00:00:00.000 to 23:59:59.997. It’s internally represented as a 8 bytes value where first four bytes are used to represent the date part while last four are there for time part. So, we can see this representation as a floating-point value.

如上所述,该数据类型使我们可以存储从01/01/1753到31/12/9999的日期和从00:00:00.000到23:59:59.997的时间信息。 它内部表示为8个字节的值,其中前四个字节用于表示日期部分,后四个字节用于表示时间部分。 因此,我们可以将此表示形式视为浮点值。

DataTime overview and internal storage

We could think that Microsoft stores date and time as two concatenated integer from a particular value. We could expect it’s not the case as that 3 milliseconds accuracy wouldn’t even exist as an the maximum value of an integer is 2 147 483 647, which is far bigger than the number of milliseconds in a day is 1000*60*60*24 = 86 400 000.

我们可以认为Microsoft将日期和时间存储为来自特定值的两个串联的整数。 我们可以预料不是这样,因为整数的最大值是2147483647,这甚至不存在3毫秒的精度,这远远大于一天中的毫秒数1000 * 60 * 60 * 24 = 8640万。

Anyway, the better way to know is to test and check. To do so, we will use CONVERT built-in function extensively.

无论如何,更好的了解方法是测试和检查。 为此,我们将广泛使用CONVERT内置函数。

Storage of the Date part of a SQL DateTime

存储SQL DateTime的Date部分

First, we will check the way SQL Server stores the date part of a SQL DateTime.

首先,我们将检查SQL Server存储SQL DateTime的日期部分的方式。

Here is a sample result when running the following query. Instead of using GETDATE() function, I preferred taking a fixed value to be sure to run following statements and compare results.

这是运行以下查询时的示例结果。 我更喜欢采用固定值来确保运行以下语句并比较结果,而不是使用GETDATE()函数。

 
DECLARE @dt DATETIME = '2017-05-01 15:09:26.128'
SELECT CONVERT(VARBINARY(8), @dt);
 

This statement will give you back a 0x0000A76600F9C8AE value.

该语句将为您返回0x0000A76600F9C8AE值。

If we take up only the date value, with following query :

如果我们仅使用日期值,则使用以下查询:

 
DECLARE @dt DATETIME = '2017-05-01 15:09:26.128'
SELECT SUBSTRING(CONVERT(VARBINARY(8), @dt), 1, 4);
 

This query gives back the first 4 bytes of previous value: 0x0000A766. We deduct that 00F9C8AE is the time value for 15:09:26.128.

该查询返回先前值的前4个字节: 0x0000A766 。 我们减去00F9C8AE15:09: 26.128的时间值。

Now, what’s the corresponding integer value of the first four bytes?

现在,前四个字节对应的整数值是多少?

 
DECLARE @b2i VARBINARY(8);
SET @b2i = 0x0000A766;
SELECT CONVERT(INT,@b2i)
 

And the value is 42854. Would this possibly be the number of days since 01/01/1900?

值是42854。这可能是自1900年1月1日以来的天数吗?

 
DECLARE @dt DATETIME = '2017-05-01 15:09:26.128'
SELECT DATEDIFF(DAY,@dt,'1900-01-01 00:00:00.000')
 

Running previous query will provide a YES answer.

运行先前的查询将提供是的答案。

There is one last question for this tour: does a 01/01/1900 value for a SQL DateTime really imply a value of 0 in its storage?

此游览还有最后一个问题:SQL DateTime的01/01/1900值是否真的意味着其存储中的值为0?

If we run following query, we will get back a value of 0x0000.

如果运行以下查询,则将返回值0x0000。

 
DECLARE @dt DATETIME = '1900-01-01 15:09:26.128'
SELECT SUBSTRING(CONVERT(VARBINARY(8), @dt), 1, 4);
 

Now, we can conclude that, actually, the date part of a DateTime is a signed integer from 01/01/1900.

现在,我们可以得出结论,实际上,DateTime的日期部分是从01/01/1900开始的带符号整数。

Storage of the time part of a SQL DateTime

存储SQL DateTime的时间部分

Now, we will consider how SQL Server handles the time part of a DateTime. We will modify the second query in previous subsection and proceed the same way.

现在,我们将考虑SQL Server如何处理DateTime的时间部分。 我们将修改前面小节中的第二个查询,并以相同的方式进行。

 
DECLARE @dt DATETIME = '2017-05-01 15:09:26.128'
SELECT SUBSTRING(CONVERT(VARBINARY(8), @dt), 5, 4);
 

As expected, we get back following value: 0x00F9C8AE.

如预期的那样,我们返回以下值: 0x00F9C8AE

If we convert it to integer, what does it give?

如果将其转换为整数,它将产生什么?

 
DECLARE @b2i VARBINARY(8);
SET @b2i = 0x00F9C8AE;
SELECT CONVERT(INT,@b2i)
 

It returns 16369838. Well this number does not seem to be the number of seconds since 00:00:00.000 as:

返回16369838 。 那么这个数字似乎不是从00:00:00.000开始的秒数,因为:

  • 15 hours = 540 000 000 ms

    15小时= 540000000毫秒
  • 9 minutes = 540 000 ms

    9分钟= 540 000毫秒
  • 26 seconds = 26 000 ms

    26秒= 26000 ms
  • 128 milliseconds = 128 milliseconds

    128毫秒= 128毫秒
  • Total is : 54 566 128 milliseconds

    总计为:54566128毫秒

If we divide the total above by 3.33, we will get a value near the one we got back from the last query. So, for an obscure reason, SQL Server « counts » by 3.33 milliseconds for DateTimes.

如果我们将上述总和除以3.33,我们将获得与上一个查询返回的值相近的值。 因此,出于一个晦涩的原因,SQL Server的“日期时间”“计数”为3.33毫秒。

SQL DateTime限制示例 (SQL DateTime limitations by example)

From now on, we will focus on practical cases where DateTime is a limitation and should absolutely be avoided.

从现在开始,我们将重点关注DateTime是局限性且应绝对避免使用的实际情况。

First, let’s present the example that drove me to write this article.

首先,让我们给出促使我撰写本文的示例。

My plan was to take back contents of a data source (log file, SQL Server Error Log, Extended Events…) and load it into a SQL Server table. When it would be done, I could generate reports on these data and even correlate each record with the results of the importation of another data source.

我的计划是取回数据源的内容(日志文件,SQL Server错误日志,扩展事件…)并将其加载到SQL Server表中。 完成后,我可以生成有关这些数据的报告,甚至可以将每个记录与另一个数据源的导入结果相关联。

SQL Datetime limitations

This process had to be designed so that it could run regularly. To do so, it would store timestamp value of last imported record at the end of execution. Once another run is launched, it would take back this timestamp and filter out data from its data source so that it only takes care of records after that timestamp.

必须设计此过程,使其可以正常运行。 为此,它将在执行结束时存储最后导入的记录的时间戳值。 一旦启动另一个运行,它将收回该时间戳并从其数据源中过滤掉数据,以便仅在该时间戳之后处理记录。

ETL process

This design is quite simple actually and, I’m pretty sure, is intensely used as it’s the basics of an incremental ETL process.

实际上,这种设计非常简单,并且我敢肯定,由于它是增量ETL流程的基础,因此得到了广泛使用。

Now, let’s review some of the limitations that I faced.

现在,让我们回顾一下我面临的一些限制。

SQL DateTime transformation: Data alteration

SQL DateTime转换:数据更改

The contents of the log was prepended with timing information with a millisecond precision. Even aware of the advice of Microsoft, I naively chose to ignore it and took the DateTime data type to store the information. But thanks to the accuracy, some of my data was different from the source…

日志的内容前面带有毫秒精度的计时信息。 即使知道Microsoft的建议,我还是天真地选择忽略它,而是使用DateTime数据类型来存储信息。 但是由于准确性,我的一些数据与原始数据有所不同……

You will find below a simple query that simulates the outcome of a load. We first define some string values representing a DateTime in milliseconds, using Dates CTE, and use this CTE to convert these string values to its DateTime data type version. It also compares source and generated DateTimes to provide either « success » or « data loss » outcome.

您将在下面找到一个模拟负载结果的简单查询。 我们首先使用Dates CTE定义一些表示DateTime的字符串值(以毫秒为单位),然后使用此CTE将这些字符串值转换为其DateTime数据类型版本。 它还比较源和生成的DateTime,以提供“成功”或“数据丢失”结果。

 
WITH Dates( 
  DateStr
)
AS (
    SELECT 
        '2017-05-01 14:12:59.150'
UNION ALL 
SELECT
  '2017-05-01 15:00:01.171'
UNION ALL 
SELECT
  '2017-05-01 15:00:01.172'
UNION ALL 
SELECT
  '2017-05-01 15:10:15.183'
UNION ALL 
SELECT
  '2017-05-01 16:28:37.176'
) 
SELECT 
DateStr as SourceVal,
CONVERT(DATETIME,DateStr,121) as StoredVal,
CASE 
  WHEN DateStr = CONVERT(VARCHAR(32),(CONVERT(DATETIME,DateStr,121)),121)  THEN 'Success'
  ELSE 'Data Loss!'
END as LoadOutcome
FROM Dates
 

Here is a screen capture of query results :

这是查询结果的屏幕截图:

Query results

It’s a 2/5 success for this simple example… Why?

这个简单的例子成功了2/5 ...为什么?

Because of this 3.33 accuracy that actually leads to round values and only keep track of 3 values in milliseconds: 0, 3 and 7.

由于这种3.33的精度实际上会导致取整值,因此只能以毫秒为单位跟踪3个值:0、3和7。

Incremental load: False comparison results

增量负载:错误的比较结果

For the same reason as above, comparing two SQL DateTimes with a precision to the milliseconds is not acceptable as you can see with the results of following query:

出于与上述相同的原因,将两个SQL DateTime的精度与毫秒进行比较是不可接受的,您可以从以下查询的结果中看到:

 
select 
'2017-05-01 15:00:01.171',
'2017-05-01 15:00:01.170',
CASE WHEN 
  CONVERT(DATETIME,'2017-05-01 15:00:01.171',121) 
    <= CONVERT(DATETIME,'2017-05-01 15:00:01.170',121)
  THEN 'LOWER OR EQUAL'
  ELSE 'BIGGER'
END;
 

The results:

结果:

Query output

Workarounds won’t work

解决方法不起作用

When we face a problem, it’s common to keep trying to make it work. We can be very inventive and we could imagine different solutions.

当我们遇到问题时,通常会不断尝试使其正常工作。 我们可以发挥创造力,可以想象出不同的解决方案。

When I faced this SQL DateTime problem, I figured out that comparison was not reliable when there was only one millisecond that separates two DateTimes. So I tried to run following statement:

当我遇到这个SQL DateTime问题时,我发现当只有两个毫秒的时间分隔两个DateTime时,比较是不可靠的。 所以我尝试运行以下语句:

 
DATEADD(MILLISECOND,YourDateTime,1)
 

As I so it did not give me the expected results, I even tried to manipulate a string version to add 1 millisecond with following code then compare.

由于我没有得到预期的结果,因此我什至尝试使用以下代码来操纵字符串版本以增加1毫秒,然后进行比较。

 
DECLARE @TmpDateTime DATETIME;
DECLARE @LastExecTimeStr VARCHAR(MAX);
DECLARE @TmpCnt BIGINT;
DECLARE @StoredDT DATETIME;
 
SET @TmpDateTime        = GETDATE()
SET @LastExecTimeStr    = CONVERT(VARCHAR(32),@TmpDateTime,120);
SET @TmpCnt             = CONVERT(INT,SUBSTRING(CONVERT(VARCHAR(32),@TmpDateTime,121),LEN(@LastExecTimeStr + '.')+1,3)) + 1;
SET @LastExecTimeStr    = @LastExecTimeStr + '.' + CONVERT(CHAR(3),@TmpCnt);
SET @StoredDT           = CONVERT(DATETIME,@LastExecTimeStr,121);
 
SELECT @TmpDateTime, @LastExecTimeStr,@StoredDT
 

Obviously, no matter the solution we can imagine, this would not work due to limitations and I came to conclusion that I had to read documentation.

显然,无论我们能想到什么解决方案,由于局限性,这都行不通,我得出的结论是我必须阅读文档。

结论 (Conclusion)

It’s now time to conclude. In this article, we’ve seen that Microsoft provides several data types to store date and time information. Among them, there is SQL DateTime data type. This data type is extensively used, even inside SQL Server, but it has by design some limitations that everyone need to be well aware of.

现在该总结了。 在本文中,我们已经看到Microsoft提供了几种数据类型来存储日期和时间信息。 其中,有SQL DateTime数据类型。 即使在SQL Server内部,此数据类型也得到了广泛使用,但是从设计上来说,它具有一些局限性,每个人都必须清楚。

Due to that limitation, when we need a precision lower than seconds, it’s an absolute necessity to use DateTime2 over DateTime data type.

由于该限制,当我们需要低于秒的精度时,绝对有必要在DateTime数据类型上使用DateTime2。

看更多 (See more)

Consider these free tools for SQL Server that improve database developer productivity.

考虑使用这些免费SQL Server工具来提高数据库开发人员的生产力。

参考资料 (References)

翻译自: https://www.sqlshack.com/sql-server-datetime-data-type-considerations-and-limitations/

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值