Mysql 数据库(九)
一 视图
什么是视图:视图就是一张虚拟表。方便查看。
创建视图:create view 起名 as sql语句
#两张有关系的表 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 体育 | 3 | | 4 | 美术 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> select * from teacher; +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 张磊老师 | | 2 | 李平老师 | | 3 | 刘海燕老师 | | 4 | 朱云海老师 | | 5 | 李杰老师 | +-----+-----------------+ 5 rows in set (0.00 sec) #查询李平老师教授的课程名 mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师'); +--------+ | cname | +--------+ | 物理 | | 美术 | +--------+ 2 rows in set (0.00 sec) #子查询出临时表,作为teacher_id等判断依据 select tid from teacher where tname='李平老师'
#语法:CREATE VIEW 视图名称 AS SQL语句 create view teacher_view as select tid from teacher where tname='李平老师'; #于是查询李平老师教授的课程名的sql可以改写为 mysql> select cname from course where teacher_id = (select tid from teacher_view); +--------+ | cname | +--------+ | 物理 | | 美术 | +--------+ 2 rows in set (0.00 sec) #!!!注意注意注意: #1. 使用视图以后就无需每次都重写子查询的sql,但是这么效率并不高,还不如我们写子查询的效率高 #2. 而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的sql过分依赖于数据库中存放的视图,那么意味着,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,你要想完成修改,必须付出大量的沟通成本DBA可能才会帮你完成修改,极其地不方便
注意sql语句不能包括子查询。
使用视图:
#修改视图,原始表也跟着改 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 体育 | 3 | | 4 | 美术 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> create view course_view as select * from course; #创建表course的视图 Query OK, 0 rows affected (0.52 sec) mysql> select * from course_view; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 体育 | 3 | | 4 | 美术 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> update course_view set cname='xxx'; #更新视图中的数据 Query OK, 4 rows affected (0.04 sec) Rows matched: 4 Changed: 4 Warnings: 0 mysql> insert into course_view values(5,'yyy',2); #往视图中插入数据 Query OK, 1 row affected (0.03 sec) mysql> select * from course; #发现原始表的记录也跟着修改了 +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 1 | xxx | 1 | | 2 | xxx | 2 | | 3 | xxx | 3 | | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 5 rows in set (0.00 sec)
修改视图:alter view 视图名称 as sql语句
语法:ALTER VIEW 视图名称 AS SQL语句 mysql> alter view teacher_view as select * from course where cid>3; Query OK, 0 rows affected (0.04 sec) mysql> select * from teacher_view; +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 2 rows in set (0.00 sec)
删除视图:drop view 视图名
DROP VIEW teacher_view
二 触发器
什么是触发器:由一个行为触发了某一个行为。也就是用户对于表的增删改的操作前后的行为。
创建触发器:
# 插入前 CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW BEGIN ... END # 插入后 CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW BEGIN ... END # 删除前 CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW BEGIN ... END # 删除后 CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW BEGIN ... END # 更新前 CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW BEGIN ... END # 更新后 CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW BEGIN ... END
trigger:触发器
for each row:每一行
before:执行前
after:执行后
插入触发触发器:
#准备表 CREATE TABLE cmd ( id INT PRIMARY KEY auto_increment, USER CHAR (32), priv CHAR (10), cmd CHAR (64), sub_time datetime, #提交时间 success enum ('yes', 'no') #0代表执行失败 ); CREATE TABLE errlog ( id INT PRIMARY KEY auto_increment, err_cmd CHAR (64), err_time datetime ); #创建触发器 delimiter // CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW BEGIN IF NEW.success = 'no' THEN #等值判断只有一个等号 INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必须加分号 END IF ; #必须加分号 END// delimiter ; #往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志 INSERT INTO cmd ( USER, priv, cmd, sub_time, success ) VALUES ('egon','0755','ls -l /etc',NOW(),'yes'), ('egon','0755','cat /etc/passwd',NOW(),'no'), ('egon','0755','useradd xxx',NOW(),'no'), ('egon','0755','ps aux',NOW(),'yes'); #查询错误日志,发现有两条 mysql> select * from errlog; +----+-----------------+---------------------+ | id | err_cmd | err_time | +----+-----------------+---------------------+ | 1 | cat /etc/passwd | 2017-09-14 22:18:48 | | 2 | useradd xxx | 2017-09-14 22:18:48 | +----+-----------------+---------------------+ 2 rows in set (0.00 sec)
new:刚插入的数据;old:早已存在的数据
delimiter :命名结束符号。
触发器无法有用户直接调用,而知由于对表的增删改操作被动引发的。
删除触发器:
drop trigger tri_after_insert_cmd;
三 事物
什么是事物:包含着一堆sql语句,要么同时成功,要么同时失败。
start transaction:开启事物。
rollbask:如果出现异常,就要执行这个命令,还原开始的记录
commit:事务的语句执行完毕后,用这个命令提交一下。
try:扑捉异常;except:抛出异常。
如下:
create table user( id int primary key auto_increment, name char(32), balance int ); insert into user(name,balance) values ('wsb',1000), ('egon',1000), ('ysb',1000); #原子操作 start transaction; update user set balance=900 where name='wsb'; #买支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 update user set balance=1090 where name='ysb'; #卖家拿到90元 commit; #出现异常,回滚到初始状态 start transaction; update user set balance=900 where name='wsb'; #买支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 uppdate user set balance=1090 where name='ysb'; #卖家拿到90元,出现异常没有拿到 rollback; commit; mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ 3 rows in set (0.00 sec)
四 存储过程
什么是存储过程:也就是一堆失去了语句,与存储有关的。
只要一执行存储过程,就会触发一堆sql语句的执行。
优点:用来代替程序写的sql语句,实现程序与数据库的解耦。
基于网络传输,传别名数据量小,直接传sql数据量大。
缺点:扩展不方便
程序与sql语句结合使用的方式
1 mysql:存储过程 程序:调用存储过程
2 mysql:什么都不做 程序:纯sql语句
3 mysql:什么都不做 程序:类和对象,即orm(本质还是纯sql语句)
创建存储过程程序:
procedure:创建函数
call:在数据库中执行存储过程函数
callproc:在python中基于pymysql模块执行存储过程函数
1 创建无参存储过程程序:
delimiter // create procedure p1() BEGIN select * from blog; INSERT into blog(name,sub_time) values("xxx",now()); END // delimiter ; #在mysql中调用 call p1() #在python中基于pymysql调用 cursor.callproc('p1') print(cursor.fetchall())
2 创建有参存储过程程序:
in:接收参数
delimiter // create procedure p2( in n1 int, in n2 int ) BEGIN select * from blog where id > n1; END // delimiter ; #在mysql中调用 call p2(3,2) #在python中基于pymysql调用 cursor.callproc('p2',(3,2)) print(cursor.fetchall())
out:返回值的参数
delimiter // create procedure p3( in n1 int, out res int ) BEGIN select * from blog where id > n1; set res = 1; END // delimiter ; #在mysql中调用 set @res=0; #0代表假(执行失败),1代表真(执行成功) call p3(3,@res); select @res; #在python中基于pymysql调用 cursor.callproc('p3',(3,0)) #0相当于set @res=0 print(cursor.fetchall()) #查询select的查询结果 cursor.execute('select @_p3_0,@_p3_1;') #@p3_0代表第一个参数,@p3_1代表第二个参数,即返回值 print(cursor.fetchall())
注意:in接收的参数值不能够当作out的返回值使用。
inout:既可以传入值,又可以做为返回值。
delimiter // create procedure p4( inout n1 int ) BEGIN select * from blog where id > n1; set n1 = 1; END // delimiter ; #在mysql中调用 set @x=3; call p4(@x); select @x; #在python中基于pymysql调用 cursor.callproc('p4',(3,)) print(cursor.fetchall()) #查询select的查询结果 cursor.execute('select @_p4_0;') print(cursor.fetchall())
事物:
#介绍 delimiter // create procedure p4( out status int ) BEGIN 1. 声明如果出现异常则执行{ set status = 1; rollback; } 开始事务 -- 由秦兵账户减去100 -- 方少伟账户加90 -- 张根账户加10 commit; 结束 set status = 2; END // delimiter ; #实现 delimiter // create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN -- ERROR set p_return_code = 1; rollback; END; DECLARE exit handler for sqlwarning BEGIN -- WARNING set p_return_code = 2; rollback; END; START TRANSACTION; DELETE from tb1; #执行失败 insert into blog(name,sub_time) values('yyy',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0代表执行成功 END // delimiter ; #在mysql中调用存储过程 set @res=123; call p5(@res); select @res; #在python中基于pymysql调用存储过程 cursor.callproc('p5',(123,)) print(cursor.fetchall()) #查询select的查询结果 cursor.execute('select @_p5_0;') print(cursor.fetchall())
执行存储过程:
在MySQL中执行:
-- 无参数 call proc_name() -- 有参数,全in call proc_name(1,2) -- 有参数,有in,out,inout set @t1=0; set @t2=3; call proc_name(1,2,@t1,@t2)
set @:定义变量
在python中基于pymysql模块执行:
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 执行存储过程 cursor.callproc('p1', args=(1, 22, 3, 4)) # 获取执行完存储的参数 cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3") result = cursor.fetchall() conn.commit() cursor.close() conn.close() print(result)
select @_名_值:获取执行完的存储参数。
删除存储过程:
drop procedure proc_name;
五 函数
mysql中部分内置函数:
CHAR_LENGTH(str) 返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。 对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。 CONCAT(str1,str2,...) 字符串拼接 如有任何一个参数为NULL ,则返回值为 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定义连接符) CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。 CONV(N,from_base,to_base) 进制转换 例如: SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示 FORMAT(X,D) 将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 为 0, 则返回结果不带有小数点,或不含小数部分。 例如: SELECT FORMAT(12332.1,4); 结果为: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替换位置其实位置 len:替换的长度 newstr:新字符串 特别的: 如果pos超过原字符串长度,则返回原字符串 如果len超过原字符串长度,则由新字符串完全替换 INSTR(str,substr) 返回字符串 str 中子字符串的第一个出现位置。 LEFT(str,len) 返回字符串str 从开始的len位置的子序列字符。 LOWER(str) 变小写 UPPER(str) 变大写 LTRIM(str) 返回字符串 str ,其引导空格字符被删除。 RTRIM(str) 返回字符串 str ,结尾空格字符被删去。 SUBSTRING(str,pos,len) 获取字符串子序列 LOCATE(substr,str,pos) 获取子序列索引位置 REPEAT(str,count) 返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。 若 count <= 0,则返回一个空字符串。 若str 或 count 为 NULL,则返回 NULL 。 REPLACE(str,from_str,to_str) 返回字符串str 以及所有被字符串to_str替代的字符串from_str 。 REVERSE(str) 返回字符串 str ,顺序和字符顺序相反。 RIGHT(str,len) 从字符串str 开始,返回从后边开始len个字符组成的子序列 SPACE(N) 返回一个由N空格组成的字符串。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str) 返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定情况下,可删除空格。 mysql> SELECT TRIM(' bar '); -> 'bar' mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx'); -> 'barxxx' mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx'); -> 'bar' mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); -> 'barx'
date_format函数:将时间日期取出某段部分
#1 基本使用 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00' #2 准备表和记录 CREATE TABLE blog ( id INT PRIMARY KEY auto_increment, NAME CHAR (32), sub_time datetime ); INSERT INTO blog (NAME, sub_time) VALUES ('第1篇','2015-03-01 11:31:21'), ('第2篇','2015-03-11 16:31:21'), ('第3篇','2016-07-01 10:21:31'), ('第4篇','2016-07-22 09:23:21'), ('第5篇','2016-07-23 10:11:11'), ('第6篇','2016-07-25 11:21:31'), ('第7篇','2017-03-01 15:33:21'), ('第8篇','2017-03-01 17:32:21'), ('第9篇','2017-03-01 18:31:21'); #3. 提取sub_time字段的值,按照格式后的结果即"年月"来分组 SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m'); #结果 +-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ 3 rows in set (0.00 sec)
自定义函数:不要在函数内部写sql语句,函数仅仅只有一个功能,是一个在mysql中被应用的功能。若想在begin......end.... 中写sql语句,请用存储过程。
定义函数:
实例1:
delimiter // create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END // delimiter ;
实例2:
delimiter // create function f5( i int ) returns int begin declare res int default 0; if i = 10 then set res=100; elseif i = 20 then set res=200; elseif i = 30 then set res=300; else set res=400; end if; return res; end // delimiter ;
删除函数:
drop function func_name;
执行函数:
# 获取返回值 select UPPER('egon') into @res; SELECT @res; # 在查询中使用 select f1(11,nid) ,name from tb2;
六 流程控制
1 if 条件语句:
delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END // delimiter ;
declare:声明变量
then :接下来执行
end if:接收循环
2 循环语句:
while:
delimiter // CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END // delimiter ;
do :while的结尾
end while:结束循环
repeat:
delimiter // CREATE PROCEDURE proc_repeat () BEGIN DECLARE i INT ; SET i = 0 ; repeat select i; set i = i + 1; until i >= 5 end repeat; END // delimiter ;
end repeat:结束repeat循环
loop:
BEGIN declare i int default 0; loop_label: loop set i=i+1; if i<8 then iterate loop_label; end if; if i>=10 then leave loop_label; end if; select i; end loop loop_label; END
本片博客详情:http://www.cnblogs.com/linhaifeng/articles/7495918.html
练习题
准备表:
create table class(cid int primary key auto_increment, caption char(5) not null unique); INSERT into class(caption)values('三年二班'),('一年三班'),('三年一班'); CREATE table student(sid int primary key auto_increment, sname char(6) not null, gender enum('男','女','male','female') not null, class_id int(4) not null, foreign key(class_id) references class(cid) on delete CASCADE on update cascade); insert into student(sname,gender,class_id)values ('钢蛋','女',1),('铁锤','女',1),('山炮','男',2); create table teacher(tid int primary key auto_increment, tname char(6) not null); insert into teacher(tname)values('波多'),('苍空'),('饭岛'); create table course(cid int primary key auto_increment, cname CHAR(5) not null unique, teacher_id int not null, foreign key(teacher_id) references teacher(tid) on delete CASCADE on update cascade); insert into course(cname,teacher_id)values('生物',1),('体育',1),('物理',2); create table score(sid int primary key auto_increment, student_id int not null, foreign key(student_id) references student(sid) on delete cascade on update cascade, course_id int not null, foreign key(course_id) references course(cid) on delete cascade on update cascade, number int(4) not null); insert into score(student_id,course_id,number)values(1,1,60),(1,2,59),(2,2,100); SELECT * from class; show CREATE table class; select * from student; show create table student; SELECT * from teacher; show create table teacher; select * from course; show create table course; select * from score; show create table score;
开始练习:
1、查询所有的课程的名称以及对应的任课老师姓名 SELECT cname,tname from course inner join teacher ON course.teacher_id = teacher.tid; 2、查询学生表中男女生各有多少人 select gender,COUNT(sid) from student GROUP BY gender; 3、查询物理成绩等于100的学生的姓名 SELECT sname from student where sid in ( SELECT student_id from score where course_id = (SELECT cid from course where cname = '物理') and num = 100 ); 4、查询平均成绩大于八十分的同学的姓名和平均成绩 方法1: SELECT student.sname,t1.avg_num from student inner join (SELECT student_id,AVG(num) avg_num from score GROUP BY student_id HAVING avg(num) > 80) as t1 on student.sid = t1.student_id; 方法2: select * from student where sid in ( select student_id from score group by student_id having avg(num)>80 ); 5、查询所有学生的学号,姓名,选课数,总成绩 SELECT student.sid,student.sname,t1.course_num,t1.total_num from student inner JOIN (SELECT student_id, count(course_id) course_num, sum(num) total_num FROM score GROUP BY student_id) as t1 on student.sid = t1.student_id; 6、 查询姓李老师的个数 方法1: SELECT COUNT(1) from teacher where tname like '李%'; 方法2: select count(t1) from ( select tname t1 from teacher where tname LIKE '李%' )as t 7、 查询没有报李平老师课的学生姓名 SELECT sname FROM student WHERE sid NOT IN ( SELECT student_id FROM score WHERE course_id IN ( SELECT cid FROM course WHERE teacher_id = ( SELECT tid FROM teacher WHERE tname = '李平老师' ) ) ); 8、 查询物理课程比生物课程高的学生的学号 SELECT t1.student_id from (SELECT student_id,num from score where course_id = ( SELECT cid from course where cname = '物理' )) as t1 inner join (SELECT student_id,num from score where course_id = ( SELECT cid from course where cname = '生物' )) as t2 on t1.student_id = t2.student_id where t1.num > t2.num; 9、 查询没有同时选修物理课程和体育课程的学生姓名 方法1: SELECT sname from student where sid in ( SELECT student_id from score LEFT JOIN course on score.course_id = course.cid WHERE course.cname in ('物理','体育') GROUP BY student_id HAVING count(sid) < 2 ); 方法2: select sname from student where sid not in ( SELECT s1.student_id from ( select student_id from score where course_id =( SELECT cid from course where cname ='体育')) s1 INNER JOIN ( select student_id from score where course_id =( SELECT cid from course where cname ='物理')) s2 on s1.student_id=s2.student_id); 10、查询挂科超过两门(包括两门)的学生姓名和班级 方法1:: SELECT sname,caption from student LEFT JOIN class on student.class_id = class.cid where student.sid in ( SELECT student_id from score where num < 60 GROUP BY student_id HAVING COUNT(course_id) >= 2 ) ; 方法2: select s.sname,class.caption from class INNER JOIN (select * from student where sid in ( select student_id from score GROUP BY student_id having student_id>=2)) s on s.class_id=class.cid; 11 、查询选修了所有课程的学生姓名 select sname from student where sid in ( select student_id from score GROUP BY student_id having count(sid)=( select count(cid) from course)) 12、查询李平老师教的课程的所有成绩记录 方法1: SELECT * from score where course_id in ( SELECT cid from course inner JOIN teacher on course.teacher_id = teacher.tid WHERE tname = '李平老师' ); 方法2: select num from score WHERE course_id in ( select cid from course where teacher_id=( select tid from teacher where tname='李平老师')); 13、查询全部学生都选修了的课程号和课程名 SELECT ss.s1,ss.s2,course.cid,course.cname from (select student.sid s1,student.sname s2,score.course_id s3 from student INNER JOIN score on student.sid=score.student_id ) ss INNER JOIN course on ss.s3=course.cid; 14、查询每门课程被选修的次数 方法1: SELECT course.cname,t1.count_student FROM course INNER JOIN ( SELECT course_id,count(student_id) count_student from score GROUP BY course_id ) as t1 ON course.cid = t1.course_id; 方法2: select course.cname,COUNT(score.sid) from course INNER JOIN score on course.cid=score.course_id group by score.course_id; 15、查询只选修了一门课程的学生姓名和学号 select sid,sname from student where sid in( select student_id from score GROUP BY student_id having count(sid)=1); 16、查询所有学生考出的总成绩并按从高到低排序(成绩去重) 方法1: SELECT DISTINCT sum(num) sum_num from score group by student_id ORDER BY sum_num desc; 方法2: select student.sname,avg(score.num) avg_num from student INNER JOIN score on student.sid=score.student_id GROUP BY student_id ORDER BY avg_num desc; 17、查询平均成绩大于85的学生姓名和平均成绩 方法1: SELECT student.sname,t1.avg_num from student inner join ( SELECT student_id,avg(num) avg_num from score GROUP BY student_id having avg(num) > 85 ) as t1 on student.sid = t1.student_id; 方法2: select student.sname,avg(score.num) from student INNER JOIN score on student.sid=score.student_id GROUP BY score.student_id having avg(score.num)>85; 18、查询生物成绩不及格的学生姓名和对应生物分数 方法1: SELECT sname,t1.num from student INNER JOIN ( SELECT student_id,num from score LEFT JOIN course on score.course_id = course.cid where course.cname = '生物' and score.num < 60 ) as t1 on student.sid = t1.student_id; 方法2: select student.sname,ss.num from student INNER JOIN( select * from score where course_id=( select cid from course where cname='生物') and num<60) ss on ss.student_id=student.class_id; 19、查询在所有选修了李平老师课程的学生中,这些课程(李平老师的课程,不是所有课程)平均成绩最高的学生姓名 select sname from student where sid in( select student_id from score where course_id in( select cid from course where teacher_id=( select tid from teacher where tname='李平老师')) GROUP BY student_id HAVING avg(num)=( select avg(num) from score where course_id in( select cid from course where teacher_id=( select tid from teacher where tname='李平老师')) GROUP BY student_id order by avg(num) desc limit 1)) 20、查询每门课程成绩最好的前两名学生姓名 SELECT * from score ORDER BY course_id,num desc; #取得课程编号与第一高的成绩:course_id,first_num SELECT course_id,max(num) first_num from score GROUP BY course_id; #取得课程编号与第二高的成绩:course_id,second_num SELECT score.course_id,max(num) second_num from score LEFT JOIN ( SELECT course_id,max(num) first_num from score GROUP BY course_id ) as t1 on score.course_id = t1.course_id where score.num < t1.first_num GROUP BY score.course_id ; #链表得到一张新表,新表包含课程编号与这门课程前两名的成绩分数 select t1.course_id,t1.first_num,t2.second_num from (SELECT course_id,max(num) first_num from score GROUP BY course_id) as t1 inner join (SELECT score.course_id,max(num) second_num from score LEFT JOIN ( SELECT course_id,max(num) first_num from score GROUP BY course_id ) as t1 on score.course_id = t1.course_id where score.num < t1.first_num GROUP BY score.course_id) as t2 on t1.course_id = t2.course_id; #取前两名学生的编号 SELECT score.course_id,score.student_id from score LEFT JOIN ( select t1.course_id,t1.first_num,t2.second_num from (SELECT course_id,max(num) first_num from score GROUP BY course_id) as t1 inner join (SELECT score.course_id,max(num) second_num from score LEFT JOIN ( SELECT course_id,max(num) first_num from score GROUP BY course_id ) as t1 on score.course_id = t1.course_id where score.num < t1.first_num GROUP BY score.course_id) as t2 on t1.course_id = t2.course_id ) as t3 on score.course_id = t3.course_id where score.num >= t3.second_num and score.num <= t3.first_num ; SELECT t4.course_id,student.sname from student inner join ( SELECT score.course_id,score.student_id from score LEFT JOIN ( select t1.course_id,t1.first_num,t2.second_num from (SELECT course_id,max(num) first_num from score GROUP BY course_id) as t1 inner join (SELECT score.course_id,max(num) second_num from score LEFT JOIN ( SELECT course_id,max(num) first_num from score GROUP BY course_id ) as t1 on score.course_id = t1.course_id where score.num < t1.first_num GROUP BY score.course_id) as t2 on t1.course_id = t2.course_id ) as t3 on score.course_id = t3.course_id where score.num >= t3.second_num and score.num <= t3.first_num ) as t4 on student.sid = t4.student_id ORDER BY t4.course_id ; select student.sname,t.course_id,t.num from student INNER JOIN ( select s1.student_id,s1.course_id,s1.num, (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 0,1) as first_num, (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 1,1) as second_num from score as s1 ) as t on student.sid = t.student_id where t.num in (t.first_num,t.second_num) ORDER BY t.course_id ; SELECT sid from score as s1 ;