很多人认为 Oracle 和 PostgreSQL 的 CASE WHEN 都遵循 SQL 标准,因此可以直接迁移。实际上,大部分语法确实一致,但在 NULL、空字符串、数据类型转换等方面存在一些容易踩坑的地方。

本文通过真实测试案例,带大家看看 Oracle 与 PostgreSQL 在 CASE WHEN 使用上的区别。

CASE WHEN 的基本用法

Oracle 和 PostgreSQL 都支持 SQL 标准的 CASE WHEN 语法。

创建测试表和插入数据:

CREATE TABLE t_employee( emp_id INTEGER, emp_name VARCHAR(50), salary INTEGER, status VARCHAR(20) );
INSERT INTO t_employee VALUES(1,'张三',12000,'ACTIVE'); 
INSERT INTO t_employee VALUES(2,'李四',8000,'ACTIVE'); 
INSERT INTO t_employee VALUES(3,'王五',4000,'LOCK'); 
INSERT INTO t_employee VALUES(4,'赵六',2000,'LOCK'); 
COMMIT;

写法一:Search CASE

SELECT emp_name, salary,
    CASE
        WHEN salary > 10000 THEN 'HIGH'
        WHEN salary > 5000 THEN 'MIDDLE'
        ELSE 'LOW'
    END AS salary_level
FROM t_employee;

写法二:Simple CASE

SELECT emp_name, status, 
       CASE status 
       WHEN 'ACTIVE' THEN '正常用户' 
       WHEN 'LOCK' THEN '冻结用户' 
       ELSE '未知状态' 
   END AS status_desc 
 FROM t_employee;

两种数据库都支持上述语法,因此对于简单的 CASE WHEN 来说,可以互相迁移到对方。

Oracle与PostgreSQL对于空字符串的理解不同

Oracle与PostgreSQL对于空字符串的理解不同,这是数据库迁移过程中最经典的问题。

Oracle测试

SELECT
    CASE
        WHEN '' IS NULL
        THEN 'YES'
        ELSE 'NO'
    END result
from t_employee;

执行结果:

RES
---
YES
YES
YES
YES

继续测试:

SQL> SELECT LENGTH('') FROM dual;

LENGTH('')
----------


结果说明:Oracle认为:’ ’ = NULL

PostgreSQL测试

SELECT
    CASE
        WHEN '' IS NULL
        THEN 'YES'
        ELSE 'NO'
    END;

执行结果:

 result
--------
 NO
 NO
 NO
 NO

说明:

'' != NULL

继续测试:

postgres=# SELECT LENGTH('');
 length
--------
      0
(1 行记录)

结果说明 PostgreSQL 的空字符串是真正存在的。

对CASE WHEN的影响

SQL> update t_employee set status='' where emp_id>2;                                                                       

2 rows updated.

SQL> select * from t_employee;

    EMP_ID EMP_NAME						  SALARY STATUS
---------- -------------------------------------------------- ---------- --------------------
         1 张三                                                    12000 ACTIVE
         2 李四                                                     8000 ACTIVE
         3 王五                                                     4000
         4 赵六                                                     2000

Oracle测试结果:

select 
CASE
    WHEN status IS NULL
        THEN 'NULL'
    WHEN status=''
        THEN 'EMPTY'
    ELSE status
END result
from t_employee;

RESULT
--------------------
ACTIVE
ACTIVE
NULL
NULL

如果 status=‘’,Oracle会认为:status=NULL,所以输出:NULL

但是 PostgreSQL,不会进入IS NULL判断,会进入status=''判断,输出的结果不一样:

select 
CASE
    WHEN status IS NULL
        THEN 'NULL'
    WHEN status=''
        THEN 'EMPTY'
    ELSE status
END result
from t_employee;

 result
--------
 ACTIVE
 ACTIVE
 EMPTY
 EMPTY
对比项 Oracle PostgreSQL
‘’ NULL 空字符串
LENGTH(‘’) NULL 0
IS NULL是否匹配’’

Oracle迁移PostgreSQL时,一定要检查所有涉及空字符串的 CASE WHEN 判断。

日期运算后的返回类型不一样导致的问题

再看另外一个案例,类似的sql,在oracle正常执行,到PostgreSQL执行报错, Oracle 迁移 PostgreSQL 最容易踩坑的问题之一。

Oracle测试

测试的表和数据:

create table t_time(
    id integer,
    back_time varchar2(30),
    sc_sm_time date
);
insert into t_time values (1,'2026-07-19 16:30:00',to_date('2026-07-19 15:00:00','yyyy-mm-dd hh24:mi:ss'));
insert into t_time values (2,'2026-07-19 14:30:00',to_date('2026-07-19 15:00:00','yyyy-mm-dd hh24:mi:ss'));
commit;
SQL> select * from t_time;

	ID BACK_TIME			  SC_SM_TIME
---------- ------------------------------ -------------------
	 1 2026-07-19 16:30:00		  2026-07-19 15:00:00
	 2 2026-07-19 14:30:00		  2026-07-19 15:00:00

执行的sql语句:

SELECT id,
  CASE 
  WHEN to_date(back_time,'yyyy-mm-dd hh24:mi:ss')> sc_sm_time THEN (to_date(back_time,'yyyy-mm-dd hh24:mi:ss')-sc_sm_time)*24
  ELSE
  0
  END AS zysc_time
FROM t_time;

执行结果:

	ID  ZYSC_TIME
---------- ----------
	 1	  1.5
	 2	    0

PostgreSQL测试

测试的表和数据:

create table t_time(
    id integer,
    back_time varchar(30),
    sc_sm_time timestamp
);
insert into t_time values(1,'2026-07-19 16:30:00','2026-07-19 15:00:00');
insert into t_time values(2,'2026-07-19 14:30:00','2026-07-19 15:00:00');


postgres=#  select * from t_time;
 id |      back_time      |     sc_sm_time
----+---------------------+---------------------
  1 | 2026-07-19 16:30:00 | 2026-07-19 15:00:00
  2 | 2026-07-19 14:30:00 | 2026-07-19 15:00:00
(2 行记录)

执行相同SQL:

SELECT id,
  CASE 
  WHEN to_date(back_time,'yyyy-mm-dd hh24:mi:ss')> sc_sm_time THEN (to_date(back_time,'yyyy-mm-dd hh24:mi:ss')-sc_sm_time)*24
  ELSE
  0
  END AS zysc_time
FROM t_time;

错误:  CASE 的类型 integerinterval 不匹配
第3...k_time,'yyyy-mm-dd hh24:mi:ss')> sc_sm_time THEN (to_date(ba...

会报错,错误信息为 CASE 的类型 integer 和 interval 不匹配。该语句在oracle和PG上有2个差异点:

(1)Oracle 和 PostgreSQL 的 to_date() 函数行为不同,oracle的to_date 包含年月日+时分秒,而pg只包含年月日

oracle:

SQL> select to_date('2026-07-19 14:30:00', 'yyyy-mm-dd hh24:mi:ss')
  2  from dual;

TO_DATE('2026-07-19
-------------------
2026-07-19 14:30:00

pg:

postgres=# select to_date('2026-07-19 14:30:00', 'yyyy-mm-dd hh24:mi:ss');
  to_date
------------
 2026-07-19
(1 行记录)

pg可以用to_timestamp来替代:

--PG: 查看实际数据(精确到秒)

SELECT 
  id, 
  back_time, 
  sc_sm_time,
  to_timestamp(back_time, 'YYYY-MM-DD HH24:MI:SS') as back_ts,
  sc_sm_time as sc_ts,
  to_timestamp(back_time, 'YYYY-MM-DD HH24:MI:SS') - sc_sm_time as diff
FROM t_time;

 id |      back_time      |     sc_sm_time      |        back_ts         |        sc_ts        |   diff
----+---------------------+---------------------+------------------------+---------------------+-----------
  1 | 2026-07-19 16:30:00 | 2026-07-19 15:00:00 | 2026-07-19 16:30:00+08 | 2026-07-19 15:00:00 | 01:30:00
  2 | 2026-07-19 14:30:00 | 2026-07-19 15:00:00 | 2026-07-19 14:30:00+08 | 2026-07-19 15:00:00 | -00:30:00
(2 行记录)

(2)对于oracle来说,两个date类型运算结果返回的是number类型,而pg的timestamp类型运算结果返回的是INTERVAL类型,CASE WHEN 要求返回值必须是同一类的数据类型,所以pg的会报错。

最终在oracle执行的sql,改成如下才能正常在pg上跑:

SELECT id,
  CASE WHEN to_timestamp(back_time,'yyyy-mm-dd hh24:mi:ss') >sc_sm_time
  THEN extract(epoch from(to_timestamp(back_time,'yyyy-mm-dd hh24:mi:ss')-sc_sm_time))/3600
  ELSE
  0
END AS zysc_time
FROM t_time;

CASE WHEN 所有返回值必须能够推导出统一的数据类型,否则会报错。
在这里插入图片描述

总结

CASE WHEN 是 SQL 中最常用的逻辑表达式之一,也是异构数据库迁移时最容易碰到的。本文揭示的两个案例:空字符串判空、日期运算类型问题,只是冰山一角。迁移工作的核心不只是"让 SQL 跑起来",也要让结果跟原来一致,需要多做测试。

更多内容,请点击踩过才懂:Oracle 与 PostgreSQL 的 CASE WHEN 暗坑实录 查看

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐