摘要:你是不是经常遇到SQL跑得比蜗牛还慢?改SQL改到怀疑人生?别慌!今天带你用“上帝视角”操控执行计划,让SQL性能起飞!就算你是SQL新人,看完也能秒变调优达人!
想象一下:你要从北京去上海,导航给你规划了三条路线:
执行计划就是数据库的“导航路线”,优化器(导航APP)会根据“路况”(统计信息)自动选路线。但有时候它会抽风选错,这时候就需要我们手动干预!
举个真实案例:
-- 某电商平台订单查询SQL
SELECT * FROM orders WHERE create_time > '2023-01-01';
结论:控制执行计划就像握住方向盘,关键时刻能救命!
适用场景:紧急止血!比如优化器死活不用索引。
极简步骤:
/*+ HINT内容 */
代码示例:
-- 强制使用索引(INDEX提示)
SELECT /*+ INDEX(orders idx_create_time) */ *
FROM orders
WHERE create_time > '2023-01-01';
-- 强制嵌套循环连接(USE_NL提示)
SELECT /*+ USE_NL(a b) */ *
FROM table_a a, table_b b
WHERE a.id = b.id;
HINT类型速查表:
HINT类型 | 作用 | 使用场景 |
---|---|---|
INDEX |
强制使用索引 | 避免全表扫描 |
USE_NL |
强制嵌套循环连接 | 小表驱动大表时 |
LEADING |
指定表连接顺序 | 多表关联顺序敏感时 |
PARALLEL |
开启并行查询 | 大数据量计算 |
⚠️注意:HINT是猛药,用多了会有抗药性!比如:
适用场景:不想改SQL代码,偷偷影响优化器判断。
-- 手动收集统计信息(Oracle示例)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SCOTT',
tabname => 'ORDERS',
estimate_percent => 30
);
END;
-- 创建函数索引(针对复杂条件)
CREATE INDEX idx_func ON orders (TO_CHAR(create_time, 'YYYY-MM-DD'));
-- 查询时自动命中索引
SELECT * FROM orders
WHERE TO_CHAR(create_time, 'YYYY-MM-DD') = '2023-01-01';
-- 按时间范围分区
CREATE TABLE orders (
id NUMBER,
create_time DATE
) PARTITION BY RANGE (create_time) (
PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD'))
);
-- 查询时自动裁剪分区
SELECT * FROM orders
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
适用场景:重要SQL必须稳定,禁止优化器作妖!
特性 | SQL Profile | SQL Baseline |
---|---|---|
绑定方式 | 自动建议优化方案 | 手动选择历史计划 |
适用场景 | 单次优化 | 长期固化 |
维护难度 | 低 | 中 |
版本兼容性 | 一般 | 高 |
Oracle绑定Baseline步骤:
DECLARE
my_plans PLS_INTEGER;
BEGIN
my_plans := DBMS_SPM.LOAD_PLANS_FROM_AWR(
begin_snap => 1234,
end_snap => 5678
);
END;
原始写法(性能差):
SELECT *
FROM (
SELECT t.*, ROWNUM rn
FROM big_table t
)
WHERE rn BETWEEN 1000001 AND 1000010;
问题:先全表扫描100万行,再取最后10行!
优化写法(性能起飞):
SELECT *
FROM (
SELECT /*+ INDEX(t idx_id) */
t.*, ROWNUM rn
FROM big_table t
WHERE id > 1000000 -- 利用索引快速定位
ORDER BY id
)
WHERE rn <= 10;
原理:通过COUNT STOPKEY
机制,扫描到第10行立即停止!
现有一个查询:
SELECT * FROM user_logs WHERE log_time > SYSDATE - 7;
执行计划显示全表扫描,如何用HINT强制使用索引idx_log_time
?
如何通过统计信息调整,让优化器认为某表只有100行数据?
EXPLAIN PLAN
)练习题答案:
SELECT /*+ INDEX(user_logs idx_log_time) */ *
FROM user_logs
WHERE log_time > SYSDATE - 7;
BEGIN
DBMS_STATS.SET_TABLE_STATS(
ownname => 'SCOTT',
tabname => 'YOUR_TABLE',
numrows => 100
);
END;
下期预告:《SQL优化之体系结构》
互动话题:你在学习SQL时遇到过哪些坑?欢迎评论区留言讨论!
️温馨提示:我是[随缘而动,随遇而安], 一个喜欢用生活案例讲技术的开发者。如果觉得有帮助,点赞关注不迷路