如何在数据库中表示时间结束?
我想知道如何在数据库中表示时间结束(正无穷大)值。
当我们使用 32 位时间值时,明显的答案是实际的 32 位时间结束 - 接近年份 2038。
现在我们使用的是 64 位时间值,我们无法在 DATETIME 字段中表示 64 位时间结束,因为 64 位时代的终结是从现在起数十亿年。
由于 SQL Server 和 Oracle(我们支持的两个平台)都允许长达 9999 年的年份,我想我们可以选择一些“大”的未来日期,例如 1/1/3000。
然而,由于客户和我们的 QA 部门都会查看 DB 值,我希望它是显而易见的,而不是看起来像是有人弄乱了他们的日期算术。
我们只是选择一个日期并坚持下去吗?
I am wondering how to represent an end-of-time (positive infinity) value in the database.
When we were using a 32-bit time value, the obvious answer was the actual 32-bit end of time - something near the year 2038.
Now that we're using a 64-bit time value, we can't represent the 64-bit end of time in a DATETIME field, since 64-bit end of time is billions of years from now.
Since SQL Server and Oracle (our two supported platforms) both allow years up to 9999, I was thinking that we could just pick some "big" future date like 1/1/3000.
However, since customers and our QA department will both be looking at the DB values, I want it to be obvious and not appear like someone messed up their date arithmetic.
Do we just pick a date and stick to it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
使用最大整理日期,根据您的 DBMS,该日期可能是 9999-12-31。您之所以要这样做,是因为如果您尝试采用“纯粹”方法,例如使用 Null(如某些评论者所建议的那样)或使用永久标志(如 Marc B 所建议的那样),那么基于日期范围的查询将很快变得极其复杂
。最大整理日期意味着日期范围内的“永远”或“直至另行通知”,这使得查询非常简单、自然。它使此类查询变得非常清晰和简单:
... WHERE effective_date <= @PointInTime AND expiry_date >= @PointInTime
... WHERE effective_date <= @StartOfRange AND expiry_date >= @EndOfRange
... WHERE A. effective_date <= B.expiry_date AND B. effective_date <= A.expiry_date
... WHERE expiry_date = @MaxCollatingDate
好吧,这个并不简单,但是使用最大整理日期作为终点更简单。请参阅: 这个问题 一个好的方法。
使用此方法可能会给某些用户带来一些问题,他们可能会发现“9999-12-31”在报告或屏幕上令人困惑。如果这对您来说是个问题,那么 drdwicox 关于使用用户友好值的翻译的建议是很好的。然而,我建议用户界面层,而不是中间层,是执行此操作的地方,因为最明智或最合意的可能会有所不同,具体取决于您正在谈论的是报告还是数据输入表单,并且受众是内部的还是外部的。例如,有些地方您可能想要的是一个简单的空白。其他人你可能想要“永远”这个词。其他人可能需要一个空文本框,其中带有一个复选框,上面写着“直到另行通知”。
Use the max collating date, which, depending on your DBMS, is likely going to be 9999-12-31. You want to do this because queries based on date ranges will quickly become miserably complex if you try to take a "purist" approach like using Null, as suggested by some commenters or using a forever flag, as suggested by Marc B.
When you use max collating date to mean "forever" or "until further notice" in your date ranges, it makes for very simple, natural queries. It makes these kind of queries very clear and simple:
... WHERE effective_date <= @PointInTime AND expiry_date >= @PointInTime
... WHERE effective_date <= @StartOfRange AND expiry_date >= @EndOfRange
... WHERE A.effective_date <= B.expiry_date AND B.effective_date <= A.expiry_date
... WHERE expiry_date = @MaxCollatingDate
OK, so this one isn't simple, but it's simpler using max collating dates for the end point. See: this question for a good approach.
Using this approach can create a bit of an issue for some users, who might find "9999-12-31" to be confusing in a report or on a screen. If this is going to be a problem for you then drdwicox's suggestion of using a translation to a user-friendly value is good. However, I would suggest that the user interface layer, not the middle tier, is the place to do this, since what may be the most sensible or palatable may differ, depending on whether you are talking about a report or a data entry form and whether the audience is internal or external. For example, some places what you might want is a simple blank. Others you might want the word "forever". Others you may want an empty text box with a check box that says "Until Further Notice".
在 PostgreSQL 中,时间的结束是“无穷大” 。它还支持“-无穷大”。值“无穷大”保证晚于所有其他时间戳。
PostgreSQL 至少从 8.0 版本开始就支持“infinity”和“-infinity”。
您可以通过使用 dbms 支持的最大日期来模仿这种行为,至少部分地如此。但最长日期可能不是最佳选择。 PostgreSQL 的最大时间戳是 294,276 年的某个时间,这肯定会让一些人感到惊讶。 (我不喜欢让用户感到惊讶。)
这样的值可能更有用:“9999-12-31 11:59:59.999”。
这并不是 9999 年的最大值,但数字对齐得很好。您可以将该值包装在 infinity() 函数和
CREATE DOMAIN
语句中。如果您从源代码构建或维护数据库结构,则可以使用宏扩展将INFINITY
扩展为合适的值。In PostgreSQL, the end of time is 'infinity'. It also supports '-infinity'. The value 'infinity' is guaranteed to be later than all other timestamps.
PostgreSQL has supported 'infinity' and '-infinity' since at least version 8.0.
You can mimic this behavior, in part at least, by using the maximum date your dbms supports. But the maximum date might not be the best choice. PostgreSQL's maximum timestamp is some time in the year 294,276, which is sure to surprise some people. (I don't like to surprise users.)
A value like this is probably more useful: '9999-12-31 11:59:59.999'.
That's not quite the maximum value in the year 9999, but the digits align nicely. You can wrap that value in an infinity() function and in a
CREATE DOMAIN
statement. If you build or maintain your database structure from source code, you can use macro expansion to expandINFINITY
to a suitable value.有时我们会选择一个日期,然后制定一项政策,该日期绝不能未经过滤。执行该策略最常见的地方是中间层。我们只是过滤结果,将“神奇”的结束日期更改为更容易接受的日期。
We sometimes pick a date, then establish a policy that the date must never appear unfiltered. The most common place to enforce that policy is in the middle tier. We just filter the results to change the "magic" end-of-time date to something more palatable.
代表“直到永恒”或“直到进一步通知”的概念是一个不确定的命题。
关系理论正确地说,不存在空之类的东西,因此您必须将其分为两部分:一部分包含已知结束日期/结束时间的行,另一部分包含已知的行结束时间尚不清楚。
但是(就像有一个空值一样)将表分成两部分也会使您的查询编写变得混乱。视图可以在一定程度上容纳只读部分,但更新(或在视图上写入 INSTEAD OF)无论如何都会很困难,并且可能会对性能产生负面影响(无论如何)。
让 null 表示“结束时间未知”将使更新变得“更容易”,但是读取查询会因您需要的所有 CASE ... 或 COALESCE ... 构造而变得混乱。
在您想要从 DATETIME 中“提取”DATE 的所有情况下,使用 dportas 提到的理论上正确的解决方案会变得混乱。如果手头的 DATETIME 值是“(可表示的)时间的结束(如您所说,从现在起数十亿年)”,那么这不仅仅是对该 DATETIME 值调用 DATE 提取器函数的简单情况,因为您还希望该日期提取器为您的情况生成“可表示日期的结尾”。
另外,您可能不希望在用户界面中将“缺席时间结束”显示为值 9999-12-31。因此,如果您在数据库中使用时间结束的“实际值”,您将面临一些工作,以确保该值不会出现在您的 UI 中的任何位置。
很抱歉不能说有办法避免所有混乱。你真正拥有的唯一选择就是最终陷入困境。
Representing the notion of "until eternity" or "until further notice" is an iffy proposition.
Relational theory proper says that there is no such thing as null, so you're obliged to have whatever table it is split in two: one part with the rows for which the end date/end time is known, and another for the rows for which the end time is not yet known.
But (like having a null) splitting the tables in two will make a mess of your query writing too. Views can somewhat accommodate the read-only parts, but updates (or writing the INSTEAD OF on your view) will be tough no matter what, and likely to affect performance negatively no matter what at that).
Having the null represent "end time not yet known" will make updating a bit "easier", but the read queries get messy with all the CASE ... or COALESCE ... constructs you'll need.
Using the theoretically correct solution mentioned by dportas gets messy in all those cases where you want to "extract" a DATE from a DATETIME. If the DATETIME value at hand is "the end of (representable) time (billions of years from now as you say)", then this is not just a simple case of invoking the DATE extractor function on that DATETIME value, because you'd also want that DATE extractor to produce the "end of representable DATEs" for your case.
Plus, you probably do not want to show "absent end of time" as being a value 9999-12-31 in your user interface. So if you use the "real value" of the end of time in your database, you're facing a bit of work seeing to it that that value won't appear in your UI anywhere.
Sorry for not being able to say that there's a way to stay out of all messes. The only choice you really have is which mess to end up in.
不要让约会变得“特别”。虽然您的代码不太可能在 9999 甚至 2^63-1 左右,但看看几年前使用“12/31/1999”带来的所有乐趣。
如果您需要表示“无限”或“无限”时间,请添加一个布尔/位字段来表示该状态。
Don't make a date be "special". While it's unlikely your code would be around in 9999 or even in 2^63-1, look at all the fun that using '12/31/1999' caused just a few years ago.
If you need to signal an "endless" or "infinite" time, then add a boolean/bit field to signal that state.