GROUP BY 子句—数据的分组

  GROUP BY 子句——数据的分组 
   聚合函数 (比如 SUM) 常常需要添加 GROUP BY 语句。
一、GROUP BY 子句
     GROUP BY 子句用于结合聚合函数,根据一个或多个列对结果集进行分组。在 SELECT 语句里,GROUP BY 子句在 WHERE 子句之后,在 ORDER BY 子句之前。
     语法:SELECT  列名, 聚合函数(列名) FROM  表名 [ WHERE 条件 ] GROUP BY 列名 [ ORDER BY 列名 ];
     前面的例子中我们用 SUM 这个函数来算出所有店的营业总额。如果我们的需求变成是要算出每一间店(store_name)的营业额(Sales),就可以利用GROUP BY 子句。 
     例:由 Store_Information 表里求出每一间店的营业总额:
         SELECT store_name, SUM(Sales) FROM Store_Information GROUP BY store_name;
         结果:store_name  SUM(Sales)
 ------------------------------------------
 Los Angeles  1800
 San Diego  250
 Boston   700 
   当我们查询不只一个字段,且其中至少一个字段有聚合函数的运用时,我们就需要用到 GROUP BY 子句。这时,我们需要确定 GROUP BY 子句中包含所有其他的字段。
        像 ORDER BY 子句一样,GROUP BY 子句里也可以用整数代表字段名称。
        用 GROUP BY 子句实现排序操作与用 ORDER BY 子句的区别是:
        ->  所有被选中的、非聚合函数的字段必须列在 GROUP BY 子句里;
        ->  除非需要所有聚合函数,否则使用 GROUP BY 子句进行排序通常没有必要;
        ->  GROUP BY 子句用于对相同的数据进行分组,而 ORDER BY 子句基本上只用于让数据形成次序。
二、HAVING 子句
    那么,我们如何对函数产生的值设定条件呢?举例来说,我们可能需要知道哪些店的营业额超过1,500 。这时,我们不能使用WHERE 子句,因为WHERE 无法与聚合函数一起使用。那该怎么办呢?很幸运地,SQL 提供一个 HAVING 的指令,而我们就可以用这个指令来达到这个目的。
    HAVING 子句在 SELECT 语句里与 GROUP BY 子句联合使用时,用于告诉 GROUP BY 子句在输出里包含哪些分组。HAVING 对 GROUP BY 的作用相当于 WHERE 对于 SELECT 的作用。
    语法:SELECT 列名, 聚合函数(列名) FROM 表名 [ WHERE 条件 ] GROUP BY 列名 HAVING  聚合函数(列名)  操作符  值;
    注意:如果被 SELECT 的只有函数栏,那就不需要 GROUP BY 子句。
      例:在 Store_Information 表里,找出 Sales 大于1,500 的 store_name ,可以用:
      SELECT store_name, SUM(sales) FROM Store_Information GROUP BY store_name HAVING  SUM(sales) > 1500; 
      结果:store_name  SUM(Sales)
  -------------------------------------------
 Los Angeles  1800
三、查询举例
        有学生表 Student(Sno, Sname, Sage, Ssex),包括学号、姓名、年龄、性别四个字段;
        课程表 Course(Cno, Cname, Tno),包括课程编号、课程名、教师编号三个字段;
        成绩表 SC(Sno, Cno, Score),包括学号、课程编号、成绩三个字段;
        教师表 Teacher(Tno, Tname),包括教师编号、姓名两个字段;
        完成如下查询:
    1、查询姓"张"的学生名单
    SELECT Sname FROM Student WHERE Sname like '张%';
    2、求选了课程的学生人数
    SELECT count(*) FROM SC;
    3、查询姓"李"的老师的个数
    SELECT count(distinct(Tname))FROM Teacher WHERE Tname like '李%';
    4、查询不及格的课程,并按课程号从大到小排列
    SELECT Cno FROM SC WHERE Score <60 ORDER BY Cno;
    5、检索"004"课程分数小于60,按分数降序排列的同学学号
    SELECT Sno FROM SC WHERE Cno='004' and Score <60 ORDER BY Score desc;
    6、查询各个课程及相应的选修人数
    SELECT count(*) FROM SC GROUP BY Cno;
    7、查询每门课程被选修的学生数
    SELECT Cno,count(Sno) FROM SC GROUP BY Cno;
    8、统计每门课程的学生选修人数,要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
    SELECT Cno as 课程号,count(*) as 人数 FROM SC GROUP BY Cno ORDER BY count(*) desc, Cno;
    9、检索至少选修两门课程的学生学号
    SELECT Sno FROM SC GROUP BY Sno HAVING count(*) >=2;
    10、查询平均成绩大于60分的同学的学号和平均成绩
    SELECT Sno,avg(Score) FROM SC GROUP BY Sno HAVING avg(Score) >60;
    11、查询男生、女生人数
    SELECT count(Ssex) as 男生人数  FROM Student GROUP BY Ssex HAVING Ssex='男';  
    SELECT count(Ssex) as 女生人数  FROM Student GROUP BY Ssex HAVING Ssex='女';
    12、查询同名同姓学生名单,并统计同名人数
    SELECT Sname,count(*) FROM Student GROUP BY Sname HAVING count(*) >1;
    13、删除"002"同学的"001"课程的成绩
    DELECT FROM SC WHERE Sno='002' AND Cno='001';
    14、查询每门课程的平均成绩,结果按平均成绩升序排列,平均成绩相同时,按课程号降序排列
    SELECT Cno, avg(score) FROM SC GROUP BY Cno  ORDER BY avg(score), Cno DESC;


【参考资料】
W3SChool 在线SQL教程   http://www.w3SChool.com.cn/sql/sql_groupby.asp  SQL GROUP BY 语句
   http://www.w3SChool.com.cn/sql/sql_HAVING.asp  SQL HAVING 子句
   
课后练习:
        1. 下面的SQL语句能正常执行吗?
 a.
 SELECT SUM(SALARY), EMP_ID
 FROM EMPLOYEE_PAY_TBL
 GROUP BY 1 AND 2;
 b.
 SELECT EMP_ID, MAX(SALARY)
 FROM EMPLOYEE_PAY_TBL
 GROUP BY SALARY, EMP_ID;
 c.
 SELECT EMP_ID , COUNT(SALARY)
 FROM EMPLOYEE_PAY_TBL
 ORDER BY EMP_ID
 GROUP BY SALARY;
 d.
 SELECT YEAR(DATE_HIRE) AS YEAR_HIRED, SUM(SALARY)
 FROM EMPLOYEE_PAY_TBL
 GROUP BY 1
 HAVING SUM(SALARY) > 20000;
        3. 判断正误:
         1)在使用 HAVING 子句时一定也要使用 GROUP BY 子句。
 2)下面的SQL语句返回分组的薪水总和:
        SELECT SUM(SALARY)
       FROM EMPLOYEE_PAY_TBL;
 3)被选中的字段在 GROUP BY 子句里必须以相同次序出现。
 4)HAVING 子句告诉 GROUP BY 子句要包括哪些分组。
        4. 运行数据库 learnsql,输入如下查询来显示表 EMPLOYEE_TBL 里的全部城市:
 SELECT CITY
 FROM EMPLOYEE_TBL;
        输入如下查询,把结果与上面的结果进行比较:
 SELECT CITY, COUNT(*)
 FROM EMPLOYEE_TBL
 GROUP BY CITY;
        5. HAVING 子句与 WHERE 子句的相似之处在于都可以指定返回数据的条件。WHERE 子句是查询的主过滤器,而 HAVING 子句是在 GROUP BY 子句对数据进行分组之后进行过滤。输入如下查询来了解 HAVING 子句的工作方式:
 SELECT CITY,COUNT(*)
 FROM EMPLOYEE_TBL
 GROUP BY CITY
 HAVING COUNT(*) > 1;
        修改上面的查询,把结果按降序排序,也就是数值从大到小。
        6. 编写一个查询,从 EMPLOYEE_PAY_RATE 表里列出每个城市的平均税率和工资。
        7. 编写一个查询,从 EMPLOYEE_PAY_RATE 表里列出每个城市平均薪水高于$20000的每个城市的平均薪水。
        8. 有学生表 Student(Sno, Sname, Sage, Sdept),包括字段学号、姓名、年龄、系名;
         及成绩表 SC(studentid, courseid,grade),包括字段学生学号、课程编号、成绩。
         完成以下查询:
 1)查询每个学生的平均成绩。
 2)查询学生的平均成绩在70分以上的。   
        9. 在4.1节的练习1中,我们为数据库创建的四个表----学生表(Student)、课程表(Course)、成绩表(Score)以及教师信息表(Teacher)填充了数据。在此基础上完成以下查询:
 1)查询Student表中的所有记录的Sname、Ssex和Class列。
 2)查询教师所有的单位即不重复的Depart列。
 3)查询Student表的所有记录。
 4)查询Score表中成绩在60到80之间的所有记录。
 5)查询Score表中成绩为85,86或88的记录。
 6)查询Student表中"95031"班或性别为"女"的同学记录。
 7)以Class降序查询Student表的所有记录。
 8)以Cno升序、Degree降序查询Score表的所有记录。
 9)查询"95031"班的学生人数。
 10)查询'3-105'号课程的平均分。
 11)查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。
 12)查询最低分大于70,最高分小于90的Sno列。
 13)查询所有学生的Sname、Cno和Degree列。
 14)查询所有学生的Sno、Cname和Degree列。
 15)查询所有学生的Sname、Cname和Degree列。
 16)查询95033班和95031班全体学生的记录。
 17)查询存在有85分以上成绩的课程Cno.
 18)查询"计算机系"与"电子工程系"不同职称的教师的Tname和Prof。
 19)查询所有教师和同学的name、sex和birthday.
 20)查询所有"女"教师和"女"同学的name、sex和birthday.
 21)查询至少有2名男生的班号。
 22)查询Student表中不姓"王"的同学记录。
 23)查询Student表中每个学生的姓名和年龄。
 24)查询Student表中最大和最小的Sbirthday日期值。
 25)以班号和年龄从大到小的顺序查询Student表中的全部记录。
 26)查询"男"教师及其所上的课程。
 27)查询最高分同学的Sno、Cno和Degree列。