首批通过分布式安全可靠测评,为关键业务系统打造
使用 Query Block Name 和 QB_NAME Hint 控制查询优化
更新时间:2026-07-05 14:35:07
OceanBase 数据库的优化器对查询进行分析处理时,会根据查询的结构分解为多个查询块(Query Block),每个块拥有唯一的查询块名(Query Block Name,简称 QB Name)。如果不手动指定,优化器则自动生成 QB Name。
QB_NAME Hint 的语法与规则
可以使用 QB_NAME Hint 显式为查询块分配 QB Name,语法如下:
/*+ QB_NAME(qb_name) */
下面的例子中,外层查询块被命名为 qb1,内联视图(视图 view_1 内)为 qb2:
SELECT /*+ QB_NAME(qb1) */ * FROM (SELECT /*+ QB_NAME(qb2) */ * FROM t1) view_1;
使用说明与注意事项:
- QB Name 不区分大小写。
- QB Name 不能包含特殊字符。
- QB Name 限制长度至多 20 个字符。
- 不能对同一查询块指派多个 QB Name 或不同块使用同一 QB Name。
如何使用 Query Block Name
定义 Query Block Hint 的作用范围
Query Block Hint 允许在 Hint 内通过指定 Query Block Name 来定义其作用范围。
如下例所示,Hint index(@qb2 t1 idx) 虽在查询块 qb1 声明,却指定其在查询块 qb2 生效:
SELECT /*+ INDEX(@qb2 t1 idx) QB_NAME(qb1) */ * FROM (SELECT /*+ NO_MERGE QB_NAME(qb2) */ * FROM t1) view_1;
指定查询中 Hint 作用对象
指定查询中 Hint 作用对象,即对查询中 Hint 的对象进行精确控制。
通过在 Hint 中使用 Query Block Name,可以精确指定作用对象。
如下例所示,使用 leading Hint 时,如果未明确指定表的 Query Block Name,则可能导致 Hint 无效:
SELECT /*+ LEADING(t1@sel$1 t1@sel$2) */ * FROM t1, (SELECT * FROM t1) v WHERE t1.c2 = v.c2;
Query Plan
===========================
|ID|OPERATOR |NAME|
---------------------------
|0 |HASH JOIN | |
|1 |├─TABLE FULL SCAN|t1 |
|2 |└─TABLE FULL SCAN|t1 |
===========================
Query Block Name 自动生成规则
当未指定 QB Name 时,优化器按照以下格式自动生成 Query Block Name:
query_block_type_str$idx
其中 query_block_type_str$ 为标识 Query Block 类型的字符串,idx 为标识该 Query Block 的序号。
下表展示了不同类型的 Query Block 自动生成的名称示例:
| 查询块 | 示例 |
|---|---|
| SELECT | SEL$1 |
| UNION ALL/UNION/INTERSECT/MINUS | SET$1 |
| INSERT | INS$1 |
| UPDATE | UPD$1 |
| DELETE | DEL$1 |
| MERGE | MRG$1 |
| REPLACE | REP$1 |
| INSERT ALL | INS_ALL$1 |
如下面的例子所示,UNION 会导致优化器为包含这些操作的查询块自动生成 Query Block Name。例如,在使用 UNION 的查询中,该查询块自动被命名为 SET$1:
SELECT /*+ USE_HASH_SET(@SET$1) */ c1, c2 FROM t1
UNION
SELECT c1, c2 FROM t2;
在这个例子中,Hint /*+USE_HASH_SET(@SET$1) */ 指示优化器为 UNION 操作使用 HASH DISTINCT SET 算法。
复杂查询中的 Query Block Name
在解析更复杂的查询时,优化器会按照从外到内的顺序为每个查询块分配递增的序号。如果移除手动指定的 QB_NAME Hint,自动生成的 Query Block Names 会如下:
SELECT /*+ QB_NAME(qb1) */ * FROM (SELECT /*+ QB_NAME(qb2) */ * FROM t1) v;
在去掉 QB_NAME Hint 后,外层查询块 qb1 的自动生成名字将是 SEL$1,而内层块 qb2 的名字将是 SEL$2。
查询改写与 Query Block Name 变化
当查询块触发查询改写时,其 Query Block Name 可以改变。通过使用 EXPLAIN EXTENDED 命令,可以在查询计划的 qb name trace 中跟踪这些变化。
要确认查询改写之前的自动生成 Query Block Name,可以在查询中包含 NO_QUERY_TRANSFORMATION Hint 来禁止所有查询改写,然后使用 EXPLAIN OUTLINE 来获取 OUTLINE DATA。通过对 OUTLINE DATA 的 FULL 、 INDEX 等 Query Block Hint 的分析,可以验证每个 Query Block 的 Name。
例如,在此查询中 OUTLINE DATA 的 FULL(@"SEL$2" "test"."t1"@"SEL$2") 表明了内联视图 v 中的 Query Block Name 为 SEL$2:
SELECT /*+ NO_QUERY_TRANSFORMATION */ COUNT(*)
FROM (SELECT * FROM t1) v GROUP BY c1;