目录
-
- [一、基本 JOIN 类型](#一、基本 JOIN 类型)
-
- [1. 单独一个 `JOIN` 等价于 `INNER JOIN`](#1. 单独一个
JOIN等价于INNER JOIN) - [2. `LEFT JOIN` / `RIGHT JOIN` 用法一致](#2.
LEFT JOIN/RIGHT JOIN用法一致) - [3. 关联键重复导致数据翻倍](#3. 关联键重复导致数据翻倍)
- [1. 单独一个 `JOIN` 等价于 `INNER JOIN`](#1. 单独一个
- [二、ODPS 与 Oracle 的主要差异](#二、ODPS 与 Oracle 的主要差异)
-
- [1. `(+)` 语法差异(重要)](#1.
(+)语法差异(重要)) - [2. 半连接示例:`LEFT SEMI JOIN`](#2. 半连接示例:
LEFT SEMI JOIN) - [3. `LEFT ANTI JOIN`(ODPS 特有)](#3.
LEFT ANTI JOIN(ODPS 特有))
- [1. `(+)` 语法差异(重要)](#1.
- [三、多表 JOIN 顺序与优化](#三、多表 JOIN 顺序与优化)
- 四、完整示例对比
- 五、注意事项汇总
ODPS(MaxCompute)的 JOIN 语法与 Oracle 高度相似,但在一些细节上存在差异。以下将逐一说明。
一、基本 JOIN 类型
| JOIN 类型 | ODPS 语法 | Oracle 语法 | 说明 |
|---|---|---|---|
| 内连接(INNER JOIN) | JOIN 或 INNER JOIN |
JOIN 或 INNER JOIN |
两者完全一致,只返回匹配的行。 |
| 左外连接(LEFT JOIN) | LEFT JOIN 或 LEFT OUTER JOIN |
LEFT JOIN 或 LEFT OUTER JOIN |
返回左表全部行,右表无匹配则为 NULL。 |
| 右外连接(RIGHT JOIN) | RIGHT JOIN 或 RIGHT OUTER JOIN |
RIGHT JOIN 或 RIGHT OUTER JOIN |
返回右表全部行,左表无匹配则为 NULL。 |
| 全外连接(FULL JOIN) | FULL JOIN 或 FULL OUTER JOIN |
FULL JOIN 或 FULL OUTER JOIN |
返回两表全部行,无匹配的一侧为 NULL。 |
| 交叉连接(CROSS JOIN) | CROSS JOIN |
CROSS JOIN |
返回笛卡尔积。 |
| 半连接 / 反连接 | LEFT SEMI JOIN、LEFT ANTI JOIN(ODPS特有) |
EXISTS / NOT EXISTS 或 IN / NOT IN |
ODPS 提供了专门的半连接语法,性能更好。 |
1. 单独一个 JOIN 等价于 INNER JOIN
sql
-- ODPS 与 Oracle 一致
SELECT *
FROM table_a a
JOIN table_b b ON a.id = b.id;
-- 等价于
SELECT *
FROM table_a a
INNER JOIN table_b b ON a.id = b.id;
2. LEFT JOIN / RIGHT JOIN 用法一致
sql
-- 左连接:保留左表所有记录
SELECT a.id, a.name, b.order_date
FROM customers a
LEFT JOIN orders b ON a.id = b.cust_id;
3. 关联键重复导致数据翻倍
无论 ODPS 还是 Oracle,只要 JOIN 的关联键在任一侧存在重复值,结果行数都可能翻倍(相当于一对多或多对多)。
sql
-- 左表:id=1 出现两次
-- 右表:id=1 出现三次
-- 结果会产生 2×3 = 6 行 id=1 的记录
SELECT *
FROM left_table a
JOIN right_table b ON a.id = b.id;
注意 :若需避免数据翻倍,可提前去重或使用
DISTINCT、聚合函数等。
二、ODPS 与 Oracle 的主要差异
| 差异点 | ODPS | Oracle |
|---|---|---|
旧式外连接语法((+)) |
不支持 (+) 语法 |
支持 WHERE a.id = b.id(+) 表示左外连接 |
| 半连接语法 | LEFT SEMI JOIN / LEFT ANTI JOIN |
无直接等价语法,需用 EXISTS / NOT EXISTS |
| JOIN 条件写法 | 必须使用 ON 子句,不支持 USING |
支持 USING(column) 简化相同列名关联 |
| NULL 比较行为 | 标准 SQL:NULL = NULL 结果为 NULL(不匹配) |
相同 |
| MAPJOIN 提示 | 支持 /*+ MAPJOIN(small_table) */ 优化小表 |
支持 /*+ USE_HASH(table) */ 等不同提示 |
1. (+) 语法差异(重要)
Oracle 允许在 WHERE 中使用 (+) 表示外连接,ODPS 不支持 ,必须使用标准的 LEFT/RIGHT/FULL JOIN。
sql
-- Oracle 旧式语法(不推荐,但可用)
SELECT *
FROM emp e, dept d
WHERE e.dept_id = d.dept_id(+); -- 左外连接
-- ODPS 必须改写为:
SELECT *
FROM emp e
LEFT JOIN dept d ON e.dept_id = d.dept_id;
2. 半连接示例:LEFT SEMI JOIN
ODPS 的 LEFT SEMI JOIN 只返回左表中与右表匹配的记录,且不返回右表任何列(类似 EXISTS)。
sql
-- ODPS:查询有订单的客户(不关心订单详情)
SELECT a.id, a.name
FROM customers a
LEFT SEMI JOIN orders b ON a.id = b.cust_id;
-- Oracle 等价写法:
SELECT a.id, a.name
FROM customers a
WHERE EXISTS (SELECT 1 FROM orders b WHERE a.id = b.cust_id);
3. LEFT ANTI JOIN(ODPS 特有)
返回左表中没有 匹配右表的记录(类似 NOT EXISTS)。
sql
-- ODPS:查询从未下过订单的客户
SELECT a.id, a.name
FROM customers a
LEFT ANTI JOIN orders b ON a.id = b.cust_id;
三、多表 JOIN 顺序与优化
- ODPS :优化器会自动重排 JOIN 顺序,但可通过
/*+ MAPJOIN(table) */强制将小表加载到内存。 - Oracle :优化器基于统计信息选择最优顺序,也可使用
ORDERED提示强制顺序。
MAPJOIN 示例(ODPS):
sql
SELECT /*+ MAPJOIN(dim_date) */ *
FROM fact_sales a
JOIN dim_date b ON a.date_key = b.date_key;
四、完整示例对比
场景:员工表(emp)与部门表(dept)左连接,获取所有员工及其部门名称。
ODPS:
sql
SELECT e.empno, e.ename, d.dname
FROM emp e
LEFT JOIN dept d ON e.deptno = d.deptno;
Oracle(标准语法相同,也支持旧式):
sql
-- 标准语法(推荐)
SELECT e.empno, e.ename, d.dname
FROM emp e
LEFT JOIN dept d ON e.deptno = d.deptno;
-- 旧式语法(仅 Oracle)
SELECT e.empno, e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno(+);
五、注意事项汇总
- 关联键重复导致数据膨胀:这是 SQL JOIN 的通用行为,并非 ODPS 或 Oracle 特有。务必检查关联键的唯一性。
- NULL 值处理 :
NULL与任何值(包括NULL)比较均为NULL,导致不匹配。若需将NULL视为相等,需使用NVL或COALESCE转换。 - 性能优化 :大表 JOIN 时,ODPS 中常用
MAPJOIN提示;Oracle 中则常用USE_HASH或USE_NL。 - 字符串类型隐式转换:ODPS 对隐式转换限制较严,建议显式转换类型;Oracle 较宽松但可能引发索引失效。
- 分区表 JOIN :ODPS 中 JOIN 前尽量裁剪分区(
WHERE partition_col = ...),可大幅减少数据扫描量。
ODPS 的 JOIN 核心功能与 Oracle 保持一致 ,只需注意 (+) 旧语法不支持、以及半连接语法的差异即可轻松迁移。建议统一使用标准 SQL 的 JOIN ... ON 语法。