我们可以通过两种方式存储日期和时间信息。存储DateTime信息的最佳方法是什么?
使用DateTime将日期和时间存储在2个单独的列中还是1个列中?
您能解释一下为什么这种方法更好吗?
(链接到MySQL文档以供参考,该问题是一般性的,不特定于MySQL)
日期和时间类型:日期和时间
date,time
,group by date
但不能使用索引进行排序。datetime
group by cast(datetime as date)
我们可以通过两种方式存储日期和时间信息。存储DateTime信息的最佳方法是什么?
使用DateTime将日期和时间存储在2个单独的列中还是1个列中?
您能解释一下为什么这种方法更好吗?
(链接到MySQL文档以供参考,该问题是一般性的,不特定于MySQL)
日期和时间类型:日期和时间
date,time
,group by date
但不能使用索引进行排序。datetime
group by cast(datetime as date)
Answers:
最好将数据存储在单列中,因为它们是密不可分的。时间点是一条信息,而不是两条。
许多产品在幕后使用的一种存储日期/时间数据的常用方法是将其转换为十进制值,其中“日期”是十进制值的整数部分,而“时间”是小数部分值。因此,将1900-01-01 00:00:00存储为0.0,并将2016年9月20日9:34:00存储为42631.39861。42631是自1900-01-01以来的天数。.39861是从午夜起经过的时间的一部分。不要直接使用小数类型来执行此操作,而应使用显式的日期/时间类型;我的观点只是一个例证。
将数据存储在两个单独的列中意味着您想在任何时候查看给定的时间点早于或晚于存储的值,就需要合并两个列的值。
如果单独存储这些值,则总是会遇到难以检测的“错误”。例如以下内容:
IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
dt_value DATETIME NOT NULL
, d_value DATE NOT NULL
, t_value TIME(0) NOT NULL
);
DECLARE @d DATETIME = '2016-09-20 09:34:00';
INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);
SET @d = '2016-09-20 11:34:00';
INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);
/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';
/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
AND dt.t_value >= CONVERT(TIME(0), '11:00:00');
在上面的代码中,我们正在创建一个测试表,用两个值填充它,然后针对该数据执行简单的查询。第一个SELECT
返回两行,但是第二个SELECT
仅返回单行,这可能不是期望的结果:
@ypercube在注释中指出,过滤值在离散列中的日期/时间范围的正确方法是:
WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
OR (
dt.d_value = CONVERT(DATE, '2016-07-01')
AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
)
如果出于分析目的需要将时间成分分开,则可以考虑为该值的时间部分添加一个已计算的,持久的列:
ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;
SELECT *
FROM #dt;
然后可以按一天中的时间对持久化的列进行索引,以进行快速排序等。
如果出于显示目的考虑将日期和时间分为两个字段,则应意识到格式化应在客户端而不是服务器上进行。
我将对其他答案提出不同意见。
如果同时需要日期和时间两个部分,即如果一个条目包含一个但不包含另一个,则该条目无效(或者一个条目为NULL,而另一个不包含NULL),则将其存储在单个列中是有道理的,因为其他给出的原因答案。
但是,一个或两个组件可能是单独可选的。在这种情况下,将其存储在单个列中将是不正确的。这样做将迫使您以任意方式表示NULL值,例如将时间存储为00:00:00。
以下是几个示例:
您正在记录车辆旅程以扣除里程税。知道行程的确切时间会很有用,但是如果员工没有记下来并且忘记了,则日期仍应自己记录(必需日期,可选时间)。
您正在进行一项调查,以了解人们什么时候吃午餐,并要求参与者填写午餐时间(包括日期)样本的表格。有些人不愿意填写日期,并且您不想丢弃数据,因为这是您真正关心的时间(可选日期,所需时间)。
有关替代方法,请参见此相关问题。
除非有特定的业务/应用程序需求,否则我总是喜欢将其存储为单个列。以下是我的观点-
在SQL Server中,最好将DataTime存储为一个字段。如果在DataTime列上创建索引,则可以将其用作日期搜索和日期时间搜索。因此,如果需要限制在特定日期存在的所有记录,则仍可以使用索引而不必执行任何特殊操作。如果您需要查询时间部分,则将无法使用相同的索引,因此,如果您有一个业务案例,其中您比日期时间更关心一天中的时间,则应将其分开存储,因为您需要创建它索引并提高性能。
确实,可惜没有标准的跨DBMS类型(例如INT和VARCHAR用于整数和字符串值)。到目前为止,我已经遇到的2种跨数据库方法是使用VARCHAR / CHAR列将DataTime值存储为根据ISO 8601(更方便,更易读)标准格式化的字符串,并使用BIGINT将它们存储为POSIX时间戳(存储更多)高效,更快,更易于数学操作)。
timestamp
这就是SQL标准定义的内容。将时间戳记存储为字符串是一个非常糟糕的建议
阅读了大量内容后,BIGINT中的UTC Unix时间似乎是最佳的解决方案。VARCHAR中的TZDB时间戳ID,用于存储时区(如果需要)。一些争论:
TIMESTAMP和DATETIME在后台执行了大量的花哨的转换,这些转换似乎很复杂且不清楚。服务器有时从本地时间切换到UTC,或者从服务器时间切换回时间。每个函数的一堆隐藏开销。
BIGINT(8kb)至少比xxxxxx.xxxxxx格式存储所需的DECIMAL轻或浅,MySQL实际将其存储为两个INT +。并且足以存储未来的几个世纪。
几乎所有主要的编程语言都具有标准函数库,可用于Unix时间。
使用BIGINT进行数学运算应该比在任何硬件上的其他任何事物都快或快。
当然,以上所有内容都与大型国际项目有关。对于较小的东西,使用所选框架的默认格式似乎已经足够了。
timestamp
列,(在数据库层)没有“花哨的转换”发生,并且timestamp with time zone
对此进行了充分的文档记录和解释(至少对于Oracle和Postgres而言)