MySQL-開發實踐

關鍵詞(Key Words)

  • MySQL 綜合實踐
  • 三表聯查 inner join
  • 聚合查詢avg,sum,Group by
  • 排序order by
  • 查詢格式化cast...as… , round
  • 條件判斷case...when...

開發環境

  • 系 統:CentOS Linux release 7.5.1804 (Core)
  • MySQL版本:MySQL 5.6

問題場景描述

在掘金,有一位朋友@Horizon757分享了一份這樣的SQL的訓練題,借用此題進行MySQL實踐:mysql

img

參考答案

先說答案,再請耐心細看下面的解題思路。sql

select 
	name,
	cast(sum(credit*(case 
         when grade < 60 then 0 
         else grade/10-5 end))/sum(credit) 
         as decimal(3,2) ) 
     as avg_gpa 
from t_student a 
inner join t_grade b on  a.id = b.sid 
inner join t_course c on b.cid = c.id 
group by sid 
order by avg_gpa desc;
複製代碼

或者shell

select 
	name,
	round(sum(credit*(case 
         when grade < 60 then 0 
         else grade/10-5 end))/sum(credit) 
         ,2 ) 
     as avg_gpa 
from t_student a 
inner join t_grade b on  a.id = b.sid 
inner join t_course c on b.cid = c.id 
group by sid 
order by avg_gpa desc;
複製代碼

解題思路

  1. 進入MySQL數據庫

mysql -h localhost -u root -p
複製代碼
  1. 定義數據庫及數據表(DDL)

  • 新建一個數據庫;數據庫

    create database school;
    複製代碼
  • 新建三個數據表t_student , t_course , t_gradebash

    • t_student 表中,id 做爲主鍵,name 做爲必須項,不能爲空(not null);函數

      create table t_student (
          id(11) int not null auto_increment primary key,
          name varchar(10) not null
      ) engine=InnoDB default charset=utf8;
      複製代碼
    • t_course 表中,id 做爲主鍵,name 做爲必須項,不能爲空,credit 是小數用 decimal(2,1) ,默認值0.0;學習

      create table t_course (
          id int(11) not null auto_increment primary key,
          course varchar(5) not null,
          credit decimal(2,1) default 0.0 
      ) engine=InnoDB default charset=utf8;
      複製代碼
    • t_grade 表中,sid 與 cid 做爲t_grade 表的聯合主鍵,確保一位學生在一個課程中只能有一個成績;同時,sid 做爲 t_student 的外鍵,cid做爲 t_course 的外鍵,二者在個人理解中他們的限制條件 on delete 須要有些不一樣,當刪除一位學生時,他的成績級聯一塊兒被刪除是很好的;可是想要刪除一門課程時,假如這門課程已經學生選修這門課而且得到了成績,在刪除時就必須作出限制,提示不能刪除該門課程;grade的分數看起來優化

      create table t_grade (
          sid int(11) not null,
          cid int(11) not null,
          grade int(2) default 0,primary key (sid,cid), 
          foreign key (sid) references t_student(id) 
          on update cascade on delete cascade,
          foreign key (cid) references t_course(id) 
          on update cascade on delete restrict
      ) engine=InnoDB default charset=utf8;
      複製代碼
  1. 插入數據(DML)

  • t_student 表中插入信息:spa

    insert into t_student (name) values ('S1'),('S2'),('S3');
    複製代碼
  • t_course 表中插入信息:3d

    insert into t_course (course,credit) values ('A',4.0),('B',3.0),('C',2.0);
    複製代碼
  • t_grade 表中插入信息:

    insert into t_grade (sid,cid,grade) values (1,1,95),(1,2,90) ,(1,3,85),(2,1,85),(2,2,97),(2,3,90),(3,1,60),(3,2,50),(3,3,59);
    複製代碼

    這裏插入完以後,就是題圖中的數據表所呈現的信息了;

    聯合查詢下,下面是咱們此次所要用到全部基本信息:

    select 
    	name,course,credit,grade 
    from t_grade a 
    left join t_student b on a.sid = b.id
    left join t_course c on a.cid = c.id;
    複製代碼

    img

  1. 查詢數據(DML)

用一條 SQL 查詢各位學生的平均學分績點(四捨五入精確兩位小數),並從高到低排序:

  • 首先咱們看看績點是如何計算的:績點=分數/10-5,而且小於60分,績點爲0,使用case...when...,SQL 語句表示就是:

    select 
    	sid,
    	case 
    		when grade < 60 then 0 
    		else grade/10-5 end 
    	as gpa 
    from t_grade;
    複製代碼

    img

  • 再看看使用 SQL 怎麼樣一步步查詢出平均績點:

    平均績點=(課程學分1績點+課程學分2績點+...+課程學分n績點)/(課程學分1+課程學分2+...+課程學分n績點)

    • 計算每一個學生的平均績點,因此要按照學生進行分組, 即 group by sid

    • 這裏須要提到 group by sid 使用的時候呈現數據的方式,若是在不使用聚合函數的時候,單單使用 group by sid 時,對比上一個圖咱們能夠發現,查詢的數據是顯示每一位同窗第一條記錄在數據表中的數據,而且多幾位小數點:

      select 
      	sid,
      	case 
      		when grade < 60 then 0 
      		else grade/10-5 end 
      	as gpa 
      from t_grade 
      group by sid;
      複製代碼

      img

    • 若是按照最簡單的求每個同窗全部成績的平均值,那就很簡單,但這不是最終答案:

      select 
      	sid,
      	avg(case 
              when grade < 60 then 0 
              else grade/10-5 end) 
          as gpa 
      from t_grade group by sid;
      複製代碼

      img

      在這裏我發現結合使用 group by 就能夠實現遍歷計算的效果;

    • 我試着利用 group by 求出每一個同窗所修課程的總學分 ,發現可行!

      select 
      	sid,
      	sum(credit) 
      from t_grade a 
      left join t_course b on a.cid = b.id 
      group by sid order by sid ;
      複製代碼

      img

    • 我試着利用 sum 和 group by 求出 (課程學分1績點+課程學分2績點+...+課程學分n*績點),發現也可行!!

      select 
      	sid,
      	sum(credit*(case 
              when grade < 60 then 0 
              else grade/10-5 end)) 
          as gpa 
      from t_grade a 
      left join t_course b on a.cid = b.id 
      group by sid;
      複製代碼

      img

    • 接着順利求出平均績點:

      select 
      	sid,
      	sum(credit*(case 
              when grade < 60 then 0 
              else grade/10-5 end))/sum(credit) 
          as gpa 
      from t_grade a 
      left join t_course b on a.cid = b.id
      group by sid;
      複製代碼

      img

      與在Excel驗證的結果相符:

      img

    • 接着利用cast...as..將平均績點四捨五入,小數點精確到兩位數,利用order by從高到低排序:

      select 
      	name,
      	cast(sum(credit*(case 
              when grade < 60 then 0 
              else grade/10-5 end))/sum(credit) 
          	as decimal(3,2)) 
      	as avg_gpa 
      from t_grade a 
      left join t_course b on a.cid = b.id 
      left join t_student c on a.sid = c.id 
      group by sid 
      order by avg_gpa desc;
      複製代碼

      img

    • 在這裏遇到小問題,cast(exp as decimal(3,2)) is ok , but cast(exp as float(3,2)) and cast(exp as float) is failed,多是float 不能在cast中使用。

      img

    • 還有四捨五入還有一種方式就是使用round

      複製代碼

    select name, round(sum(credit*(case when grade < 60 then 0 else grade/10-5 end))/sum(credit) ,2 ) as avg_gpa from t_student a inner join t_grade b on a.id = b.sid inner join t_course c on b.cid = c.id group by sid order by avg_gpa desc;

    複製代碼

六、優化

根據@Horizon757 老師提供的建議,將主表改成t_student,將left join 改成 inner join 提升效率

```mysql
select 
	name,
	cast(sum(credit*(case 
         when grade < 60 then 0 
         else grade/10-5 end))/sum(credit) 
         as decimal(3,2) ) 
     as avg_gpa 
from t_student a 
inner join t_grade b on  a.id = b.sid 
inner join t_course c on b.cid = c.id 
group by sid 
order by avg_gpa desc;
```

![img](https://user-gold-cdn.xitu.io/2019/2/13/168e62c2b63b1c2e?w=610&h=239&f=png&s=20384)
複製代碼

以上我探索出來的參考答案,可能有一些優化的地方我沒有想到,但願和你們交流。接下來繼續學習join優化器的知識,但願能夠和你們分享一些查詢優化的筆記。

很是感謝@Horizon757老師對個人耐心指導。

——57EN寫於2018年12月28號,修改於2019年2月13日。

相關文章
相關標籤/搜索