笔者使用的环境:

# 类别 版本
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. Tutte 定理与 Tutte–Berge 公式

    Tutte theorem 图 \(G=(V,E)\) 有完美匹配当且仅当满足 \(\forall U\subseteq V,o(G-U)\le|U|,o(X)\) 表示 X 子图的奇连通块数. Tu ...

  2. java 增强for循环与泛型

    一 增强for循环 增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的.它的内部 原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作 ...

  3. Java实现经典七大经典排序算法

    利用Java语言实现七大经典排序算法:冒泡排序.选择排序.插入排序.希尔排序.堆排序.归并排序以及快速排序. 分类 类别 算法 插入排序类 插入排序.希尔排序 选择排序类 选择排序.堆排序 交换排序类 ...

  4. JavaScript 数组中根据某个属性值的中文进行排序

    普通排序 const arr = [] arr.sort((x, y) => x.prop - y.prop) 中文属性值排序 const arr = [] arr.sort((x, y) =& ...

  5. GitHub 热点速览 Vol.32:VScode 韭菜基金插件,极大提高“工作”效率

    作者:HelloGitHub-小鱼干 摘要:有什么比干着本职工作--编码,而又兼顾"外快"--炒股更有开心的事情呢?leek-fund 就是这么一个极大提升你工作幸福度和效率的插件 ...

  6. LeetCode 309 Best Time to Buy and Sell Stock with Cooldown 解决方案

    题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与多笔 ...

  7. ldap登录验证的通用步骤

    和利用数据库进行验证类似,LDAP中也是利用登陆名和密码进行验证,LDAP中会定义一个属性password,用来存放用户密码,而登陆名使用较多的都是mail地址.那怎么样才能正确的用LDAP进行身份验 ...

  8. Python 为什么要在 18 年前引入布尔类型?且与 C、C++ 和 Java 都不同?

    花下猫语:在上一篇<Python 为什么能支持任意的真值判断? >文章中,我们分析了 Python 在真值判断时的底层实现,可以看出 Python 在对待布尔值时,采用了比较宽泛的态度.官 ...

  9. 把H2数据库从jar包部署到Kubernetes,并解决Ingress不支持TCP的问题

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! H2 Database是一个优秀的数据库,又小又方便,支持内存和文件形式,经常会在测试.POC(proof of conce ...

  10. 【算法•日更•第二期】查找算法:三分VS二分

    ▎前言:函数 如果你已经上过初二的数学课了,那么你十有八九会被函数折磨到吐血,这是一种中考压轴题类的题目,往往分类讨论到你恶心.不过没学过也不打紧,现场讲解一下: ☞『数学中的函数』 一般地,如果在一 ...