首批通过分布式安全可靠测评,为关键业务系统打造
Optimized Hint 类型下的 Join Operation Hint
更新时间:2026-07-15 20:42:46
在 SQL 查询中,与联接操作相关的 Hint,包括启用或禁用特定联接算法的 Hint 如下所示:
| Hint 类型 | 描述 |
|---|---|
USE_MERGE |
当此 Hint 中指定的表作为连接的右表时,使用排序合并连接算法。它的反向操作是 NO_USE_MERGE。 |
NO_USE_MERGE |
当此 Hint 中指定的表作为连接的右表时,不使用排序合并连接算法。它的反向操作是 USE_MERGE。 |
USE_HASH |
当此 Hint 中指定的表作为连接的右表时,使用 HASH-JOIN 算法。它的反向操作是 NO_USE_HASH。 |
NO_USE_HASH |
当此 Hint 中指定的表作为连接的右表时,不使用 HASH-JOIN 算法。它的反向操作是 USE_HASH。 |
USE_NL |
当此 Hint 中指定的表作为连接的左表时,使用 NL-JOIN 算法。它的反向操作是 NO_USE_NL。 |
NO_USE_NL |
当此 Hint 中指定的表作为连接的左表时,不使用 NL-JOIN 算法。它的反向操作是 USE_NL。 |
PQ_DISTRIBUTE |
控制联接操作的数据分配方式。 |
PQ_MAP |
指定连接操作使用从节点映射策略。 |
USE_NL_MATERIALIZATION |
强制物化嵌套循环联接中的左表。它的反向操作是 NO_USE_NL_MATERIALIZATION。 |
NO_USE_NL_MATERIALIZATION |
防止物化嵌套循环联接中的左表。它的反向操作是 USE_NL_MATERIALIZATION。 |
PX_JOIN_FILTER |
指示优化器控制 HASH JOIN 使用 JOIN FILTER。它的反向操作是 NO_PX_JOIN_FILTER。 |
NO_PX_JOIN_FILTER |
指示优化器控制 HASH JOIN 禁用 JOIN FILTER。它的反向操作是 PX_JOIN_FILTER。 |
PX_PART_JOIN_FILTER |
用于指示优化器手动打开 PART FILTER。它的反向操作是 NO_PX_PART_JOIN_FILTER。 |
NO_PX_PART_JOIN_FILTER |
用于指示优化器手动关闭 PART FILTER。它的反向操作是 NO_PX_PART_JOIN_FILTER。 |
USE_MERGE Hint
USE_MERGE Hint 当此 Hint 中指定的表作为连接的右表时,使用排序合并连接算法。它的反向操作是 NO_USE_MERGE。
语法
/*+ USE_MERGE ( [ @queryblock ] tablespec [ tablespec ]... ) */
使用说明及注意事项
建议
USE_NL和USE_MERGHint 与LEADING或ORDEREDHint 一起使用。当被引用的表是联接的右表时,优化器将使用这些 Hint。
如果被引用的表是左表,则忽略 Hint。
USE_MERGE指定表作为右表时候使用MERGE JOIN算法。OceanBase 数据库使用
MERGE JOIN算法时必须有等值条件的join-condition,因此无等值条件的两个表做联接时,USE_MERGE失效。
示例
-- 使用 USE_MERGE Hint 来指示优化器使用排序合并联接算法(sort-merge join)执行查询
-- 对于 employees 和 departments 表的联接操作,employees 表将作为右表,departments 表作为左表
SELECT /*+ USE_MERGE(employees departments) */ *
FROM employees, departments
WHERE employees.department_id = departments.department_id;
NO_USE_MERGE Hint
NO_USE_MERGE Hint 指示优化器在使用指定表作为左表并联接到另一个行资源时排除 USE_MERGE Hint 使用的联接。它的反向操作是 USE_MERGE。
语法
/*+ NO_USE_MERGE ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 NO_USE_MERGE Hint 来指示优化器不要使用排序合并联接算法执行查询
-- 对于 employees 和 departments 表的联接操作,将排除使用 sort-merge join 算法
SELECT /*+ NO_USE_MERGE(e d) */ *
FROM employees e, departments d
WHERE e.department_id = d.department_id;
USE_HASH Hint
USE_HASH Hint 指定的表作为连接的右表时,使用 HASH-JOIN 算法。它的反向操作是 NO_USE_HASH。
语法
/*+ USE_HASH ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 USE_HASH Hint 来指示优化器使用哈希联接算法(HASH-JOIN)执行查询
-- 对于 orders 和 order_items 表的联接操作,orders 表将作为右表,order_items 表作为左表
SELECT /*+ USE_HASH(l h) */ *
FROM orders h, order_items l
WHERE l.order_id = h.order_id
AND l.order_id > 2400;
NO_USE_HASH Hint
NO_USE_HASH Hint 指定的表作为连接的右表时,不使用 HASH-JOIN 算法。它的反向操作是 USE_HASH。
语法
/*+ NO_USE_HASH ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 NO_USE_HASH Hint 来指示优化器不要使用哈希联接算法执行查询
-- 对于 employees 和 departments 表的联接操作,将排除使用 HASH-JOIN 算法
SELECT /*+ NO_USE_HASH(e d) */ *
FROM employees e, departments d
WHERE e.department_id = d.department_id;
USE_NL Hint
USE_NL Hint 指定的表作为连接的左表时,使用嵌套循环联接(NL-JOIN)算法。它的反向操作是 NO_USE_NL。
建议
USE_NL和USE_MERGHint 与LEADING或ORDEREDHint 一起使用。当被引用的表是联接的左表时,优化器将使用这些 Hint。
如果被引用的表是右表,则忽略 Hint。
语法
/*+ USE_NL ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
如下方查询示例所示,Hint 强制执行了嵌套循环,通过全表扫描访问了 orders 表并且将筛选条件 l.order_id = h.order_id 应用在了每一行。对于满足筛选条件的每一行,通过索引 order_id 访问 order_items。
-- 使用 USE_NL Hint 来指示优化器使用嵌套循环联接算法(NL-JOIN)执行查询
-- 对于 orders 和 order_items 表的联接操作,orders 表将作为右表,order_items 表作为左表
SELECT /*+ USE_NL(l h) */ h.customer_id, l.unit_price * l.quantity
FROM orders h, order_items l
WHERE l.order_id = h.order_id;
NO_USE_NL Hint
NO_USE_NL Hint 指定的表作为连接的左表时,不使用嵌套循环联接(NL-JOIN)算法。它的反向操作是 USE_NL。
语法
/*+ NO_USE_NL ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 NO_USE_NL Hint 来指示优化器不要使用嵌套循环联接算法执行查询
-- 对于 employees 和 departments 表的联接操作,将排除使用 NL-JOIN 算法
SELECT /*+ NO_USE_NL(e d) */ *
FROM employees e, departments d
WHERE e.department_id = d.department_id;
PQ_DISTRIBUTE Hint
PQ_DISTRIBUTE Hint 用于并行执行查询时,提示优化器如何在并行查询的生产者(Producer,负责生成查询结果的行数据)和消费者(Consumer,负责接收并处理这些行数据)服务器之间分配数据。您可以使用 PQ_DISTRIBUTE Hint 控制行数据在连接操作或负载操作中的分布方式。
在并行查询场景下,尤其是在需要处理大量数据的情况下,PQ_DISTRIBUTE 可以优化资源使用,提高查询性能。
语法
/*+ PQ_DISTRIBUTE
( [ @queryblock ] tablespec
{ distribution | outer_distribution inner_distribution }
) */
控制联接的分配
您可以通过指定两种分配方法来控制联接的分发方式。
如语法中的下部分支所示:
outer_distribution指定左表的数据分发方式。inner_distribution指定右表的数据分发方式。
分发方式包括 HASH、BROADCAST、PARTITION 和 NONE。只有下表中的 6 种分发方式组合是有效的:
| 分发方式 | 说明 |
|---|---|
| HASH, HASH | 使用联接键上的哈希函数,将每个表的行映射到查询服务器。映射完成后,每个查询服务器都会在一对结果分区之间执行联接。 当表的大小可比较并且联接操作是通过哈希联接或排序合并联接实现时,建议使用此分发方式。 |
| BROADCAST, NONE | 右表的所有行都广播到每个查询服务器。左表行是随机分区的。 当右表与左表相比非常小时,建议使用此分布方法。通常,当左表大小乘以查询服务器的数量大于右表大小时,也建议使用此分发方式。 |
| NONE, BROADCAST | 左表的所有行都广播给每个查询服务器。右表行是随机分区的。 当左表与右表相比非常小时,建议使用此分布方法。通常,当左表大小乘以查询服务器的数量小于右表大小时,也建议使用此分发方式。 |
| PARTITION, NONE | 左表的行使用以右表的分区进行映射。左表必须在联接键上进行分区。 当右表的分区数等于或几乎等于查询服务器数的倍数时,建议使用此分发方式。例如,有 14 个分区和 15 个查询服务器。 注意 如果左表未分区或未在分区键上等分联接时,优化器将忽略此 Hint。 |
| NONE, PARTITION | 右表的行使用左表的分区进行映射。右表必须在联接键上进行分区。 当右表的分区数等于或几乎等于查询服务器数的倍数时,建议使用此分发方式。例如,有 14 个分区和 15 个查询服务器。 注意 如果右表未在分区键上进行分区或未等分联接时,优化器将忽略此 Hint。 |
| NONE, NONE | 每个查询服务器在一对匹配的分区之间执行联接操作,每个表中都有一个。两个表必须在联接键上等分。 |
示例
如下方查询示例所示,指定使用哈希联接来联接两个表 r 和 s,以下查询包含使用哈希分发方式的 Hint:
SELECT /*+ORDERED PQ_DISTRIBUTE(s HASH, HASH) USE_HASH (s) */ column_list
FROM r, s
WHERE r.c = s.c;
如果要广播右表 r,包含 Hint 的查询语句如下:
SELECT /*+ORDERED PQ_DISTRIBUTE(s BROADCAST, NONE) USE_HASH (s) */ column_list
FROM r, s
WHERE r.c = s.c;
USE_NL_MATERIALIZATION Hint
USE_NL_MATERIALIZATION Hint 强制指示优化器指定表为左表(子树)时生成一个物化算子来缓存数据。它的反向操作是 NO_USE_NL_MATERIALIZATION。
语法
/*+ USE_NL_MATERIALIZATION ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 USE_NL_MATERIALIZATION Hint 来指示优化器在嵌套循环联接中物化 departments 表
SELECT /*+ USE_NL_MATERIALIZATION(departments) */ *
FROM employees, departments
WHERE employees.department_id = departments.department_id;
NO_USE_NL_MATERIALIZATION Hint
NO_USE_NL_MATERIALIZATION Hint 强制指示优化器在指定表为左表(子树)时避免生成一个物化算子来缓存数据。它的反向操作是 USE_NL_MATERIALIZATION。
语法
/*+ NO_USE_NL_MATERIALIZATION ( [ @queryblock ] tablespec [ tablespec ]... ) */
示例
-- 使用 NO_USE_NL_MATERIALIZATION Hint 来阻止优化器在嵌套循环联接中物化 departments 表
-- 这意味着每次循环联接时都会重新访问 departments 表的数据,而不是使用缓存的物化结果
SELECT /*+ NO_USE_NL_MATERIALIZATION(departments) */ *
FROM employees, departments
WHERE employees.department_id = departments.department_id;
连接过滤(Join Filter)Hint
Join Filter 相关的 Hint 总共有四种,前两个用于控制普通的 Join Filter,后两个用于控制部分 Join Filter:
PX_JOIN_FILTERHintNO_PX_JOIN_FILTERHintPX_PART_JOIN_FILTERHintNO_PX_PART_JOIN_FILTERHint
需要注意的是,这四个 Hint 在并行执行环境中生效,在非并行环境中不会有明显效果。
它们的语法和参数解释如下:
PX_JOIN_FILTER Hint
在并行执行环境中,PX_JOIN_FILTER Hint 指示优化器控制 HASH JOIN 使用 JOIN FILTER。使用此 HINT 可以指定特定表作为 hash join 的右表时使用 join filter 做执行期的过滤。它的反向操作是 NO_PX_JOIN_FILTER。
语法
/*+ PX_JOIN_FILTER ( [ @qb_name ] filter_table [ left_tables ] [real_filter_table]) */
参数解释
qb_name:指定 hint 生效 query block,可选参数。filter_table:描述下压 Join Filter 的单表, 如果是子查询,这里应该是 view 的名字。left_tables:指定分配 Join Filter 时 HASH-JOIN 的左表, 可选参数。real_filter_table:子查询中实际下压 Join Filter 的单表。
NO_PX_JOIN_FILTER Hint
NO_PX_JOIN_FILTER Hint 用于指示优化器控制 HASH JOIN 禁用 JOIN FILTER。它的反向操作是 PX_JOIN_FILTER。
语法
/*+ NO_PX_JOIN_FILTER( table ) */
PX_PART_JOIN_FILTER Hint
PX_PART_JOIN_FILTER Hint 用于指示优化器手动打开 PART FILTER。它的反向操作是 NO_PX_PART_JOIN_FILTER。
语法
/*+ PX_PART_JOIN_FILTER ( [ @qb_name ] filter_table [ left_tables ] [real_filter_table]) */
NO_PX_PART_JOIN_FILTER Hint
NO_PX_PART_JOIN_FILTER Hint 用于指示优化器手动关闭 PART FILTER。它的反向操作是 PX_PART_JOIN_FILTER。
语法
/*+ NO_PX_PART_JOIN_FILTER (table) */
应用场景
连接过滤(Join Filter)Hint 的这四种 Hint(PX_JOIN_FILTER、NO_PX_JOIN_FILTER、PX_PART_JOIN_FILTER、NO_PX_PART_JOIN_FILTER)通常与 leading 和 use_hash Hint 一起使用,如果不与 leading 和 use_hash 联合使用,容易因生成其它连接顺序和连接算法而导致这些 Hint 失效。
通用场景
Join Filter 类型的 Hint 一般与 LEADING 和 USE_HASH 一同使用,否则可能因生成了不同的连接顺序或连接算法而导致失效。
首先,创建分区表:
CREATE TABLE t1 (
c1 INT,
c2 INT,
c3 INT,
c4 INT
) PARTITION BY HASH(c1) PARTITIONS 10;
强制使用 Join Filter
可以使用以下 SQL 强制应用 Join Filter:
EXPLAIN SELECT
/*+ PARALLEL(2) LEADING(a b) USE_HASH(b) PQ_DISTRIBUTE(b BC2HOST NONE)
PX_JOIN_FILTER(b)
PX_PART_JOIN_FILTER(b)
*/ *
FROM t1 a, t1 b WHERE a.c1 = b.c1;
或:
EXPLAIN SELECT
/*+ PARALLEL(2) LEADING(a b) USE_HASH(b) PQ_DISTRIBUTE(b BC2HOST NONE)
PX_JOIN_FILTER(b a)
PX_PART_JOIN_FILTER(b a)
*/ *
FROM t1 a, t1 b WHERE a.c1 = b.c1;
输出的执行计划示例如下:
===============================================================
| ID | OPERATOR | NAME | EST. ROWS | COST |
---------------------------------------------------------------
| 0 | PX COORDINATOR | | 1 | 456 |
| 1 | EXCHANGE OUT DISTR | :EX10001| 1 | 456 |
| 2 | SHARED HASH JOIN | | 1 | 455 |
| 3 | JOIN FILTER CREATE | :BF0001 | 1 | 228 |
| 4 | PART JOIN FILTER CREATE | :BF0000 | 1 | 228 |
| 5 | EXCHANGE IN DISTR | | 1 | 228 |
| 6 | EXCHANGE OUT DISTR (BC2HOST)| :EX10000| 1 | 228 |
| 7 | PX BLOCK ITERATOR | | 1 | 228 |
| 8 | TABLE SCAN | a | 1 | 228 |
| 9 | JOIN FILTER USE | :BF0001 | 1 | 228 |
| 10 | PX BLOCK HASH JOIN-FILTER | :BF0000 | 1 | 228 |
| 11 | TABLE SCAN | b | 1 | 228 |
===============================================================
多表场景
对于三表连接,指定左表为 a 时,可以为右表 c 生成 Join Filter:
EXPLAIN SELECT
/*+ PARALLEL(2) LEADING(a (b c)) USE_HASH(c (b c)) PQ_DISTRIBUTE((b c) BC2HOST NONE) PQ_DISTRIBUTE(c BC2HOST NONE)
NO_PX_JOIN_FILTER(c)
NO_PX_JOIN_FILTER(b)
NO_PX_PART_JOIN_FILTER(c)
NO_PX_PART_JOIN_FILTER(b)
PX_JOIN_FILTER(c a)
*/ *
FROM t1 a, t1 b, t1 c WHERE a.c1 = c.c1 AND b.c1 = c.c1;
输出的执行计划示例如下:
===============================================================
| ID | OPERATOR | NAME | EST. ROWS | COST |
---------------------------------------------------------------
| 0 | PX COORDINATOR | | 1 | 684 |
| 1 | EXCHANGE OUT DISTR | :EX10002| 1 | 683 |
| 2 | SHARED HASH JOIN | | 1 | 683 |
| 3 | JOIN FILTER CREATE | :BF0000 | 1 | 228 |
| 4 | EXCHANGE IN DISTR | | 1 | 228 |
| 5 | EXCHANGE OUT DISTR (BC2HOST) | :EX10000| 1 | 228 |
| 6 | PX BLOCK ITERATOR | | 1 | 228 |
| 7 | TABLE SCAN | a | 1 | 228 |
| 8 | SHARED HASH JOIN | | 1 | 455 |
| 9 | EXCHANGE IN DISTR | | 1 | 228 |
| 10 | EXCHANGE OUT DISTR (BC2HOST) | :EX10001| 1 | 228 |
| 11 | PX BLOCK ITERATOR | | 1 | 228 |
| 12 | TABLE SCAN | b | 1 | 228 |
| 13 | JOIN FILTER USE | :BF0000 | 1 | 228 |
| 14 | PX BLOCK ITERATOR | | 1 | 228 |
| 15 | TABLE SCAN | c | 1 | 228 |
===============================================================
同样,对于指定左表为 b 的三表连接,可以为右表 c 生成 Join Filter:
EXPLAIN SELECT
/*+ PARALLEL(2) LEADING(a (b c)) USE_HASH(c (b c)) PQ_DISTRIBUTE((b c) BC2HOST NONE) PQ_DISTRIBUTE(c BC2HOST NONE)
NO_PX_JOIN_FILTER(c)
NO_PX_JOIN_FILTER(b)
NO_PX_PART_JOIN_FILTER(c)
NO_PX_PART_JOIN_FILTER(b)
PX_JOIN_FILTER(c b)
*/ *
FROM t1 a, t1 b, t1 c WHERE a.c1 = c.c1 AND b.c1 = c.c1;
输出的执行计划示例如下:
===============================================================
| ID | OPERATOR | NAME | EST. ROWS | COST |
---------------------------------------------------------------
| 0 | PX COORDINATOR | | 1 | 684 |
| 1 | EXCHANGE OUT DISTR | :EX10002| 1 | 683 |
| 2 | SHARED HASH JOIN | | 1 | 683 |
| 3 | EXCHANGE IN DISTR | | 1 | 228 |
| 4 | EXCHANGE OUT DISTR (BC2HOST) | :EX10000| 1 | 228 |
| 5 | PX BLOCK ITERATOR | | 1 | 228 |
| 6 | TABLE SCAN | a | 1 | 228 |
| 7 | SHARED HASH JOIN | | 1 | 455 |
| 8 | JOIN FILTER CREATE | :BF0000 | 1 | 228 |
| 9 | EXCHANGE IN DISTR | | 1 | 228 |
| 10 | EXCHANGE OUT DISTR (BC2HOST) | :EX10001| 1 | 228 |
| 11 | PX BLOCK ITERATOR | | 1 | 228 |
| 12 | TABLE SCAN | b | 1 | 228 |
| 13 | JOIN FILTER USE | :BF0000 | 1 | 228 |
| 14 | PX BLOCK ITERATOR | | 1 | 228 |
| 15 | TABLE SCAN | c | 1 | 228 |
=======================================================================
Hint 冲突处理
PX_JOIN_FILTER 和 NO_PX_JOIN_FILTER 可以依据是否指定了左表 left_tables 有四种合法形式。根据优先级,按如下表进行匹配使用:
| Hint | 功能 |
|---|---|
| NO_PX_JOIN_FILTER( a (b c) ) | 当左表为 (b c) 时,禁止对右表中的 a 使用 join filter |
| PX_JOIN_FILTER( a (b c) ) | 当左表为 (b c) 时,对右表中的 a 使用 join filter |
| NO_PX_JOIN_FILTER( a ) | 对任意连接左表,禁止对右表中的 a 使用 join filter |
| PX_JOIN_FILTER( a ) | 对任意连接左表,对右表中的 a 使用 join filter |
PX_PART_JOIN_FILTER 和 NO_PX_PART_JOIN_FILTER 的冲突处理与 PX_JOIN_FILTER 相同。