Oracle中日期字段未定义日期类型的案例一则

Posted bisal(Chen Liu)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle中日期字段未定义日期类型的案例一则相关的知识,希望对你有一定的参考价值。

可能很多开发规范中都写了日期类型的字段,应该就是用标准的日期类型定义,不要用字符串、数值型等替代,相关历史文章,如下所示,

日期字段未定义DATE类型所带来的一些问题

为什么日期不建议使用VARCHAR2或者NUMBER?

但是有时候,出于某些考虑,例如异构数据库同步避免字段类型的差异,就会将日期字段定义为字符串类型,虽然满足了这个需求,但可能对其他方面的使用带来了不便,如下例子,就是最近某个Oracle技术群中提出的问题,可能很多朋友,都会碰到。

表中包含一个日期数据字段,但是定义为char字符串的类型,而且做了分区,分区字段就是这个字符串类型的日期,但是分区条件是按照to_date(char类型字段)来做的,如下所示,

CREATE TABLE customer1(age NUMBER, birthday char(20))
PARTITION BY RANGE (birthday)
(
  PARTITION p1 VALUES less than (to_date('2022-01','yyyy-mm')),
  PARTITION p2 VALUES less than (to_date('2022-02','yyyy-mm')),
  PARTITION p3 VALUES less than (to_date('2022-03','yyyy-mm'))
);

如果是这种定义,插入范围内月份的一号是可以的,

INSERT INTO customer1 values(1, to_date('2022-02-01','yyyy-mm-dd'));

但是当插入范围内月份的其他天,就会提示报错,"SQL 错误 [14400] [72000]: ORA-14400: 插入的分区关键字未映射到任何分区",

SQL> INSERT INTO customer1 values(2, to_date('2022-02-02','yyyy-mm-dd'));
INSERT INTO customer1 values(2, to_date('2022-02-02','yyyy-mm-dd'))
            *
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition

如果我们按规范将日期字段定义为标准的DATE类型,

CREATE TABLE customer2(age NUMBER, birthday date)
PARTITION BY RANGE (birthday)
(
  PARTITION p1 VALUES less than (to_date('2022-01','yyyy-mm')),
  PARTITION p2 VALUES less than (to_date('2022-02','yyyy-mm')),
  PARTITION p3 VALUES less than (to_date('2022-03','yyyy-mm'))
);

插入符合分区条件的日期,都是能执行的,

INSERT INTO customer2 values(1, to_date('2022-01-01','yyyy-mm-dd'));
INSERT INTO customer2 values(1, to_date('2022-02-01','yyyy-mm-dd'));
INSERT INTO customer2 values(2, to_date('2022-02-02','yyyy-mm-dd'));

如下所示,

SQL> SELECT * FROM customer2;
       AGE BIRTHDAY
---------- ------------------
         1 01-JAN-22
         1 01-FEB-22
         2 02-FEB-22

如果日期字段,就是定义为字符串类型,还得做分区,创建虚拟列,算是一种解决方案,如下所示,虚拟列将这个字符串日期字段用to_date转换为DATE日期类型,

CREATE TABLE customer3(age NUMBER, birthday char(20), partiton_key DATE GENERATED ALWAYS AS (to_date(birthday, 'yyyy-mm-dd')))
PARTITION BY RANGE (partiton_key)
(
  PARTITION p1 VALUES less than (to_date('2022-01','yyyy-mm')),
  PARTITION p2 VALUES less than (to_date('2022-02','yyyy-mm')),
  PARTITION p3 VALUES less than (to_date('2022-03','yyyy-mm'))
);

但是给开发带来一个问题,如果插入时,不指定除虚拟列外其他的列,就会提示错误,因为少了虚拟列的值,

SQL> insert into customer3 values(1, '2022-01-01');
insert into customer3 values(1, '2022-01-01')
            *
ERROR at line 1:
ORA-00947: not enough values

只能显式在INSERT语句中指定除虚拟列外的其他列,由于日期字段是字符串类型,此处日期是字符串的格式,不是to_date(),但是按照定义,虚拟列会自动计算这个日期字段的to_date()类型进行存储,

INSERT INTO customer3(age, birthday) values(1, '2022-01-01');
INSERT INTO customer3(age, birthday) values(2, '2022-02-01');
INSERT INTO customer3(age, birthday) values(3, '2022-02-02');

此时插入、检索就可以看到对应的字符串类型和日期类型的日期字段了,

SQL> SELECT * FROM customer3;
       AGE BIRTHDAY             PARTITON_KEY
---------- -------------------- ------------------
         1 2022-01-01           01-JAN-22
         2 2022-02-01           01-FEB-22
         3 2022-02-02           02-FEB-22

这就给开发提出了更高的要求,代码中的SQL,必须按照明确具体列的形式来写,如果是系统改造,侵入性就比较高,因此,还是应该按照规范的开发模式来设计,才能避免这些所谓的workaround,但往往,某些场景下,就需要在这些不同的方案中进行权衡,但是不能仅考虑开发方便了,将问题都给运维,原本很简单的维护就会变得复杂。

近期更新的文章:

MySQL中的联合索引、覆盖索引及最左匹配原则

二进制和十六进制的转换操作

最近碰到的问题

道路上机动车掉头的规则

Oracle中怎么导出索引数据块?

文章分类和索引:

公众号900篇文章分类和索引

以上是关于Oracle中日期字段未定义日期类型的案例一则的主要内容,如果未能解决你的问题,请参考以下文章

MySQL的TIMESTAMP数据类型

MySQL的TIMESTAMP数据类型

日期字段未定义DATE类型所带来的一些问题

Graphql 日期字段类型必须是输出类型但得到:未定义

mysql 中,创建表时如何定义一个日期类型的字段

oracle日期时间范围查询