zoukankan      html  css  js  c++  java
  • Delete..In.. 删除语句的优化再次探讨

    有这样一张成绩表

    CREATE TABLE stu_score
    (
        id int ,
        stu_id int,
        course_id int,
        score int,
        primary key(id)
    )

    可以这样给它插入实验值:

    Insert into stu_score
    select rownum,dbms_random.value(0,10000),dbms_random.value(1,5),dbms_random.value(0,150) from dual
    connect by level<10001

    然后需求来了,stu_id和course_id相同时,最高的成绩保留,其余删除。用日常话来说就是取每个学生单科的最佳成绩。

    第一种删除方式:

    delete from stu_score where (stu_id,course_id,score) not in (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id);

    处理结果:

    SQL> delete from stu_score where (stu_id,course_id,score) not in (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id);
    
    已删除1048行。
    
    已用时间:  00: 00: 28.68

    耗时约28秒,select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id的结果是8943条,这种删除需要从一万条里逐条取出去和8943条比对,比对次数为10000*8943次。虽说小表产生性能问题是小概率事件,但迟早会碰上。

    这条删除语句慢的原因部分在于 stu_id,course_id,score 都不是主键,没有索引帮助,只能走全表查询的路子。但是,从解释计划来看,cost的飙升段发生在Delete Statement,达到了八千多,和后面的查询关系真心不大。我试了select * from stu_score where (stu_id,course_id,score) not in (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id)  这样的语句,其中只是把delete替换成了select * ,执行起来就是秒出,并无delete时的阻滞。

    有些书上提到过给甄别列stu_id,course_id,score 加上索引能加快删除速度的说法,我尝试过没有效果。具体如下:

    SQL> create index idx_stuscore_three on stu_score(stu_id,course_id,score);
    
    索引已创建。
    
    已用时间:  00: 00: 00.06
    SQL> delete from stu_score where (stu_id,course_id,score) not in (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id);
    
    已删除1011行。
    
    已用时间:  00: 00: 29.47

    第二种删除方式:

    delete from stu_score where id not in (select a.id from stu_score a,( select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id ) b
    where a.stu_id=b.stu_id and a.course_id=b.course_id and a.score=b.score)

    处理结果:

    SQL> delete from stu_score where id not in (select a.id from stu_score a,( select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id ) b
      2  where a.stu_id=b.stu_id and a.course_id=b.course_id and a.score=b.score);
    
    已删除1048行。
    
    已用时间:  00: 00: 00.06

    用时不到一秒,这种方式形成了一个id临时结果集,一万条里逐条拿id去看在不在这个结果集里,比对次数为10000次加一次结果集查询。由于id是主键,有效利用了id上的索引,这自然比全表查询要快。从解释计划上看,包括delete statement部分的cost只有区区三十几。

    第三种删除方式:

    delete from stu_score where not exists (
    select null from stu_score a,
    (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id) b
    where a.stu_id=b.stu_id and a.course_id=b.course_id and a.score=b.score and stu_score.id=a.id)

    处理结果:

    SQL> delete from stu_score where not exists (
      2  select null from stu_score a,
      3                   (select stu_id,course_id,max(score) as score from stu_score group by stu_id,course_id) b
      4  where a.stu_id=b.stu_id and a.course_id=b.course_id and a.score=b.score and stu_score.id=a.id);
    
    已删除1048行。
    
    已用时间:  00: 00: 00.12

    用时不到一秒,这种方式取出每行id进行了一次存在性查询,总体为一万次存在性查询。从解释计划上看,包括delete statement部分的cost也只有区区三十几。

    可以看出,虽然存在子查询,但后两种相当于一万次函数调用,且函数参数只有一个,结果都是秒出;而第一种是10000*8943约九千万次比对,比对参数还有三个,相当于九千万次三个参数的函数调用,这自然落了下风。

    由于SQL语句的度量不像程序般透彻,只能以运行时间去套函数调用的概念,目前水平也只能解释到这个程度,大家见谅。

    前作:https://www.cnblogs.com/heyang78/p/12263253.html

    END

  • 相关阅读:
    二分法查找递归方式()
    JDBC操作MySQL(crud)
    (转)JAVA中的权限修饰符
    抽象类和接口(面试题总结)
    java基础-集合
    Java泛型通配符以及限定
    div中嵌套的多个div使用了浮动后居中的办法
    将博客搬至CSDN
    (补)Java解析XML之dom4j
    Java单元测试
  • 原文地址:https://www.cnblogs.com/heyang78/p/15215124.html
Copyright © 2011-2022 走看看