笔者使用的环境:

# 类别 版本
1 操作系统 Win10
2 数据库 Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
3 硬件环境 T440p
4 内存 8G

有这样一张表:

CREATE TABLE tb_sc
(
id NUMBER not null primary key,
studentid int not null,
courseid int not null,
score int not null
)

可以用以下SQL给它充值:

Insert into tb_sc
select rownum,dbms_random.value(0,100000),dbms_random.value(1,5),dbms_random.value(0,150) from dual
connect by level<=200000
order by dbms_random.random

填充20万数据,很快就完成了。

因为上面学生科目是随机数产生的,因此会出现同一studentid,同一科目id,而有不同考分的多条记录,这在现实的一次高考中是不会发生的,因此需要清除掉重复的记录。

我采用的方案是留下学生id和科目id相同而分数最高的一条,可以用以下sql来得到记录:

select studentid,courseid,max(score) from tb_sc group by studentid,courseid

看以下DB里这样的记录约有十六万条,也就是说有四万多条记录要清除掉:

SQL> select count(*) from
2 ( select studentid,courseid,max(score) from tb_sc group by studentid,courseid ); COUNT(*)
----------
162315

使用如下语句就能找到这十六万条记录:

select tb_sc.* from tb_sc,( select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid ) tb_sc2
where tb_sc.studentid=tb_sc2.studentid and tb_sc.courseid=tb_sc2.courseid and tb_sc.score=tb_sc2.score

然后使用以下语句能将这十六万条输入导入到一张新表:

create table tb_sc_nodup2 as select tb_sc.* from tb_sc,( select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid ) tb_sc2
where tb_sc.studentid=tb_sc2.studentid and tb_sc.courseid=tb_sc2.courseid and tb_sc.score=tb_sc2.score

下面是执行记录:

SQL> create table tb_sc_nodup2 as select tb_sc.* from tb_sc,( select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid ) tb_sc2
2 where tb_sc.studentid=tb_sc2.studentid and tb_sc.courseid=tb_sc2.courseid and tb_sc.score=tb_sc2.score ; 表已创建。 已用时间: 00: 00: 00.42

可见,创建新表还是挺快的。之后truancate掉旧表,再insert into 旧表 select * from 新表也花不了多少时间。

也可以使用以下sql取删除掉重复项 :

delete from tb_sc where id not in (select tb_sc.id from tb_sc,( select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid ) tb_sc2
where tb_sc.studentid=tb_sc2.studentid and tb_sc.courseid=tb_sc2.courseid and tb_sc.score=tb_sc2.score)

看看这下要花多长时间:

SQL> delete from tb_sc where id not in (select tb_sc.id from tb_sc,( select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid ) tb_sc2
2 where tb_sc.studentid=tb_sc2.studentid and tb_sc.courseid=tb_sc2.courseid and tb_sc.score=tb_sc2.score); 已删除37448行。 已用时间: 00: 00: 00.81

也还可以。和导入新表删旧表再倒回来估计耗时相去不远。

但如果采用如下sql去删除:

delete from tb_sc where (studentid,courseid,score) not in (select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid)

那花的时间可就长了,长到能令人怀疑人生。因为它相当于跑了个双重循环(20万*16万=320亿),再用六个量进行三三比对,这六个量还没有一个是主键,自然就慢得吓人了。

虽然说小表一般不会产生性能问题,但sq书写不合理也一样会导致性能问题的。

这个语句到底多慢呢,如果是20万数据量我等不起,让我们通过减少数据量再进行测试:

SQL> truncate table tb_sc;

表被截断。

SQL> Insert into tb_sc
2 select rownum,dbms_random.value(0,10000),dbms_random.value(1,5),dbms_random.value(0,150) from dual
3 connect by level<=10000
4 order by dbms_random.random; 已创建10000行。 SQL> commit; 提交完成。 SQL> select count(*) from
2 ( select studentid,courseid,max(score) from tb_sc group by studentid,courseid ); COUNT(*)
----------
8969

commit之后,tb_sc表里有一万条数据,大约有一千多条是不符合要求的。

再打开执行计划看看:

SQL> set autotrace trace exp;
SQL> delete from tb_sc where (studentid,courseid,score) not in (select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid); 已删除1029行。 执行计划
----------------------------------------------------------
Plan hash value: 3448751082 -------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 449 | 23348 | 77150 (15)| 00:15:26 |
| 1 | DELETE | TB_SC | | | | |
|* 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL | TB_SC | 8971 | 455K| 9 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
| 5 | HASH GROUP BY | | 8971 | 341K| 11 (19)| 00:00:01 |
| 6 | TABLE ACCESS FULL| TB_SC | 8971 | 341K| 9 (0)| 00:00:01 |
------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 2 - filter( NOT EXISTS (SELECT 0 FROM "TB_SC" "TB_SC" GROUP BY
"STUDENTID","COURSEID" HAVING LNNVL("STUDENTID"<>:B1) AND
LNNVL("COURSEID"<>:B2) AND LNNVL(MAX("SCORE")<>:B3)))
4 - filter(LNNVL("STUDENTID"<>:B1) AND LNNVL("COURSEID"<>:B2) AND
LNNVL(MAX("SCORE")<>:B3)) Note
-----
- dynamic sampling used for this statement (level=2)

和猜测差不多,里面有两次全表扫描,两次比较,但是cost在delete statement处骤升让人惊异。万条记录还能执行并把执行计划跑出来,二十万就遥遥无期了。

看来以后写in查询走非索引列是不合适的,这也是在调试别的课题时得到的收获吧。

汇聚点滴,终成大洋!

--2020年1月24日--

附:与not in等效之not exist语句

delete from tb_sc where not exists (
select 'x' from tb_sc a,
(select studentid,courseid,max(score) as score from tb_sc group by studentid,courseid) b
where a.studentid=b.studentid and a.courseid=b.courseid and a.score=b.score and tb_sc.id=a.id)

--2020年1月24日 18点37分--

【Oracle/Sql】清除重复而带出的性能问题的更多相关文章

  1. oracle sql 优化大全

    转自: http://panshaobinsb.iteye.com/blog/1718233 http://yulimeander.blog.sohu.com/115850824.html 最近遇到了 ...

  2. [Oracle/SQL]找出id为0的科目考试成绩及格的学生名单的四种等效SQL语句

    本文是受网文 <一次非常有意思的SQL优化经历:从30248.271s到0.001s>启发而产生的. 网文没讲创建表的数据过程,我帮他给出. 创建科目表及数据: CREATE TABLE ...

  3. oracle客户端免安装配置、64位机器PL/SQL和VS自带的IIS连接问题

    一.oracle客户端免安装配置 1.到oracle官网下载Oracle InstantClient, 把它解压缩到单独目录,例如C:\OracleClient,2. 添加环境变量 ORACLE_HO ...

  4. Oracle SQL tuning 步骤

    Oracle SQL tuning 步骤 SQL是的全称是Structured Query Language(结构化查询语言).SQL是一个在80年代中期被使用的工业标准数据库查询语言.不要把SQL语 ...

  5. Oracle SQL性能优化技巧大总结

    http://wenku.baidu.com/link?url=liS0_3fAyX2uXF5MAEQxMOj3YIY4UCcQM4gPfPzHfFcHBXuJTE8rANrwu6GXwdzbmvdV ...

  6. Oracle sql语句执行顺序

    sql语法的分析是从右到左 一.sql语句的执行步骤: 1)词法分析,词法分析阶段是编译过程的第一个阶段.这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构 ...

  7. 利用 Oracle EM 企业管理器 进行oracle SQL的优化(自动生成索引)

    利用 Oracle EM 企业管理器 进行oracle SQL的优化(自动生成索引) ##应用情景 项目中有大量的SQL,尤其是涉及到统计报表时,表关联比较多,当初开发建表时也没搞好索引关联的,上线后 ...

  8. Oracle SQL语句执行过程

    前言 QQ群讨论的时候有人遇到这样的问题:where子句中无法访问Oracle自定义的字段别名.这篇 博客就是就这一问题做一个探讨,并发散下思维,谈谈SQL语句的执行顺序问题. 问题呈现 直接给出SQ ...

  9. Oracle SQL优化[转]

    Oracle SQL优化 1. 选用适合的ORACLE优化器 ORACLE的优化器共有3种: a. RULE (基于规则) b. COST (基于成本) c. CHOOSE (选择性) 设置缺省的优化 ...

随机推荐

  1. CUDA线程、线程块、线程束、流多处理器、流处理器、网格概念的深入理解

    一.与CUDA相关的几个概念:thread,block,grid,warp,sp,sm. sp: 最基本的处理单元,streaming processor  最后具体的指令和任务都是在sp上处理的.G ...

  2. C#LeetCode刷题之#66-加一(Plus One)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3684 访问. 给定一个由整数组成的非空数组所表示的非负整数,在该 ...

  3. Flutter build apk 如何访问网络

    将下列配置放到路径:your_project\android\app\src下的 main 文件夹下的 AndroidManifest.xml 和 profile 文件夹下的 AndroidManif ...

  4. 国人开源了一款小而全的 Java 工具类库,厉害啊!!

    最近栈长看到了一款小而全的 Java 工具类库:Hutool,Github 已经接近 14K Star 了,想必一定很优秀,现在推荐给大家,很多轮子不要再造了! Hutool 是什么 Hutool 是 ...

  5. [netty4][netty-handler]netty之idle handler处理

    初始化时记录idle时间,并启动一个延时任务,延时时间为idle时间,延时任务是io.netty.handler.timeout.IdleStateHandler.AllIdleTimeoutTask ...

  6. Redis设计与实现——独立功能的实现

    发布和订阅 频道的订阅和退订 struct redisServer{ //键是被订阅者频道 ,键是一个链表,记录所有订阅这个频道的客户端 dict *publish_channels } 订阅实现: ...

  7. Android 用空格作为分割符切割字符串

    项目中有需要用到空格作为分割符切割字符串,进而转为List. String wordStore = edWord.getText().toString(); String[] word = wordS ...

  8. maatwebsite lost precision when export long integer data

    Maatwebsite would lost precision when export long integer data, no matter string or int storaged in ...

  9. kvm 虚拟机中鼠标不同步的问题解决方法

    在<devices>标签下添加 <input type='tablet' bus='usb'/>    

  10. 2020.5.21 第一篇 Scrum冲刺博客

    Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.Alpha 阶段成员任务安排 二.明日任务安排 三.预期的任务量 四.敏捷开发前的感想 五.团队期望 一.Alpha ...