迁移 Oracle 到 PostgreSQL: 时间类型和数字类型的加减运算

五月 20, 2023

Oracle 支持时间类型与数字类型的加减运算,数字默认单位为天。

PostgreSQL 支持时间类型与 interval 类型的加减运算。日期支持与整型做加减。

为了兼容 Oracle(时间类型与数字类型的加减运算),我们可以定义操作符来实现时间与数字的加减。

定义操作符

1、自定义几个函数,用于时间戳与数字的加减。

postgres=# create or replace function timestamp_add_num(timestamp, float8) returns timestamp as $$
  select $1 + ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function timestamptz_add_num(timestamptz, float8) returns timestamptz as $$
  select $1 + ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function num_add_timestamp(float8, timestamp) returns timestamp as $$
  select $2 + ($1||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function num_add_timestamptz(float8, timestamptz) returns timestamptz as $$
  select $2 + ($1||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function timestamp_min_num(timestamp, float8) returns timestamp as $$
  select $1 - ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function timestamptz_min_num(timestamptz, float8) returns timestamptz as $$
  select $1 - ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

2、使用 CREATE OPERATOR 语句定义操作符:

postgres=# create operator + (procedure = timestamp_add_num, leftarg=timestamp, rightarg=float8);
CREATE OPERATOR
postgres=# create operator + (procedure = timestamptz_add_num, leftarg=timestamptz, rightarg=float8);
CREATE OPERATOR

postgres=# create operator + (procedure = num_add_timestamp, leftarg=float8, rightarg=timestamp);
CREATE OPERATOR
postgres=# create operator + (procedure = num_add_timestamptz, leftarg=float8, rightarg=timestamptz);
CREATE OPERATOR

postgres=# create operator - (procedure = timestamp_min_num, leftarg=timestamp, rightarg=float8);
CREATE OPERATOR
postgres=# create operator - (procedure = timestamptz_min_num, leftarg=timestamptz, rightarg=float8);
CREATE OPERATOR

3、验证测试

postgres=# select now() + 1;
           ?column?
-------------------------------
 2017-10-25 20:03:39.256659+08
(1 row)

postgres=# select now() + 1.1;
           ?column?
-------------------------------
 2017-10-25 22:27:40.925673+08
(1 row)

postgres=# select now() - 1.1;
           ?column?
-------------------------------
 2017-10-23 18:35:04.419078+08
(1 row)

postgres=# select 1.1 + now();
           ?column?
-------------------------------
 2017-10-25 23:23:08.842953+08
(1 row)

postgres=# select 1.1 + now()::timestamp;
          ?column?
----------------------------
 2017-10-25 23:23:13.318669
(1 row)

orafce 兼容包扩展支持

orafce 是 PostgreSQL 社区推出的一个 Oracle 兼容包,包含了大量 Oracle 兼容函数,package。

create extension orafce;

兼容 oracle 中 date 类型与数字类型的加减:

postgres=# create or replace function sysdate_add_num(oracle.date, float8) returns timestamp as $$
  select $1 + ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function num_add_sysdate(float8, oracle.date) returns timestamp as $$
  select $2 + ($1||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create or replace function sysdate_min_num(oracle.date, float8) returns timestamp as $$
  select $1 - ($2||' day')::interval;
$$ language sql strict immutable;
CREATE FUNCTION

postgres=# create operator + (procedure = sysdate_add_num, leftarg=oracle.date, rightarg=float8);
CREATE OPERATOR

postgres=# create operator + (procedure = num_add_sysdate, leftarg=float8, rightarg=oracle.date);
CREATE OPERATOR

postgres=# create operator - (procedure = sysdate_min_num, leftarg=oracle.date, rightarg=float8);
CREATE OPERATOR

示例:

postgres=# SELECT ORACLE.SYSDATE() , ORACLE.SYSDATE() + 1/12.0 FROM dual;
       sysdate       |      ?column?
---------------------+---------------------  
 2017-11-08 09:45:04 | 2017-11-08 11:45:04  
(1 row)  

postgres=# SELECT ORACLE.SYSDATE() , ORACLE.SYSDATE() + 1/12.0::float8 FROM dual;
       sysdate       |      ?column?         
---------------------+---------------------  
 2017-11-08 09:45:15 | 2017-11-08 11:45:15  
(1 row)  

postgres=# SELECT ORACLE.SYSDATE() , ORACLE.SYSDATE() + 1/12.0 FROM dual;
       sysdate       |      ?column?
---------------------+---------------------
 2017-11-08 09:45:21 | 2017-11-08 11:45:21
(1 row)

postgres=# SELECT ORACLE.SYSDATE() , ORACLE.SYSDATE() - 1/12.0 FROM dual;
       sysdate       |      ?column?
---------------------+---------------------
 2017-11-08 09:45:24 | 2017-11-08 07:45:24
(1 row)

postgres=# SELECT ORACLE.SYSDATE() , 1/12.0 + oracle.sysdate() FROM dual;
       sysdate       |      ?column?
---------------------+---------------------
 2017-11-08 09:45:34 | 2017-11-08 11:45:34
(1 row)

postgres=# SELECT ORACLE.SYSDATE() , 1/24.0 + oracle.sysdate() FROM dual;
       sysdate       |      ?column?
---------------------+---------------------
 2017-11-08 09:46:19 | 2017-11-08 10:46:19
(1 row)

orafce sysdate 的时区对齐

postgres=# alter role all set orafce.timezone=-8;
ALTER ROLE
postgres=# show timezone;
 TimeZone
----------
 PRC
(1 row)

-- 退出当前会话连接,重新建立数据库连接
postgres=# select oracle.sysdate() , now();
       sysdate       |              now
---------------------+-------------------------------
 2017-11-08 18:07:16 | 2017-11-08 18:07:16.421318+08
(1 row)