Oracle 的"老写法"本质上是 ANSI SQL:1989 / SQL-92 之前 的风格,在金融、电信、银行系统里非常常见。下面按类型 + 示例 + 现代等价写法 + 踩坑点给你系统梳理一遍,和你刚才那条 SQL 属于同一个"时代产物"。
一、隐式连接(Implicit JOIN)✅ 你刚遇到的
老写法
sql
SELECT *
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO;
新写法
sql
SELECT *
FROM EMP E
JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
📌 本质 :逗号 = CROSS JOIN,WHERE = 过滤条件
📌 风险:少写一个条件就变笛卡尔积
二、(+) 外连接(Oracle 独有)
这是 Oracle 最著名的"老古董"。
左外连接(老)
sql
SELECT E.ENAME, D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO(+);
左外连接(新)
sql
SELECT E.ENAME, D.DNAME
FROM EMP E
LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
右外连接(老)
sql
SELECT E.ENAME, D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO(+) = D.DEPTNO;
规则口诀(非常重要)
(+)写在"补空的一边"
❌ 不支持:
- 全外连接
FULL OUTER JOIN - 多个
OR条件 - 自连接 +
(+)
✅ Oracle 官方建议:12c 以后尽量不用 (+)
三、DECODE 替代 CASE(老牌条件表达式)
老写法
sql
SELECT
DECODE(SEX,
'1', '男',
'2', '女',
'未知'
) AS SEX_DESC
FROM EMP;
新写法
sql
SELECT
CASE SEX
WHEN '1' THEN '男'
WHEN '2' THEN '女'
ELSE '未知'
END AS SEX_DESC
FROM EMP;
📌 DECODE 是 Oracle 私有函数
📌 CASE 是 ANSI 标准,可读性强
四、NVL vs COALESCE
老写法
sql
SELECT NVL(COMM, 0) FROM EMP;
新写法
sql
SELECT COALESCE(COMM, 0) FROM EMP;
| 对比项 | NVL | COALESCE |
|---|---|---|
| 标准 | Oracle 私有 | ANSI SQL |
| 参数个数 | 2 个 | ≥2 个 |
| 短路求值 | ❌ | ✅ |
📌 COALESCE 更安全,尤其跨数据库
五、SYSDATE + 数字 = 日期运算(老习惯)
老写法
sql
SELECT SYSDATE + 1 FROM DUAL; -- 明天
SELECT SYSDATE - 1/24 FROM DUAL; -- 一小时前
新写法(更语义化)
sql
SELECT SYSDATE + INTERVAL '1' DAY FROM DUAL;
SELECT SYSDATE - INTERVAL '1' HOUR FROM DUAL;
📌 老写法在金融计息、账期计算中极常见
📌 新写法可读性更好,但性能没差别
六、ROWNUM 分页(经典老写法)
老写法
sql
SELECT *
FROM (
SELECT A.*, ROWNUM RN
FROM (
SELECT * FROM EMP ORDER BY HIREDATE DESC
) A
WHERE ROWNUM <= 20
)
WHERE RN > 10;
新写法(12c+)
sql
SELECT *
FROM EMP
ORDER BY HIREDATE DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
📌 ROWNUM 是 Oracle 特有
📌 OFFSET / FETCH 是 ANSI SQL
七、SELECT COUNT(*) INTO V_CNT FROM ... WHERE ROWNUM=1
老写法(判断是否存在)
sql
SELECT COUNT(*)
INTO V_CNT
FROM EMP
WHERE DEPTNO = 10
AND ROWNUM = 1;
新写法
sql
SELECT 1
INTO V_FLAG
FROM EMP
WHERE DEPTNO = 10
FETCH FIRST 1 ROWS ONLY;
📌 老写法是为了性能优化(找到一条就停)
八、EXISTS 里的 SELECT 1(老习惯)
老写法
sql
SELECT *
FROM EMP E
WHERE EXISTS (
SELECT 1
FROM DEPT D
WHERE D.DEPTNO = E.DEPTNO
);
说明
SELECT 1是历史习惯- Oracle 实际不关心 SELECT 列表
SELECT NULL也一样
📌 这是"老派写法",不是错误
九、|| 字符串拼接(Oracle 传统)
老写法
sql
SELECT 'A' || 'B' FROM DUAL;
新写法(ANSI)
sql
SELECT CONCAT('A', 'B') FROM DUAL;
📌 || 是 Oracle / PostgreSQL 风格
📌 CONCAT 在 MySQL / SQL Server 中更通用
十、HINT 写在注释里(Oracle 特色)
老写法
sql
SELECT /*+ INDEX(E EMP_IDX) */
*
FROM EMP E;
📌 注释不是注释,是优化器指令
📌 这种"伪装成注释的语法"是 Oracle 独有
十一、你这条 SQL 里还藏着的"老味儿"
结合你刚才的 SQL,其实还有这些老特征:
| 特征 | 老/新 |
|---|---|
| 逗号 + WHERE 关联 | 老 |
| LAG 窗口函数 | 新 |
| 多层子查询 | 老 |
| GROUP BY 长字段拼接 | 老 |
| 无 WITH(CTE) | 老 |
| 无 JOIN ON | 老 |
十二、总结一张对照表(速查)
| 场景 | 老写法 | 新写法 |
|---|---|---|
| 内连接 | , + WHERE |
JOIN ... ON |
| 外连接 | (+) |
LEFT/RIGHT JOIN |
| 条件分支 | DECODE |
CASE |
| 空值处理 | NVL |
COALESCE |
| 分页 | ROWNUM |
OFFSET/FETCH |
| 存在判断 | COUNT(*)+ROWNUM=1 |
EXISTS + FETCH |
十三、给你的实战建议(很实用)
- 读老代码:必须会老写法
- 写新代码:优先用 ANSI SQL
- 重构老 SQL :
- 先不改逻辑
- 只把
, + WHERE→JOIN ON
- 性能别乱动:老 SQL 很多"反直觉写法"是为性能服务的