踩过才懂:Oracle 与 PostgreSQL 的 CASE WHEN 暗坑实录
很多人认为 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 的类型 integer 和 interval 不匹配
第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 暗坑实录 查看
更多推荐

所有评论(0)