可能很多开发规范中都写了日期类型的字段,应该就是用标准的日期类型定义,不要用字符串、数值型等替代,相关历史文章,如下所示,
《日期字段未定义DATE类型所带来的一些问题》
《为什么日期不建议使用VARCHAR2或者NUMBER?》
但是有时候,出于某些考虑,例如异构数据库同步避免字段类型的差异,就会将日期字段定义为字符串类型,虽然满足了这个需求,但可能对其他方面的使用带来了不便,如下例子,就是最近某个Oracle技术群中提出的问题,可能很多朋友,都会碰到。
表中包含一个日期数据字段,但是定义为char字符串的类型,而且做了分区,分区字段就是这个字符串类型的日期,但是分区条件是按照to_date(char类型字段)来做的,如下所示,
代码语言:javascript复制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'))
);
如果是这种定义,插入范围内月份的一号是可以的,
代码语言:javascript复制INSERT INTO customer1 values(1, to_date('2022-02-01','yyyy-mm-dd'));
但是当插入范围内月份的其他天,就会提示报错,"SQL 错误 [14400] [72000]: ORA-14400: 插入的分区关键字未映射到任何分区",
代码语言:javascript复制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类型,
代码语言:javascript复制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'))
);
插入符合分区条件的日期,都是能执行的,
代码语言:javascript复制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'));
如下所示,
代码语言:javascript复制SQL> SELECT * FROM customer2;
AGE BIRTHDAY
---------- ------------------
1 01-JAN-22
1 01-FEB-22
2 02-FEB-22
如果日期字段,就是定义为字符串类型,还得做分区,创建虚拟列,算是一种解决方案,如下所示,虚拟列将这个字符串日期字段用to_date转换为DATE日期类型,
代码语言:javascript复制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'))
);
但是给开发带来一个问题,如果插入时,不指定除虚拟列外其他的列,就会提示错误,因为少了虚拟列的值,
代码语言:javascript复制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()类型进行存储,
代码语言:javascript复制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');
此时插入、检索就可以看到对应的字符串类型和日期类型的日期字段了,
代码语言:javascript复制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,但往往,某些场景下,就需要在这些不同的方案中进行权衡,但是不能仅考虑开发方便了,将问题都给运维,原本很简单的维护就会变得复杂。