问题描述
create table test_dept (dept_id number, dept_name varchar2(50), dept_region number); alter table test_dept add constraint pk primary key (dept_id); create table test_emp (emp_id number, sal number, dept_id number , constraint fk foreign key (dept_id) references test_dept(dept_id)); insert into test_dept values (10,'IT', 1); insert into test_dept values (20,'ADMIN', 1); insert into test_dept values (30,'SUPPORT', 2); insert into test_dept values (40,'SERVICES', 3); COMMIT; SELECT * FROM test_dept; insert into test_emp values (101,1000, 10); insert into test_emp values (102,6000, 30); insert into test_emp values (103,8000, 10); insert into test_emp values (104,5000, 20); insert into test_emp values (105,1000, 10); insert into test_emp values (106,6000, 30); insert into test_emp values (107,8000, 10); insert into test_emp values (108,5000, 10); insert into test_emp values (109,1000, 10); insert into test_emp values (110,6000, 30); insert into test_emp values (111,8000, 20); insert into test_emp values (112,5000, 10); insert into test_emp values (113,1000, 10); insert into test_emp values (114,6000, 20); insert into test_emp values (115,8000, 10); SELECT * FROM test_emp; SELECT dept_id, min(sal), max(sal) from test_emp e where exists (select 1 from test_dept d where e.dept_id = d.dept_id and dept_region in (1,2)) group by dept_id; Plan Hash Value : 605905731 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | | 1 | SORT GROUP BY | | | | | | | 2 | NESTED LOOPS | | | | | | | 3 | NESTED LOOPS | | | | | | | 4 | TABLE ACCESS FULL | TEST_EMP | | | | | | * 5 | INDEX UNIQUE SCAN | PK | | | | | | * 6 | TABLE ACCESS BY INDEX ROWID | TEST_DEPT | | | | | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 5 - access("E"."DEPT_ID"="D"."DEPT_ID") * 6 - filter("DEPT_REGION"=1 OR "DEPT_REGION"=2) Now consider that the test_dept has 414 records for the given dept_region and test_emp has 3 million records, in that case the explain plan gives something like -------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | -------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5124 | 97356 | 329559 | 01:16:54 | | 1 | SORT GROUP BY NOSORT | | 5124 | 97356 | 329559 | 01:16:54 | | 2 | NESTED LOOPS | | 154913245 | 2943351655 | 321286 | 01:14:59 | | 3 | SORT UNIQUE | | 414 | 2898 | 35 | 00:00:01 | | * 4 | TABLE ACCESS FULL | TEST_DEPT | 414 | 2898 | 35 | 00:00:01 | | 5 | PARTITION RANGE ALL | | 374639 | 4495668 | 1552 | 00:00:22 | | * 6 | INDEX RANGE SCAN | PK | 374639 | 4495668 | 1552 | 00:00:22 | -------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 4 - filter("D"."DEPT_REGION"=1 OR "D"."DEPT_REGION"=2) * 6 - access("D"."DEPT_ID"="E"."DEPT_ID") The execution of the query takes around 12 mnins. can you help me on reducing this time??复制
专家解答
好的,这是我的副本 (可能与您的真实版本不同),但希望仍然会有所帮助
所以我有20,000个部门,50个地区,3m员工。您的查询在逻辑上等同于:
这让我感到沮丧,1900年得到,并注意到我们从未碰过表,因为我们将SAL放入了索引中。(显然,您需要将其与应用程序的其余部分进行权衡)。
有趣的是,您的一个执行计划引用了一个分区模式,所以也许您没有告诉我所有内容,但是让我们继续前进-我们可以查看引用分区,例如
<代码>
SQL> 创建表test_dept
2 (dept_id号主键,
3页名称变量2(50),
4 dept_region number)
5按哈希划分 (dept_region)
6分区16;
创建的表。
SQL>
SQL>
SQL> 创建表test_emp
2 (emp_id号,
3 sal数,
4 dept_id号不为空,
5删除级联上的约束fk外键 (dept_id) 引用test_dept(dept_id))
6引用分区 (fk);
创建的表。
SQL>
SQL> 插入到test_dept
2选择rownum * 10,“dept' | |(rownum * 10),mod(rownum,50)
3从双连接级别 <= 50*400;
创建20000行。
SQL>
SQL> 在test_emp中插入/* 追加 */
2选择rownum,rownum,(1 mod(rownum,18000))* 10
3从 (从dual connect中选择1,by level <1000),
4 (从dual connect中按级别 <3000选择1);
创建2996001行。
SQL>
SQL>
SQL> exec dbms_stats.gather_table_stats('','test_emp');
PL/SQL过程成功完成。
SQL> exec dbms_stats.gather_table_stats('','测试 _ 部门');
PL/SQL过程成功完成。
SQL> 选择e.dept_id,min(sal),max(sal)
2来自test_emp e,
3测试 _ 部门d
4其中e.dept_id = d.dept_id
5和d.dept_region in (1,2)
6按e.dept_id分组;
最小 (SAL) 最大 (SAL)
----------
2010 200 2988200
3510 350 2988350
4020 401 2988401
...
选择720行。
执行计划
-
计划哈希值: 2769476757
-------------------------------------------
| Id | 操作 | 名称 | 行 | 字节 | 成本 (% CPU)| 时间 | Pstart | Pstop |
-------------------------------------------
| 0 | SELECT语句 | | 18130 | 336K | 3057 (2)| 00:00:01 |
| 1 | 哈希组 | | 18130 | 336K | 3057 (2)| 00:00:01 |
| 2 | 分区哈希列表 | | 132K | 2452K | 3051 (2)| 00:00:01 | 键 (I) |
| * 3 | 哈希连接 | | 132K | 2452K | 3051 (2)| 00:00:01 |
| * 4 | 表访问完全 | TEST_DEPT | 800 | 6400 | 548 (1)| 00:00:01 | 键 (I) |
| 5 | 表访问完全 | TEST_EMP | 2996K | 31M | 2489 (2)| 00:00:01 | 键 (I) |
-------------------------------------------
谓词信息 (由操作id标识):
---------------------------------------------------
3-访问 (“E”。“DEPT_ID” = “D”。“DEPT_ID”)
4-过滤器 (“D”。“DEPT_REGION” = 1或 “D”。“DEPT_REGION” = 2)
统计
-
15个递归调用
5 db块获取
1719一致获取
0物理读取
132重做大小
21334字节通过SQL * Net发送到客户端
通过SQL * Net从客户端接收到的1125字节
49 SQL * 往返客户端的净往返
2种 (内存)
0排序 (磁盘)
已处理720行
现在我在1700得到没有任何额外的索引。再一次,很明显,你需要权衡这与你的应用程序的其余部分。
所以有很多选择可以探索。
SQL> 创建表test_dept 2 (dept_id number, 3页名称变量2(50), 4 dept_region number); 创建的表。 SQL> SQL> SQL> 创建表test_emp 2 (emp_id号, 3 sal数, 4 dept_id number); 创建的表。 SQL> SQL> 插入到test_dept 2选择rownum * 10,“dept' | |(rownum * 10),mod(rownum,50) 3从双连接级别 <= 50*400; 创建20000行。 SQL> SQL> 在test_emp中插入/* 追加 */ 2选择rownum,rownum,(1 mod(rownum,18000))* 10 3从 (从dual connect中选择1,by level <1000), 4 (从dual connect中按级别 <3000选择1); 创建2996001行。 SQL> SQL> alter table test_dept 2 add constraint pk primary key (dept_id); Table altered. SQL> SQL> alter table test_emp 2 add constraint fk foreign key (dept_id) references test_dept(dept_id); Table altered. SQL> SQL> SQL> exec dbms_stats.gather_table_stats('','test_emp'); PL/SQL过程成功完成。 SQL> exec dbms_stats.gather_table_stats('','测试 _ 部门'); PL/SQL过程成功完成。复制
所以我有20,000个部门,50个地区,3m员工。您的查询在逻辑上等同于:
SQL> 选择e.dept_id,min(sal),max(sal) 2来自test_emp e, 3测试 _ 部门d 4其中e.dept_id = d.dept_id 5和d.dept_region in (1,2) 6按e.dept_id分组; 最小 (SAL) 最大 (SAL) ---------- 2010 200 2988200 3510 350 2988350 4020 401 2988401 ... 选择720行。 执行计划 - Plan hash value: 379542677 ------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------ | 0 | SELECT STATEMENT | | 18130 | 336K| 2486 (3)| 00:00:01 | | 1 | HASH GROUP BY | | 18130 | 336K| 2486 (3)| 00:00:01 | |* 2 | HASH JOIN | | 132K| 2452K| 2480 (2)| 00:00:01 | |* 3 | TABLE ACCESS FULL| TEST_DEPT | 800 | 6400 | 19 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL| TEST_EMP | 2996K| 31M| 2447 (2)| 00:00:01 | ------------------------ 谓词信息 (由操作id标识): --------------------------------------------------- 2 - access("E"."DEPT_ID"="D"."DEPT_ID") 3 - filter("D"."DEPT_REGION"=1 OR "D"."DEPT_REGION"=2) 统计 - 54 recursive calls 10 db block gets 8920 consistent gets 0物理读取 0 redo size 21334字节通过SQL * Net发送到客户端 通过SQL * Net从客户端接收到的1125字节 49 SQL * 往返客户端的净往返 6 sorts (memory) 0排序 (磁盘) 已处理720行 <代码> So already, 9000 consistent gets in the worst case (lets say all physical multiblock reads IO at 10ms) is only a couple of seconds. I could look at indexing the relevant columns to avoid TEST_EMP table access, eg <代码> SQL> create index test_emp_ix on test_emp ( dept_id, sal ); Index created. SQL> 选择e.dept_id,min(sal),max(sal) 2来自test_emp e, 3测试 _ 部门d 4其中e.dept_id = d.dept_id 5和d.dept_region in (1,2) 6按e.dept_id分组; 最小 (SAL) 最大 (SAL) ---------- 2010 200 2988200 3510 350 2988350 4020 401 2988401 ... 选择720行。 执行计划 - Plan hash value: 496026648 -------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------- | 0 | SELECT STATEMENT | | 18130 | 336K| 1627 (1)| 00:00:01 | | 1 | HASH GROUP BY | | 18130 | 336K| 1627 (1)| 00:00:01 | | 2 | NESTED LOOPS | | 132K| 2452K| 1621 (1)| 00:00:01 | |* 3 | TABLE ACCESS FULL| TEST_DEPT | 800 | 6400 | 19 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | TEST_EMP_IX | 165 | 1815 | 2 (0)| 00:00:01 | -------------------------- 谓词信息 (由操作id标识): --------------------------------------------------- 3 - filter("D"."DEPT_REGION"=1 OR "D"."DEPT_REGION"=2) 4 - access("E"."DEPT_ID"="D"."DEPT_ID") 统计 - 20 recursive calls 0 db block gets 1931 consistent gets 0物理读取 0 redo size 21334字节通过SQL * Net发送到客户端 通过SQL * Net从客户端接收到的1125字节 49 SQL * 往返客户端的净往返 6 sorts (memory) 0排序 (磁盘) 已处理720行复制
这让我感到沮丧,1900年得到,并注意到我们从未碰过表,因为我们将SAL放入了索引中。(显然,您需要将其与应用程序的其余部分进行权衡)。
有趣的是,您的一个执行计划引用了一个分区模式,所以也许您没有告诉我所有内容,但是让我们继续前进-我们可以查看引用分区,例如
<代码>
SQL> 创建表test_dept
2 (dept_id号主键,
3页名称变量2(50),
4 dept_region number)
5按哈希划分 (dept_region)
6分区16;
创建的表。
SQL>
SQL>
SQL> 创建表test_emp
2 (emp_id号,
3 sal数,
4 dept_id号不为空,
5删除级联上的约束fk外键 (dept_id) 引用test_dept(dept_id))
6引用分区 (fk);
创建的表。
SQL>
SQL> 插入到test_dept
2选择rownum * 10,“dept' | |(rownum * 10),mod(rownum,50)
3从双连接级别 <= 50*400;
创建20000行。
SQL>
SQL> 在test_emp中插入/* 追加 */
2选择rownum,rownum,(1 mod(rownum,18000))* 10
3从 (从dual connect中选择1,by level <1000),
4 (从dual connect中按级别 <3000选择1);
创建2996001行。
SQL>
SQL>
SQL> exec dbms_stats.gather_table_stats('','test_emp');
PL/SQL过程成功完成。
SQL> exec dbms_stats.gather_table_stats('','测试 _ 部门');
PL/SQL过程成功完成。
SQL> 选择e.dept_id,min(sal),max(sal)
2来自test_emp e,
3测试 _ 部门d
4其中e.dept_id = d.dept_id
5和d.dept_region in (1,2)
6按e.dept_id分组;
最小 (SAL) 最大 (SAL)
----------
2010 200 2988200
3510 350 2988350
4020 401 2988401
...
选择720行。
执行计划
-
计划哈希值: 2769476757
-------------------------------------------
| Id | 操作 | 名称 | 行 | 字节 | 成本 (% CPU)| 时间 | Pstart | Pstop |
-------------------------------------------
| 0 | SELECT语句 | | 18130 | 336K | 3057 (2)| 00:00:01 |
| 1 | 哈希组 | | 18130 | 336K | 3057 (2)| 00:00:01 |
| 2 | 分区哈希列表 | | 132K | 2452K | 3051 (2)| 00:00:01 | 键 (I) |
| * 3 | 哈希连接 | | 132K | 2452K | 3051 (2)| 00:00:01 |
| * 4 | 表访问完全 | TEST_DEPT | 800 | 6400 | 548 (1)| 00:00:01 | 键 (I) |
| 5 | 表访问完全 | TEST_EMP | 2996K | 31M | 2489 (2)| 00:00:01 | 键 (I) |
-------------------------------------------
谓词信息 (由操作id标识):
---------------------------------------------------
3-访问 (“E”。“DEPT_ID” = “D”。“DEPT_ID”)
4-过滤器 (“D”。“DEPT_REGION” = 1或 “D”。“DEPT_REGION” = 2)
统计
-
15个递归调用
5 db块获取
1719一致获取
0物理读取
132重做大小
21334字节通过SQL * Net发送到客户端
通过SQL * Net从客户端接收到的1125字节
49 SQL * 往返客户端的净往返
2种 (内存)
0排序 (磁盘)
已处理720行
现在我在1700得到没有任何额外的索引。再一次,很明显,你需要权衡这与你的应用程序的其余部分。
所以有很多选择可以探索。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
Oracle RAC 一键安装翻车?手把手教你如何排错!
Lucifer三思而后行
598次阅读
2025-04-15 17:24:06
【纯干货】Oracle 19C RU 19.27 发布,如何快速升级和安装?
Lucifer三思而后行
579次阅读
2025-04-18 14:18:38
XTTS跨版本迁移升级方案(11g to 19c RAC for Linux)
zwtian
493次阅读
2025-04-08 09:12:48
Oracle数据库一键巡检并生成HTML结果,免费脚本速来下载!
陈举超
475次阅读
2025-04-20 10:07:02
【ORACLE】记录一些ORACLE的merge into语句的BUG
DarkAthena
460次阅读
2025-04-22 00:20:37
Oracle 19c RAC更换IP实战,运维必看!
szrsu
438次阅读
2025-04-08 23:57:08
【ORACLE】你以为的真的是你以为的么?--ORA-38104: Columns referenced in the ON Clause cannot be updated
DarkAthena
436次阅读
2025-04-22 00:13:51
【活动】分享你的压箱底干货文档,三篇解锁进阶奖励!
墨天轮编辑部
423次阅读
2025-04-17 17:02:24
火焰图--分析复杂SQL执行计划的利器
听见风的声音
368次阅读
2025-04-17 09:30:30
3月“墨力原创作者计划”获奖名单公布
墨天轮编辑部
359次阅读
2025-04-15 14:48:05