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

本文是受网文 《一次非常有意思的SQL优化经历:从30248.271s到0.001s》启发而产生的。

网文没讲创建表的数据过程,我帮他给出。

创建科目表及数据:

复制代码
CREATE TABLE tb_course
(
    id NUMBER not null primary key,
    name NVARCHAR2(10) not null
)

Insert into tb_course
 select rownum,dbms_random.string('*',dbms_random.value(6,10)) from dual
 connect by level<=100
 order by dbms_random.random
复制代码

创建学生表及数据:

复制代码
CREATE TABLE tb_student
(
    id NUMBER not null primary key,
    name NVARCHAR2(20) not null
)

Insert into tb_student
 select rownum,dbms_random.string('*',dbms_random.value(15,20)) from dual
 connect by level<=70000
 order by dbms_random.random
复制代码

创建成绩表及数据:

复制代码
CREATE TABLE tb_score
(
    id NUMBER not null primary key,
    studentid int not null,
    courseid int not null,
    score int not null
)

Insert into tb_score
 select rownum,dbms_random.value(0,70000),dbms_random.value(0,100),dbms_random.value(0,100) from dual
 connect by level<=700000
 order by dbms_random.random
复制代码

 

而要取的考id=0的科目及格的学生列表,可以用下面两种等效半连接SQL语句:

select * from tb_student stu where stu.id in(select studentid from tb_score where courseid=0 and score>60)

select * from tb_student stu where exists(select studentid from tb_score where courseid=0 and score>60 and tb_score.studentid=stu.id)

如果是要走内连接的方式,则要把tb_score表的studentid清除一次重复,如果不清重复,那么两表连接如果有一对多的情况就会产生多条数据。上面的半连接只要存在就算条件通过,存在一条和存在多条等效,自然就不用清除重复了。

select stu.* from (select distinct studentid from tb_score where courseid=0 and score>60) score,
              tb_student stu
         where score.studentid=stu.id
         
select stu.* from tb_student stu,
                  (select distinct studentid from tb_score where courseid=0 and score>60) score
              where score.studentid=stu.id

 要找出重复元素可以采用下面sql:

复制代码
SQL> select studentid,count(studentid) from
  2  (select studentid from tb_score where courseid=0 and score>60) score
  3  group by studentid
  4  having count(studentid)>1;

 STUDENTID COUNT(STUDENTID)
---------- ----------------
      9508                2
     55358                2
     10852                2
     55731                2
      5751                2
       503                2

已选择6行。

已用时间:  00: 00: 00.01
复制代码

但这样还是不直观,于是可以查查重复记录究竟是怎么产生的:

复制代码
SQL> select * from tb_score where studentid in (select studentid  from
  2  (select studentid from tb_score where courseid=0 and score>60) score
  3  group by studentid
  4  having count(studentid)>1)
  5  and courseid=0
  6  order by 2,3,4;

        ID  STUDENTID   COURSEID      SCORE
---------- ---------- ---------- ----------
    173720        503          0         92
     64695        503          0         94
    157901       5751          0         71
    475290       5751          0         93
    144229       9508          0         67
    142179       9508          0         89
    240689      10852          0         73
    625426      10852          0         75
    203725      55358          0         86
    431998      55358          0        100
     83002      55731          0         68    356457      55731          0         83

已选择12行。

已用时间:  00: 00: 00.03
复制代码

这下看清楚,原来有些人考了两次! 这可以对应现实中考两次取最高分或是正考一次补考一次的例子。

因为上面四条SQL语句运行都挺快,我都没加索引都是如此,于是就先不用优化了,以后再说。

2020年1月21日

附:上面我用到的全部SQL语句:

复制代码
CREATE TABLE tb_course
(
    id NUMBER not null primary key,
    name NVARCHAR2(10) not null
)

Insert into tb_course
 select rownum,dbms_random.string('*',dbms_random.value(6,10)) from dual
 connect by level<=100
 order by dbms_random.random

CREATE TABLE tb_student
(
    id NUMBER not null primary key,
    name NVARCHAR2(20) not null
)

Insert into tb_student
 select rownum,dbms_random.string('*',dbms_random.value(15,20)) from dual
 connect by level<=70000
 order by dbms_random.random

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

Insert into tb_score
 select rownum,dbms_random.value(0,70000),dbms_random.value(0,100),dbms_random.value(0,100) from dual
 connect by level<=700000
 order by dbms_random.random
 

select * from tb_student stu where stu.id in(select studentid from tb_score where courseid=0 and score>60)

select * from tb_student stu where exists(select studentid from tb_score where courseid=0 and score>60 and tb_score.studentid=stu.id)

select stu.* from (select distinct studentid from tb_score where courseid=0 and score>60) score,
              tb_student stu
         where score.studentid=stu.id
         
select stu.* from tb_student stu,
                  (select distinct studentid from tb_score where courseid=0 and score>60) score
              where score.studentid=stu.id 

select studentid,count(studentid) from              
(select studentid from tb_score where courseid=0 and score>60) score
group by studentid
having count(studentid)>1

select * from tb_score where studentid in (select studentid  from              
(select studentid from tb_score where courseid=0 and score>60) score
group by studentid
having count(studentid)>1)
and courseid=0
order by 2,3,4
复制代码

 2020-01-22

posted @   逆火狂飙  阅读(394)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
历史上的今天:
2017-01-21 为什么一个目录里放超过十个Mp4文件会导致资源管理器和播放程序变卡变慢?
2015-01-21 【高中数学/函数/幂函数】若a=(1/2)^2/3,b=(1/5)^2/3,c=(1/2)^1/3,则a,b,c的大小关系是?
2015-01-21 【Canvas与艺术】蓝色渐变单脚四钉正方形中写盈字
2014-01-21 【Canvas与艺术】双齿轮啮合传动
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东
点击右上角即可分享
微信分享提示