在数据库中存储时间(hh:mm)的最佳方法


100

我想将时间存储在数据库表中,但只需要存储小时和分钟。我知道我可以只使用DATETIME并忽略日期的其他组成部分,但是在不存储超出我实际需要的更多信息的情况下,最好的方法是什么?


什么是sql server 2005 TIME?
Erran Morad 2014年

@BoratSagdiyev在SQL Server 2005中没有一个,这就是为什么我问这个问题。
Matthew Dresser 2014年

Answers:


135

您可以将其存储为午夜后分钟数的整数:

例如。

0 = 00:00 
60 = 01:00
252 = 04:12

但是,您需要编写一些代码来重新构造时间,但这并不容易。


8
然后使用DATEADD()获取实时数据。甚至smallint就足够了。
乔尔·科恩荷恩

36
到那里,这样做就可以了……在整数上的mins = dd%60和hours = dd / 60可以解决问题。
Osama Al-Maadeed,09年

2
这是一个伟大,简单而直截了当的想法。+1
whiskeysierra

7
一个小缺点是数据库缺乏可读性
Jowen

我们需要存储几天,几小时和几分钟的变体;SQL Server中的时间数据类型最多只能达到23:59:59,因此我们不能使用它。受您的回答启发,我们决定在几天内创建三个int列:小时:分钟,以提供最大的灵活性。谢谢!
matao 2015年


15

DATETIME开始DATETIME结束

我恳求您使用两个DATETIME值代替,它们标记为event_startevent_end之类的东西。

时间是一项复杂的业务

现在,世界上大多数国家正确或错误地采用了基于牙科的度量系统。总体而言,这是件好事,因为至少我们都可以同意,ag是毫升,是立方厘米。至少大约是这样。公制存在许多缺陷,但至少在国际上一直存在缺陷。

随着时间的流逝,我们有了;每秒1000毫秒,每秒60秒到一分钟,每小时60分钟到一小时,每天半天12个小时,每月大约30天,具体时间取决于所讨论的月份和年份,每个国家/地区与其他国家/地区的时间会有所不同,每个国家/地区的时间格式都有所不同。

有很多东西需要消化,但是对于一个如此复杂的场景来说,有一个简单的解决方案是不可能的。

可以切掉一些角落,但是有些地方最好不要

尽管此处的最高答案表明您可能在午夜之后存储了整整几分钟的时间似乎是完全合理的,但我已经学会了避免这样做。

实现两个DATETIME值的原因是为了提高准确性,分辨率和反馈。

当设计产生不良结果时,这些都非常方便。

我存储的数据是否超出要求?

最初看起来好像要存储比我需要的更多的信息,但是有充分的理由接受这一打击。

从长远来看,存储这些额外的信息几乎总会节省我的时间和精力,因为我不可避免地发现,当有人被告知某件事花了多长时间时,他们还会想知道事件的发生时间和地点。

这是一个巨大的星球

过去,我一直犯有无视我的星球之外还有其他国家的罪行。当时这似乎是个好主意,但这总是会导致问题,头痛和浪费时间。始终考虑所有时区。

C#

DateTime很好地呈现为C#中的字符串。ToString(string Format)方法紧凑且易于阅读。

例如

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

SQL服务器

同样,如果您要读取的数据库与应用程序界面是分开的,则dateTimes可以一目了然地读取,并且对它们执行计算非常简单。

例如

SELECT DATEDIFF(MINUTE, event_start, event_end)

ISO8601日期标准

如果使用SQLite,则没有此功能,因此应使用“文本”字段并将其以ISO8601格式存储,例如。

“ 2013-01-27T12:30:00 + 0000”

笔记:

  • 使用24小时制*

  • ISO8601的时间偏移量(或+0000)部分直接映射到GPS坐标的经度值(不考虑夏令时或全国范围)。

例如

TimeOffset=(±Longitude.24)/360 

...其中±是指东西方向。

因此,值得考虑是否值得将经度,纬度和高度与数据一起存储。这将因应用而异。

根据我的经验,始终需要存储完整的日期和时间,而不管我是否认为开始该项目的时间。ISO8601是一种很好的,面向未来的方法。

免费提供其他建议

同样值得将事件像链子一样组合在一起。例如,如果记录比赛,则整个事件可以按racer,race_circuit,circuit_checkpoints和circuit_laps分组。

以我的经验,确定谁存储记录也是明智的。可以是通过触发器填充的单独表,也可以是原始表中的其他列。

投入越多,得到的越多

我完全理解在空间上尽可能节约的愿望,但是我很少这样做,而以丢失信息为代价。

如标题所述,数据库的经验法则是,数据库只能告诉您所拥有的数据,而回溯历史数据并填补空白可能非常昂贵。

解决的办法是第一次使其正确。这当然说起来容易做起来难,但是现在您应该对有效的数据库设计有更深刻的了解,并且随后有很大的机会将它第一次正确设置。

您的初始设计越好,以后的维修费用就越少。

我只说所有这一切,因为如果我能时光倒流的话,那就是我到那里时会告诉自己的东西。


2
您的评论“ ISO8601的+0000部分直接映射到GPS坐标系中的纬度”是错误的。它代表了UTC
加里·沃克

10

只需存储一个常规日期时间,然后忽略其他所有内容即可。当您可以只加载日期时间时,为什么还要花额外的时间编写代码来加载一个int,对其进行处理并将其转换为datetime呢?


3
一个可能的原因可能是节省硬盘驱动器上的空间-一种DATETIME数据类型需要占用4个字节来存储,而SMALLINT例如,一个数据类型只需要占用其中的四分之一。如果只有几千行,没什么大不了,但是,如果像许多公司一样拥有数百万行,那么您节省的空间将是可观的。
谢里登

32
可能的话,但请考虑有12个人回答了这个问题,每个人都花了15分钟的时间进行思考。假设他们都是程序员,每小时赚50美元。考虑到仅花时间思考这个问题,您可能已经购买了闪亮的2TB新硬盘来存储多余的字节。
赛斯

如果我们仅谈论额外的字节数,@ Seth是正确的。但您的建议只能是具有1-2个开发人员的小型项目。但是这将是一个有许多开发人员(以及QA)的大型项目,当新程序员试图弄清楚这一点时,它将是一个大问题。las,我们都依赖于业务逻辑。
调解员2016年

使用云服务,在按读取/处理的字节数付费的情况下,节省磁盘空间非常重要。
RedShift

2
另一个有趣的问题是,如果您依赖时间组件,则在一年中的不同时间使用时,将不会考虑夏令时调整。
克里斯·塞弗特

4

因为您没有提到它(如果您使用的是SQL Server 2008),则可以使用时间数据类型,否则请使用自午夜以来的分钟


3

SQL Server实际上将时间存储为一天的一部分。例如,一整天=值1。12小时是0.5的值。

如果要在不使用DATETIME类型的情况下存储时间值,则以十进制形式存储时间将适合这种需要,同时还可以使转换为DATETIME变得简单。

例如:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

将值存储为DECIMAL(9,9)将占用5个字节。但是,如果精度不是最重要的,那么REAL将仅消耗4个字节。无论哪种情况,都可以很容易地根据数字值而不是数据/时间类型来计算汇总计算(即平均时间)。


“ SQL Server实际上将时间存储为一天的一部分。” 我认为它在前4个字节中存储自(或之前)1900年1月1日以来(或之前)的天数,在后4个字节中存储以毫秒为单位的时间。(SmallDateTime使用两个字节,每个日期范围更窄,并且使用分钟,而不是毫秒)
Kristen

我知道(肯定99.99%)在一天中的时间是零头。
graham.reeds 2010年

2

我将它们转换为整数(HH * 3600 + MM * 60),并以这种方式存储。较小的存​​储空间,仍然足够容易使用。


2

如果您使用的是MySQL,请使用TIME类型的字段以及TIME附带的相关功能。

00:00:00是标准的Unix时间格式。

如果您不得不手动查看和查看表,则整数可能比实际时间戳更加混乱。


1

试试smalldatetime。它可能无法满足您的需求,但可以帮助您满足将来在日期/时间操作方面的需求。


如果@mdresser使用的是<MSSQL 2005,则服务器将显示“超出范围的smalldatetime值”
Fergus

1

您确定只需要小时和分钟吗?如果您想对它做任何有意义的事情(例如,计算两个此类数据点之间的时间跨度),则不了解有关时区和DST的信息可能会得出错误的结果。时区可能不适用于您的情况,但DST无疑会适用。


1

我们将其存储为24小时制,而不是分钟过去的午夜,而是存储为SMALLINT。

09:12 = 912 14:15 = 1415

当转换回“可读格式”时,我们只需在右边插入两个冒号“:”。如果需要,请用零填充左键盘。每种方式保存数学,并使用更少的字节(与varchar相比),并强制将值设为数字(而不是字母数字)

虽然非常愚蠢...但应该已经在MS SQL中存在TIME数据类型很多年了...恕我直言...


克里斯汀(Kristen)绝对正确,但前提是她对小时和分钟的假设仅代表一个24小时。存储0..1439分钟需要10位。这意味着您可以随意使用其他6位,因此有创造力的空间。我建议使用5位来存储您所在时区的小时偏移量,但是仅当
问问

1

我想您需要的是一个变量,它将分钟数存储为数字。这可以通过不同类型的整数变量来完成:

SELECT 9823754987598 AS MinutesInput

然后,在您的程序中,您可以通过以下方式简单地以所需的形式查看此内容:

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

您要求使用效率时会出现问题。但是,如果您时间紧缺,则只需使用可为空的BigInt来存储您的分钟数。

值为null表示尚未记录时间。

现在,我将以往返于外层空间的形式进行解释。

不幸的是,一个表列将只存储一个类型。因此,您将需要根据需要为每种类型创建一个新表。

例如:

  • 如果MinutesInput = 0 .. 255,则使用TinyInt(如上所述进行转换)。

  • 如果MinutesInput = 256 .. 131071,则使用SmallInt(注意:SmallInt的最小值为-32,768。因此,在存储和检索值时取反并加32768,以在如上所述进行转换之前利用整个范围)。

  • 如果MinutesInput = 131072 .. 8589934591,则使用Int(注意:取反并根据需要添加2147483648)。

  • 如果MinutesInput = 8589934592 .. 36893488147419103231,则使用BigInt (注意:根据需要添加和否定9223372036854775808)。

  • 如果MinutesInput> 36893488147419103231,则我个人将使用VARCHAR(X)根据需要增加X,因为char是一个字节。我将不得不在稍后的日期重新讨论该答案以完整描述它(或者也许有一个stackoverflowee可以完成这个答案)。

因为每个值无疑都需要唯一的键,所以只有在存储的值的范围在很小(接近0分钟)和很大(大于8589934591)之间很好地混合时,数据库的效率才会显现出来。

在存储的值实际达到大于36893488147419103231的数值之前,您还可能只有一个BigInt列来表示分钟,因为您无需将Int浪费在唯一的标识符上,而将另一个Int浪费在存储分钟值上。


0

如克里斯汀建议的那样,以UTC格式节省时间可以更好地帮助您。

确保您使用的是24小时制,因为UTC中没有使用子午线AM或PM。

例:

  • 上午4:12-0412
  • 上午10:12-1012
  • 下午2:28-1428
  • 下午11:56-2356

它仍然更喜欢使用标准的四位数格式。


0

tickslong/ 存储为/ bigint,当前以毫秒为单位。可以通过查看更新的值来找到TimeSpan.TicksPerSecond值。

大多数数据库都具有DateTime类型,该类型可以自动将时间存储为幕后的滴答声,但是在某些数据库(例如SqlLite)的情况下,存储滴答声可以作为存储日期的一种方式。

大多数语言允许从简单的转换Ticks→交通TimeSpan→交通Ticks

在C#中,代码为:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

但是请注意,因为在SqlLite的情况下,它仅提供少量的不同类型,它们分别是:INTREAL并且VARCHAR必须将刻度的数目存储为字符串或两个INT单元格的组合。这是因为an INT是32位带符号数字,而BIGINT64位带符号数字。

注意

但是,我个人的喜好是将日期和时间存储为ISO8601字符串。


-1

恕我直言,最佳的解决方案在某种程度上取决于您如何在数据库的其余部分(以及应用程序的其余部分)中存储时间

我个人使用过SQLite,并尝试始终使用Unix时间戳存储绝对时间,因此在处理一天中的时间(如您所要求的)时,我会执行Glen Solsberry在他的答案中写的内容,并存储自午夜以来的秒数

采用这种通用方法时,如果我到处都使用相同的标准,那么人们(包括我在内)阅读代码的混乱程度就会降低

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.