【GaussDB】GaussDB506版本A模式中的date类型
GuassDB 在506.0版本中引入了一个新的数据类型,datea,用于兼容ORACLE的date类型。而在此版本前,GaussDB只是在ORACL兼容模式下,把位于数据类型位置的date,转换成了timestamp(0) without timezone。虽然timestamp(0)和ORACLE的date都是精确到秒,但是两者仍有诸多差异。在边上修修补补对齐行为,还要考虑原本timestamp类型行为的对齐,存在很多矛盾。所以GaussDB干脆就直接新增了一个类型,不再折腾原本的timestamp了。
GaussDB官方文档: GaussDB文档中心/集中式版Oracle兼容性说明/兼容性说明/SQL的基本元素/数据类型
启用date默认映射到datea
修改参数mapping_date_to_datea=on,重启数据库,SQL解析时就会把date类型转换成datea类型。
注:新安装的506.0 SPC0100版本中,该参数是默认开启的。
影响点
- SQL中DATE类型的语义识别
之前版本中,
sql
create table t1(a date); -> create table t1(a timestamp(0) without time zone);
select cast(sysdate as date) -> select cast(sysdate as timestamp(0) without time zone);
启用datea后
sql
create table t1(a date); -> create table t1(a datea);
select cast(sysdate as date) -> select cast(sysdate as datea);
- 默认输出的文本格式
sql
gaussdb=# select '2022-01-01 22:01:23'::timestamp(6)::datea;
datea
------------------------
2022-01-01 22:01:23 AD
(1 row)
gaussdb=# select trunc('2022-01-01 22:01:23'::timestamp(6)::datea);
trunc
------------------------
2022-01-01 00:00:00 AD
(1 row)
gaussdb=#
ORACLE里的DATE格式在sqlplus中的输出受会话中nls_date_format参数的影响,而gaussdb则为固定格式 。
相比自带的timestamp类型,秒后面没有小数了,但相比ORACLE的date类型,多了个表示公元前后的标识AD/BC,而ORACLE则是在前面加负号表示公元前(需alter session set nls_Date_format='syyyy-mm-dd hh24:mi:ss';才能显示)。
- 实际存储
sql
gaussdb=# select timestamp_send('2022-01-01 22:01:23'::timestamp(6));
timestamp_send
--------------------
\x0002778b326412c0
(1 row)
gaussdb=# select datea_send('2022-01-01 22:01:23'::timestamp(6)::datea);
datea_send
--------------------
\x0002778b326412c0
(1 row)
可以发现datea的存储格式其实还是和timestamp类型一致,因此并不会因为不记录秒后小数而节省存储空间。
- 系统函数映射
- sysdate
| mapping_date_to_datea | a_format_dev_version | a_format_date_timestamp | sysdate实际表达式 | 数据类型 | 数据输出格式示例 |
|---|---|---|---|---|---|
| on | s7 | on | sysdate_a() | datea | 2025-06-13 11:00:44 AD |
| on | s7 | off | sysdate_a() | datea | 2025-06-13 11:00:44 AD |
| on | s1 | on | sysdate_a() | datea | 2025-06-13 11:00:44 AD |
| on | s1 | off | sysdate_a() | datea | 2025-06-13 11:00:44 AD |
| off | s7 | on | (clock_timestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s7 | off | (clock_timestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s2-s6 | on | (pg_systimestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s2-s6 | off | (pg_systimestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s1 | on | (pg_systimestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s1 | off | (pg_systimestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
- current_date
| mapping_date_to_datea | a_format_dev_version | a_format_date_timestamp | current_date实际表达式 | 数据类型 | 数据输出格式示例 |
|---|---|---|---|---|---|
| on | s7 | on | pg_systimestamp())::datea | datea | 2025-06-13 11:00:44 AD |
| on | s7 | off | pg_systimestamp())::datea | datea | 2025-06-13 11:00:44 AD |
| on | s1 | on | pg_systimestamp())::datea | datea | 2025-06-13 11:00:44 AD |
| on | s1 | off | pg_systimestamp())::datea | datea | 2025-06-13 11:00:44 AD |
| off | s7 | on | ('now'::text)::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s7 | off | ('now'::text)::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s2-s6 | on | ('now'::text)::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s2-s6 | off | ('now'::text)::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s1 | on | (pg_systimestamp())::timestamp(0) without time zone | timestamp | 2025-06-13 11:00:44.000000 |
| off | s1 | off | text_date('now'::text) | "date" | 2025-06-13 |
映射到不同的函数,其查询结果的值会与事务、跨语句、执行次数等行为存在相关性,这是老生常谈的问题,本文暂不展开,可参考PG的文档或问AI。
- 部分表达式的行为变化
开启后,默认情况下date'2022-01-01'这样的用法会报错,因为nls_date_format的默认值是DD-MON-RR,需要修改参数nls_date_format='yyyy-mm-dd'才能正常执行,这里和ORACLE产生了兼容性差异,ORACLE的date'2022-01-01'这种用法与nls_date_format参数无关,只能使用syyyy-mm-dd的格式。
sql
gaussdb=# select date'2022-12-13';
ERROR: invalid value "22-12-13" for "MON"
LINE 1: select date'2022-12-13';
^
DETAIL: The given value did not match any of the allowed values for this field.
CONTEXT: referenced column: datea
gaussdb=# set nls_date_format='yyyy-mm-dd';
SET
gaussdb=# select date'2022-12-13';
datea
------------------------
2022-12-13 00:00:00 AD
(1 row)
gaussdb=#
- 操作符变化
sql
gaussdb=# select '2022-01-01 22:01:23'::timestamp(6)::datea -'2021-01-01 21:01:23'::timestamp(6)::datea;
?column?
------------------
365.041666666667
(1 row)
gaussdb=# select '2022-01-01 22:01:23'::timestamp(6) -'2021-01-01 21:01:23'::timestamp(6);
?column?
-------------------------------
+000000365 01:00:00.000000000
(1 row)
gaussdb=#
最重要的就是两个日期相减了,原本的timestamp相减只能返回interval类型,而datea相减能返回numeric类型,行为兼容ORACLE。
-
数据导入
原本timestamp可以直接以文本方式导入,开启后,对于datea类型必须指定datea_format格式(oracle的sqlldr也要指定日期格式)
-
函数易变性
另外,还有一个参数 modify_function_property,新装的默认值为"3257,5562,4164,4073",
表示把下面这4个函数的易变性从immutable改成stable
- pg_catalog.current_timestamp(numeric)
- pg_catalog.text_timestamp(text)
- pg_catalog.text_date(text)
- pg_catalog.DBTimezone()
因为date相关的表达式在不同GUC参数配置下会对应到不同的表达式,而上面这些函数的易变性可以调整,就增加了影响结果的因素。
总结
如果习惯了之前openGauss/GaussDB版本把date处理成timestamp(0),则建议在506.0版本中关闭mapping_date_to_datea,因为这些行为变化实在太多了,可能现有应用程序代码要排查的点太多。但如果是刚开始使用这个版本,可以开启mapping_date_to_datea,这样至少日期相减得数字的这个最常见的兼容性问题就不用纠结了。
- 本文作者: DarkAthena
- 本文链接: https://www.darkathena.top/archives/gaussdb-506-date-type-oracle
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处