【问题标题】:Best way to store time (hh:mm) in a database在数据库中存储时间 (hh:mm) 的最佳方式
【发布时间】:2009-02-11 20:57:04
【问题描述】:

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

【问题讨论】:

  • sql server 2005 的 TIME 相当于什么?
  • @BoratSagdiyev SQL Server 2005 中没有一个,这就是我问这个问题的原因。

标签: sql-server database database-design datetime


【解决方案1】:

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

例如。

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

不过,您需要编写一些代码来重构时间,但这应该不难。

【讨论】:

  • 然后使用 DATEADD() 获取实时时间。即使 smallint 也足够了。
  • 去过那里,做到了... mins=dd%60 and hours=dd/60 on ints 就可以了。
  • 这是一个伟大、简单和直截了当的想法。 +1
  • 一个小缺点是数据库缺乏可读性
  • 我们需要存储一些天、小时和分钟的变体; SQL Server 中的 Time 数据类型只上升到 23:59:59,所以我们不能使用它。受您的回答启发,我们决定为天数设置三个 int 列:小时:分钟,以获得最大的灵活性。谢谢!
【解决方案2】:

如果您使用的是 SQL Server 2008+,请考虑使用 TIME 数据类型。 SQLTeam article 有更多使用示例。

【讨论】:

    【解决方案3】:

    日期时间开始日期时间结束

    我恳请您改用 两个 DATETIME 值,分别标记为 event_startevent_end

    时间是一项复杂的业务

    世界上大部分地区现在都采用基于denery 的公制系统来进行大多数测量,无论对错。总的来说这很好,因为至少我们都同意 g 是 ml 是立方厘米。至少大致如此。公制有很多缺陷,但至少在国际上一直存在缺陷。

    然而,随着时间的推移,我们有; 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,则没有此功能,因此请改用 Text 字段并将其存储为 ISO8601 格式,例如。

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

    注意事项:

    • 这使用 24 小时制*

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

    例如

    TimeOffset=(±Longitude.24)/360 
    

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

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

    • ISO8601 是一种国际格式。

    • 该 wiki 非常适合在 http://en.wikipedia.org/wiki/ISO_8601 获取更多详细信息。

    • 日期和时间以国际时间存储,并且根据存储时间的世界位置记录偏移量。

    根据我的经验,总是需要存储完整的日期和时间,无论我在开始项目时是否认为有。 ISO8601 是一种非常好的、面向未来的方法。

    其他免费建议

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

    根据我的经验,确定谁存储了记录也是明智之举。作为通过触发器填充的单独表或作为原始表中的附加列。

    投入越多,得到的越多

    我完全理解希望尽可能节省空间的愿望,但我很少会以丢失信息为代价这样做。

    数据库的经验法则正如标题所说,数据库只能告诉您它所拥有的数据,而且回溯历史数据以填补空白可能会非常昂贵。

    解决方案是第一次就正确。这当然说起来容易做起来难,但您现在应该对有效的数据库设计有更深入的了解,并且随后更有可能在第一次就做好。

    您的初始设计越好,以后的维修成本就越低。

    我只说这一切,因为如果我能回到过去,那么当我到达那里时,我会告诉自己。

    【讨论】:

    • 您的评论“ISO8601 的 +0000 部分直接映射到 GPS 坐标中的纬度”是错误的。它代表offset from UTC
    • 太啰嗦了,我看不出它是如何回答这个问题的。 OP想要存储像“8:30am”这样的时间......为什么他需要2个日期时间字段? 2个字段?我觉得您的回答只是个人反映了时区在软件和随机其他数据库建议中的棘手之处。
    • OP想要存储一段时间,而不是实际时间。将其存储为两个日期会花费两倍的存储空间,但会提供额外的信息,否则您将无法从存储为整数分钟等中获得。使用它取决于知道开始和结束时间是否对您的程序有价值,并且在我的经验通常是这样。
    • 这种方法在你只需要时间的时候是行不通的,比如你希望每个星期一的 10:00-13:00 有东西,我不想创建一行每逢星期一。我只是想检查服务器端是星期一然后拉出时间如果是这样。
    • OP 没有要求这样做
    【解决方案4】:

    只需存储一个常规的日期时间,而忽略其他所有内容。当您可以只加载一个日期时间时,为什么还要花额外的时间编写代码来加载、操作它并将其转换为日期时间?

    【讨论】:

    • 一个可能的原因可能是为了节省硬盘空间 - DATETIME 数据类型需要 4 个字节来存储,而 SMALLINT 例如,只需要四分之一。如果您只有几千行没有太大区别,但如果您像许多公司一样拥有数百万行,那么您节省的空间将是可观的。
    • 可能,但考虑到有 12 个人回答了这个问题,每个人都花 15 分钟思考这个问题。假设他们都是程序员,每小时赚 50 美元。考虑到仅仅考虑这个问题所花费的时间成本,您可以购买一个闪亮的新 2TB 硬盘来存储额外的字节。
    • @Seth 你说得对,如果我们只谈论额外的字节。但是您的建议只能是只有 1-2 名开发人员的小项目。但这将是一个有很多开发人员(加上 QA)的大项目,当一个新程序员试图解决这个问题时,这将是一个大问题。唉,我们都依赖于业务逻辑。
    • 借助云服务,在您按读取/处理的字节数付费的情况下,节省磁盘空间非常重要。
    • 另一个有趣的问题是,如果您依赖时间组件,则在一年中的不同时间使用时,它不会考虑夏令时调整。
    【解决方案5】:

    如果你在 SQL Server 2008 上你没有提到它,你可以使用 time 数据类型,否则使用自午夜以来的分钟数

    【讨论】:

      【解决方案6】:

      SQL Server 实际上将时间存储为一天的分数。例如,1 天 = 值 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 每个使用 2 个字节,日期范围更窄,时间用分钟而不是毫秒)
      • 我知道(99.99% 肯定)一天中的时间是分数。
      【解决方案7】:

      我会将它们转换为整数 (HH*3600 + MM*60),然后以这种方式存储。存储空间小,但仍然很容易使用。

      【讨论】:

        【解决方案8】:

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

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

        如果您必须回顾并手动查看表格,整数可能比实际时间戳更令人困惑。

        【讨论】:

          【解决方案9】:

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

          【讨论】:

          • 如果 @mdresser 使用的是
          【解决方案10】:

          您确定只需要小时和分钟吗?如果你想用它做任何有意义的事情(比如计算两个这样的数据点之间的时间跨度),没有关于时区的信息,DST 可能会给出不正确的结果。时区可能不适用于您的情况,但 DST 肯定会。

          【讨论】:

            【解决方案11】:

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

            09:12 = 912 14:15 = 1415

            当转换回“人类可读的形式”时,我们只需从右边插入一个冒号“:”两个字符。如果需要,请在左侧填充零。以各种方式保存数学,并使用更少的字节(与 varchar 相比),并强制该值是数字(而不是字母数字)

            虽然很傻...... MS SQL中应该有一个TIME数据类型已经很多年了恕我直言......

            【讨论】:

            • Kristen 是绝对正确的,但前提是她关于小时和分钟仅代表一个 24 小时周期的假设是正确的。存储 0..1439 分钟需要 10 位。这意味着您可以随意使用额外的 6 位,因此有发挥创意的空间。我建议使用 5 位来存储您所在时区的小时偏移量,但前提是提问者只想存储 23:59 的最大时间(距午夜一分钟)
            【解决方案12】:

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

            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 上存储分钟值。

            【讨论】:

              【解决方案13】:

              正如 Kristen 建议的那样,以 UTC 格式节省时间会更好。

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

              示例:

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

              还是推荐使用标准的四位数格式。

              【讨论】:

                【解决方案14】:

                ticks 存储为long/bigint,目前以毫秒为单位。通过查看TimeSpan.TicksPerSecond 值可以找到更新后的值。

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

                大多数语言都允许从TicksTimeSpanTicks 轻松转换。

                示例

                在 C# 中,代码为:

                long TimeAsTicks = TimeAsTimeSpan.Ticks;
                
                TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);
                

                但请注意,因为在 SqlLite 的情况下,它只提供少量不同的类型,它们是; INTREALVARCHAR 需要将刻度数存储为一个字符串或两个 INT 单元格的组合。这是因为 INT 是 32 位有符号数,而 BIGINT 是 64 位有符号数。

                注意

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

                【讨论】:

                  【解决方案15】:

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

                  就我个人而言,我曾使用 SQLite 并尝试始终使用 unix timestamps 来存储绝对时间,因此在处理一天中的时间(如您所要求的那样)时,我会按照 Glen Solsberry 在他的回答中所写的操作并存储午夜后的秒数

                  当采用这种通用方法时,如果我在所有地方都使用相同的标准,那么阅读代码的人(包括我!)就不会那么困惑了

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-07-27
                    • 2018-03-16
                    • 1970-01-01
                    • 2018-12-01
                    • 2015-05-11
                    • 1970-01-01
                    • 2011-04-02
                    相关资源
                    最近更新 更多