Oracle SQL 性能调优案例
Oracle SQL 性能调优案例
适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
SQL 性能调优实战案例[1]:
内容:
- 全表扫描
- 索引选择
- JOIN 优化
- 绑定变量
- 子查询
详细见:Oracle SQL 调优最佳实践。
2. 案例 1:全表扫描
2.1 问题
SELECT * FROM employees WHERE UPPER(name) = 'SMITH';
-- 全表扫描
2.2 分析
- 函数阻止索引
- 统计信息旧
2.3 优化
-- 1. 函数索引
CREATE INDEX idx_emp_upper_name ON employees(UPPER(name));
-- 2. 重新统计
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMPLOYEES');
-- 3. 验证
EXPLAIN PLAN FOR SELECT ...;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY));
3. 案例 2:JOIN 优化
3.1 问题
SELECT e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
-- Hash Join,慢
3.2 分析
- 大表 JOIN
- 无索引
- 小结果集
3.3 优化
-- 1. 索引
CREATE INDEX idx_emp_dept ON employees(dept_id);
-- 2. Nested Loop
SELECT /*+ USE_NL(e d) INDEX(e idx_emp_dept) */
e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id AND e.id = 100;
-- 3. 验证
EXPLAIN PLAN FOR ...;
详细见:Oracle JOIN 连接方式。
4. 案例 3:子查询
4.1 问题
SELECT * FROM employees
WHERE dept_id IN (SELECT id FROM departments WHERE location = 'NY');
-- 慢
4.2 优化
-- 1. EXISTS(大表)
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.id = e.dept_id AND d.location = 'NY'
);
-- 2. JOIN
SELECT e.* FROM employees e, departments d
WHERE e.dept_id = d.id AND d.location = 'NY';
详细见:Oracle 子查询与 EXISTS。
5. 案例 4:绑定变量
5.1 问题
-- 字符串拼接
EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id = ' || v_id;
-- 每次 hard parse
5.2 优化
-- 绑定变量
EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id = :id' USING v_id;
-- 减少解析
-- Shared Pool 高效
详细见:Oracle PL/SQL 动态 SQL。
6. 案例 5:LIKE 查询
6.1 问题
SELECT * FROM employees WHERE name LIKE '%Smith%';
-- 全表扫描
6.2 优化
-- 1. 前缀匹配
SELECT * FROM employees WHERE name LIKE 'Smith%';
-- 索引有效
-- 2. 全文索引
CREATE INDEX idx_emp_text ON employees(name) INDEXTYPE IS CTXSYS.CONTEXT;
SELECT * FROM employees WHERE CONTAINS(name, 'Smith') > 0;
-- 3. 反向
SELECT * FROM employees WHERE name LIKE '%Smith';
-- REVERSE 索引
CREATE INDEX idx_emp_rev ON employees(REVERSE(name));
SELECT * FROM employees WHERE REVERSE(name) LIKE REVERSE('Smith%');
详细见:Oracle 全文检索。
7. 案例 6:OR 优化
7.1 问题
SELECT * FROM employees WHERE id = 1 OR salary > 10000;
-- 可能全表
7.2 优化
-- 1. UNION ALL
SELECT * FROM employees WHERE id = 1
UNION ALL
SELECT * FROM employees WHERE salary > 10000 AND id != 1;
-- 2. IN
SELECT * FROM employees WHERE id IN (1, 2, 3);
-- 3. 索引合并(自动)
8. 案例 7:分页优化
8.1 问题
-- 旧方式
SELECT * FROM (
SELECT ROWNUM rn, t.* FROM (
SELECT * FROM employees ORDER BY id
) t WHERE ROWNUM <= 100010
) WHERE rn > 100000;
-- 大偏移慢
8.2 优化
-- 12c+ FETCH
SELECT * FROM employees ORDER BY id
OFFSET 100000 ROWS FETCH NEXT 10 ROWS ONLY;
-- 键集分页
SELECT * FROM employees
WHERE id > :last_id
ORDER BY id
FETCH FIRST 10 ROWS ONLY;
详细见:Oracle 12c 新 SQL 特性。
9. 案例 8:DISTINCT
9.1 问题
SELECT DISTINCT dept_id FROM employees;
-- 排序去重
9.2 优化
-- 1. GROUP BY(有时更优)
SELECT dept_id FROM employees GROUP BY dept_id;
-- 2. EXISTS
SELECT d.id FROM departments d
WHERE EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.id);
10. 案例 9:UNION
10.1 问题
SELECT id FROM a UNION SELECT id FROM b;
-- 排序去重
10.2 优化
-- UNION ALL(如无重复)
SELECT id FROM a UNION ALL SELECT id FROM b;
详细见:Oracle SQL 集合操作。
11. 案例 10:统计信息
11.1 问题
-- 执行计划不准
SELECT * FROM employees WHERE dept_id = 10;
-- 估算 100 行,实际 10000 行
11.2 优化
-- 1. 收集统计
EXEC DBMS_STATS.GATHER_TABLE_STATS(
'SCOTT', 'EMPLOYEES',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
cascade => TRUE
);
-- 2. 直方图
EXEC DBMS_STATS.GATHER_TABLE_STATS(
'SCOTT', 'EMPLOYEES',
method_opt => 'FOR COLUMNS dept_id SIZE 254'
);
-- 3. 验证
EXPLAIN PLAN FOR ...;
详细见:Oracle 直方图与统计信息。
12. 案例 11:SQL Profile
12.1 问题
- 统计正确
- 但执行计划仍差
12.2 优化
-- 1. SQL Tuning Advisor
EXEC DBMS_SQLTUNE.CREATE_TUNING_TASK(sql_id => '&sql_id');
EXEC DBMS_SQLTUNE.EXECUTE_TUNING_TASK('TASK_NAME');
-- 2. 接受 Profile
EXEC DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(
task_name => 'TASK_NAME',
name => 'profile_1',
force_match => TRUE
);
详细见:Oracle SQL 调优顾问。
13. 案例 12:SQL Plan Baseline
13.1 问题
- 计划不稳定
- 时好时坏
13.2 优化
-- 1. 捕获
ALTER SYSTEM SET optimizer_capture_sql_plan_baselines = TRUE;
-- 2. 加载
DECLARE
pls PLS_INTEGER;
BEGIN
pls := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
sql_id => '&sql_id',
plan_hash_value => 12345
);
END;
/
-- 3. 固定
EXEC DBMS_SPM.ALTER_SQL_PLAN_BASELINE(...);
详细见:Oracle SQL Plan Baseline 基线。
14. 案例 13:并行
14.1 问题
-- 大表查询慢
SELECT COUNT(*) FROM big_table;
14.2 优化
-- 并行
SELECT /*+ PARALLEL(t 8) */ COUNT(*) FROM big_table t;
-- 表并行
ALTER TABLE big_table PARALLEL 8;
详细见:Oracle 并行查询。
15. 案例 14:物化视图
15.1 问题
-- 复杂聚合
SELECT dept_id, AVG(salary) FROM employees GROUP BY dept_id;
-- 每次计算
15.2 优化
-- 1. 物化视图
CREATE MATERIALIZED VIEW mv_dept_avg
REFRESH COMPLETE ON DEMAND
ENABLE QUERY REWRITE
AS
SELECT dept_id, AVG(salary) AS avg_sal
FROM employees GROUP BY dept_id;
-- 2. 自动重写
ALTER SESSION SET query_rewrite_enabled = TRUE;
详细见:Oracle 物化视图与查询重写。
16. 案例 15:分区
16.1 问题
-- 大表查询
SELECT * FROM sales WHERE sale_date = DATE '2026-07-21';
-- 全表
16.2 优化
-- 1. 分区表
CREATE TABLE sales (...)
PARTITION BY RANGE (sale_date) (...);
-- 2. 分区裁剪
SELECT * FROM sales WHERE sale_date = DATE '2026-07-21';
-- 仅扫一个分区
详细见:Oracle 分区表设计。
17. 调优流程
17.1 识别
- AWR TOP SQL
- ASH 实时
- 监控告警
17.2 分析
- 执行计划
- 统计信息
- 等待事件
17.3 优化
- 索引
- 统计
- Hint
- Profile
- Baseline
17.4 验证
- 性能对比
- 业务测试
18. 最佳实践
- 统计信息:基础
- 索引合理:覆盖
- 绑定变量:减少解析
- EXISTS/IN 选择:数据量
- 分页 FETCH:12c+
- 物化视图:聚合
- 分区:大表
- 并行:大查询
- Profile/Baseline:稳定
- 测试:验证
19. 参考资料
[1] Oracle Database SQL Tuning Guide 19c https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/