作者:@Neoest
日期:2025-11-28
关键词:Oracle、空字符串、NULL、WHERE 条件、踩坑、category != ‘’
1. 现象:一句看似人畜无害的 SQL,却翻车了
今天提测,测试小姐姐在群里突然炸了:
“业务后台题目分类全没了!”
我火速登上机器,发现罪魁祸首竟是一条简单到不能再简单的查询:
SELECT *
FROM question
WHERE category IS NOT NULL
AND category != ''
ORDER BY category ASC;
返回结果:0 行。
但 SELECT COUNT(*) FROM question WHERE category IS NOT NULL; 明明有 18 万 条记录!
2. 复现:最小化案例,一击即中
为了排除业务数据干扰,我现场建了一张空表,插入 4 条“极具代表性”的数据:
CREATE TABLE question(id INT, category VARCHAR2(100));
INSERT INTO question VALUES (1, NULL); -- ① 明确 NULL
INSERT INTO question VALUES (2, ''); -- ② 空字符串
INSERT INTO question VALUES (3, 'Java'); -- ③ 正常值
INSERT INTO question VALUES (4, 'Python'); -- ④ 正常值
COMMIT;
执行本节开头的查询,结果依旧:0 行。
3. 根源:Oracle 把空字符串当 NULL !官方文档实锤
在 Oracle 里,字符型空字符串(‘’)被等价于 NULL,这是自 1979 年就留下的“历史包袱”,与 ANSI SQL 标准背道而驰。
官方文档原文:
“Oracle Database treats an empty string as NULL, with the exception of the CHAR datatype …”
换句话说,'' 就是 NULL,NULL 不等于、不等于、不等于任何值(包括它自己)。
| 表达式 | 结果 | 原因 |
|---|---|---|
'' IS NULL | TRUE | Oracle 把 ‘’ 视为 NULL |
'' = '' | NULL | 两个 NULL 比较,结果还是 NULL |
category != '' | 永远不 TRUE | 等价于 category != NULL → 永远 UNKNOWN |
4. 验证:给你一段 PL/SQL,一眼看穿
SET SERVEROUT ON
DECLARE
v VARCHAR2(10) := '';
BEGIN
IF v IS NULL THEN
dbms_output.put_line('空字符串就是NULL!');
END IF;
IF '' != 'abc' THEN
dbms_output.put_line('这行不会打印,因为比较结果是UNKNOWN');
END IF;
END;
/
输出:
空字符串就是NULL!
5. 解决:三行代码,立竿见影
既然 != '' 无效,那我们就彻底抛弃“空字符串”这个概念,只跟 NULL 打交道:
-- ✅ 正确姿势
SELECT *
FROM question
WHERE category IS NOT NULL -- 过滤掉真正的 NULL(含 '')
ORDER BY category ASC;
如果你是从 MySQL/PostgreSQL 迁移过来的代码,务必全局扫描:
grep -n "!=" *.sql | grep "''"
把类似 col != '' 全部改成:
(col IS NOT NULL AND col <> ' ') -- 若还要排除空格
6. 延伸:CHAR 类型是例外吗?
是的,CHAR 因为定长,会右补空格,导致:
CREATE TABLE t(c CHAR(10));
INSERT INTO t VALUES ('');
SELECT '''' || c || '''' FROM t; -- 结果:' '(10 个空格)
所以 CHAR 的空字符串不会变成 NULL,但用 TRIM(c) 后又回归 NULL,极易踩第二遍坑,建议一律用 VARCHAR2。
7. 小结:记住三句话,告别加班
- Oracle 里,
''就是NULL,别再比较!= ''。 - 判断“有值”统一用
IS NOT NULL。 - 迁移脚本务必全局替换
!= ''→IS NOT NULL。
8. 一键体检脚本
送你一条“体检”SQL,批量找出库里所有可能踩坑的列:
SELECT table_name, column_name, data_type
FROM user_tab_columns
WHERE data_type LIKE '%CHAR%'
AND nullable = 'Y';
把结果集丢给研发,逐表 review,提前排雷,老板再也不用担心凌晨告警!
如果本文帮你省了一个通宵,欢迎点个 赞 + 收藏 + 转发,评论区聊聊你踩过的 Oracle 天坑!



2万+

被折叠的 条评论
为什么被折叠?



