没有好的解决方案
如何处理数据库中的空日期时间值的问题没有好的解决方案。
避免 NULL
正如Chris Date 博士在 SQL 标准指南和其他地方所解释的那样, NULL是一场血腥的噩梦。一般来说,如果可能的话,应该避免它们。在 SQL 数据库中,这意味着将每一列声明为NOT NULL
(我希望有一种方法可以将其设为默认值)。在合理的情况下,我们设置了某种默认值。
日期时间没有好的默认值
但是为日期时间值设置默认值可能会非常混乱。SQL 标准和 JDBC/java.sql.* 都没有将任何特定值定义为“空”标志。
Java 8 及更高版本中内置的java.time框架确实将最小值和最大值定义为Instant
、LocalDateTime
、LocalDate
和LocalTime
类中的常量。除了解决的关键问题外,这些值可能用作标志值。这些 java.time 类解析为纳秒,而许多数据库具有更大的粒度,例如Postgres中的微秒。最小值和最大值非常极端,以至于当截断到更小的粒度时它们的含义会发生变化。
零值
您可能会直观地考虑考虑零值日期时间,0000-00-00 00:00:00.0
// 0000-00-00
。00:00:00.0
没那么简单。
没有零月/日
首先,没有 的月份00
,也没有 的日期00
。所以,好吧,我们可以把它改成0000-01-01
,一月一日。
没有零年
下一期:没有0000
. 我不知道所有数据库,但至少在 Postgres 中没有这样的零年。尝试插入这样的值会产生错误。
INSERT INTO moment_ -- TIMESTAMP WITH TIME ZONE.
VALUES ( '0000-01-01 00:00:00.0Z' ) ;
…抛出错误:
ERROR: date/time field value out of range: "0000-01-01 00:00:00.0Z"
LINE 2: VALUES ( '0000-01-01 00:00:00.0Z' ) ;
^
********** Error **********
更改0000
为第一年的0001
作品。
INSERT INTO moment_ -- TIMESTAMP WITH TIME ZONE.
VALUES ( '0001-01-01 00:00:00.0Z' ) ;
SET TIME ZONE 'UTC' ;
TABLE moment_ ;
0001-01-01 00:00:00+00
BC
这个数字是对比AD
时代的支点。让我们减去一个小时来看看从 AD 移动到 BC 的行为。顺便说一下,诸如BC
依赖于语言环境的字符串可能会因您而异。另外,请注意,我们必须将时区设置为UTC才能正确完成此日期时间操作。
SET TIME ZONE 'UTC' ;
INSERT INTO moment_ -- TIMESTAMP WITH TIME ZONE.
VALUES ( TIMESTAMP '0001-01-01 00:00:00.0Z' - INTERVAL '1 hour' ) ;
SET TIME ZONE 'UTC' ;
TABLE moment_ ;
结果是年份,所以我们从to0001 BC
跳转;没有零年。0001
0001 BC
0000
“0001-12-31 23:00:00+00 BC”
因此,如果您决定使用某个标志值,则不能使用真正的零值,但可以使用接近零的值,即公元第一年一月的第一天:'0001-01-01 00:00:00.0Z'
时区
使用此特殊标志值时,您必须注意时区。
传递字符串时,会隐式应用 SQL 会话的当前默认时区(至少在 Postgres 中)。
对 NULL 进行排序
在我自己的工作中,我确实将几乎所有列都设为 NOT NULL 并设置了默认值。但是日期时间列是一个例外,因为没有可用的标志值。我已经为此受苦了。具体来说,我学习了如何使用NULLS FIRST
/NULLS LAST
修饰符的艰难方法,因为SQL 标准没有明确定义 NULL 的默认排序顺序。