MySQL的多表查询问题

Day18  多表查询

为了消除数据的冗余,我们存数据的时候,会把完整的数据信息,分散到多张表来存储。
但是,用户通常需要的是一个完整的全面的信息,那么在查询的时候,就需要把这些分散的信息 拼接 。——多表查询

“拼接” 《=》 连接

连接查询

    交叉连接:(笛卡尔积)

         cross join
        将两张表的信息结合在一起(表1 | 表2 ):
            select * from 表1 cross join 表2(on );
            或者隐式的写法select * from 表1,表2;
        笛卡尔积:
            实现两张表的拼接,并不确保数据的正确性。
            拼接过程:
                1.对于两张表中的数据,不管有没有关系, 全部 两两组合。
            

            
            

    内连接:

        概述:
            返回连接表中  符合连接条件  及 查询条件的  数据行
            比之于交叉连接,更加关注参与连接的数据的内容。

        
        显式内连接: inner join + on
            select * from + 表1重命名 + inner join关键字 + 表2重命名 + on 判断条件
            
            select * from customer c inner join orders o on c.id=o.customer_id;
                customer c : 给customer 表重命名 / orders o :给orders 表重命名为o
                inner join : 内连接方式拼接左右两表。
                on : 判断连接条件 (重命名的 c 表的id属性,是否等于 重命名的o表的customer_id)
                    这里的 on 的判断条件需要在两个表中对应好。
                (这里重命名的 c 和 o 可以看成是对表的引用)    
                

        
        隐式内连接: inner join + where
            select * from customer c,orders o where c.id=o.customer_id;
            
            

    外链接:

        概述:
             会将  所有人的 情况  都会显式出来,如果不满足条件,就会以null的形式显式
        比之于内连接:内连接只返回符合条件的数据行,而外链接,就会将以基准表的所有信息都显示出来

        左外链接:
            left outer join 关键字,在 on 子句中设定连接条件。
            
            select * from customer c left outer join orders o on c.id=o.customer_id;
            效果:以左边为基准表,左表的每一行数据都必须出现在结果集当中,
                1.如果满足连接条件,就正常拼接;
                2.如果不满足连接条件,那么左边的不满足连接条件的数据记录就会放在结果集的最后,并且左表数据放入其中,右表数据全为NULL;
                
        右外链接:
            right outer join 关键字(连接两表),在 on 子句中设定连接条件。
            效果:以右表为基准表,右表的每一行数据都必须出现在结果集中。
                与左外连接只是表的基准不一样。
                
        在连接之后还可以 + where 来进行新表的条件过滤。
            eg. select * from customer c left outer join orders o on c.id=o.customer_id where price>200;
                
   
    

where 子句的工作模式:            

    作用:筛选和过滤;
    筛选的对象:表 中的每一行数据。
    遍历表中的每一行数据,然后根据过滤条件,对每一行数据记录进行判断(true/false)
    如果过滤结果为true,就加入结果集中。
            

*****在关系数据库中,所有的数据都存在一张二维表中;所以表的  拼接 、 查询的结果都是一张二维表。
    (所以where 就能够作用在 所有的二维表中,只要where前面的代码 表示的是一张二维表。)
            
    
            

嵌套查询(子查询):详见后面

    概述:是指在  where 子句 或  from 子句中 又嵌入 select。
    分类:
            相关子查询 : 依赖于所生成的父表。
            不相关子查询 : 不依赖与所生成的父表。

    select * from orders where customer_id={select id from customer where name like '%郭靖%'};
        {}中得到的就是符合条件的另一张表的id属性。
    
    相当于在两张表中分别进行查询。
    
SQL 注释:
    单行注释:-- 这里写注释 --
    多行注释: /* */


联合查询:

    关键字:union
    求两张表的  并集。(MySQL中没有实现求 交集和差集的 方法)
    联合查询能够合并两条查询语句的查询结果,去掉其中的重复数据行,然后返回查询结果。
        前提是两张表的结构要相同。
    
    语法:表1 union 表2;
    (合并方式:会先将表1放入结果集,然后再查询表2的数据,没有重复的就放入结果集)
    select * from

    

报表查询:

    对数据进行 分组(group by) 统计(统计函数)。
    关键字 :group by  + (having)
    
    统计函数:
        都是针对    来进行操作的。

        1.count() : 统计某列的元素个数(即行数(满足某些条件的行)).
            select count(*) from 表1; //得到表1 的行数
            加入条件: select count(math) from 表1 where math>90;
                    返回的表的属性就是 : count(math)
            count(只能是一个属性)
            
        2.sum():对一列数据求和
            只能对数值求和,如果是字符串就会报错。可对多列求和:','
            select sum(math) from 表1 where sex='男';
            select sum(math),sum(chinese)+sum(english) from 表1; //对math、english+chinese 的成绩进行统计
            
        3.avg():对一列求平均数。
            select avg(math) from 表1;
            
        4.max():返回某列中的最大值
            select max(math) from 表1;
        5.min():返回某列中的最小值
            select min(math),max(math),avg(math),count(math),sum(math) from 表1;
    
    group by :
            表示按照什么属性列来分组。
            统计函数在加入了group by语句之后,其作用范围就变成了各个分组。

    having : 过滤分组条件。                

        select product,sum(price) from 表1 group by product;
            其中sum函数的作用范围就变成了每个分组,对每个分组的price列进行求和。
            
        select product,sum(price) from 表1 group by having sum(price)>100
            having 子句中加入过滤条件。(可以使用SQL语句)
            
    having 和 where 的区别:
        having 作用于一列数据(可以使用统计函数和SQL语句)
        where 作用于一行数据(不能使用统计函数)
        两者都能实现过滤,在某些情况下可以相互替换,但是where过滤的效率 >> having(远高于having)
        因为对于having 来讲,必须首先先分组,再过滤,分组非常消耗时间。

    
    

数据库的备份和恢复:(逻辑备份)


    备份:  

        cmd命令行下:(不是 mysql> 下)
        mysqldump -uroot -p密码 要备份的数据库名称>备份到哪个文件d:\test.sql;

    

    恢复:

        1.cmd命令下:
            mysql -uroot -p密码 要恢复的数据库名称<要恢复数据的文件d:\test.sql;
            
        2.mysql>下:
            1.先创建一个新的数据库:
                create database 数据库1;
                use 数据库1;
            2.恢复哪个文件
                然后在命令行输入:source d:\test.sql;

            
            

补充: 嵌套查询的难点:

        相关子查询:
            select sname,score from sc,student where sc.sid=student.sid and score>(
            select avf(score) from sc where sid=student.sid
            );
            执行父查询中的第一行:组合新表,先求过滤条件,发现要先求 子查询:嵌套()中的 sid的值和 外面的student.sid是否相同。
            执行父查询的第二行:
            所以子查询中的student.sid是依据父查询的结果来进行。
            

        不相关子查询:





习题:

1.

某大学研究生院有若干研究生导师,包括职工编号、姓名、职称、研究方向,其中每个导师的职工编号是唯一的。

若干研究生,包括学号、姓名、性别、入学日期,其中每个研究生的学号是唯一的。
每个导师可以带若干研究生,但每个研究生只能有一个导师。
请设计一个数据库,要求可以正确体现导师和研究生之间的关系。

设计完毕之后,请插入一定量的数据,并验证你设计的数据库是否满足要求。


CREATE DATABASE sql2;
USE sql2;
CREATE TABLE teacher(id INT PRIMARY KEY,NAME VARCHAR(20),PT VARCHAR(20),way VARCHAR(20));
INSERT INTO teacher VALUES(1,'张飞','益德','丈八蛇矛'),(2,'关羽','云长','青龙偃月刀'),(3,'赵云','子龙','亮银枪')
INSERT INTO teacher VALUES(4,'赵子龙的儿子','不知道','亮银枪')



CREATE TABLE students(sid INT PRIMARY KEY,sname VARCHAR(20),sdate DATE,sex VARCHAR(20),teacher_id INT);
INSERT INTO students VALUES(11,'小白','2000-02-05','男',3),(12,'小黑','2000-03-08','男',2),(13,'小花','2001-09-12','女',2),(14,'小团','2002-09-28','女',1),(15,'小球','2008-09-25','男',2)
INSERT INTO students VALUES(16,'蛋蛋','2002-05-05','男',4)
DESC teacher;

SELECT * FROM teacher;
SELECT * FROM students;
DESC students;


SELECT * FROM teacher LEFT OUTER JOIN students ON students.`teacher_id`=teacher.`id` ORDER BY id DESC;


/*
1.请查出每个导师所带研究生的姓名。
2.清查出特定姓名的导师所带研究生的姓名。
3.请查出每个导师所带研究生的数量。
4.请查出每个导师所带的男研究生的数量。
5.请找出选择哪个研究方向的导师最多。
6.请找统计不同职称的导师的个数。
*/

-- 1.请查出每个导师所带研究生的姓名。 --
SELECT NAME,sname FROM teacher LEFT OUTER JOIN students ON students.`teacher_id`=teacher.`id` ORDER BY id DESC


-- 2.清查出特定姓名的导师所带研究生的姓名。 --
SELECT NAME,sname FROM teacher INNER JOIN students  ON students.`teacher_id`=teacher.`id` AND NAME='关羽';


-- 3.请查出每个导师所带研究生的数量。 --
SELECT COUNT(NAME),NAME FROM (SELECT NAME,sname FROM teacher LEFT OUTER JOIN students ON students.`teacher_id`=teacher.`id` ORDER BY id DESC) AS teacher GROUP BY NAME;



-- 4.请查出每个导师所带的男研究生的数量。 --
SELECT NAME,sname,sex,COUNT(sname) FROM (SELECT NAME,sname FROM teacher LEFT OUTER JOIN students ON students.`teacher_id`=teacher.`id` WHERE students.`sex`='男' ORDER BY id DESC) AS teacher GROUP BY teacher.`name`;


-- 5.请找出选择哪个研究方向的导师最多。 --
SELECT COUNT(*) FROM (SELECT way,COUNT(way) AS ways FROM teacher GROUP BY way ) ;



-- 6.请统计不同职称的导师的个数。 --
 select a.PT,count(*) from teacher a group by a.PT


2.

在数据库中创建如下两张表
create table dept ( deptno varchar(10) primary key, dname varchar(10) );
create table emp ( empno varchar(10) primary key, ename varchar(10), job varchar(10),
mgr varchar(10), sal float, deptno varchar(10) references dept(deptno) );
 
insert into dept values ('1','事业部');
insert into dept values ('2','销售部');
insert into dept values ('3','技术部');
 
insert into emp values ('01','jacky','clerk','tom','1000','1');
insert into emp values ('02','tom','clerk','','2000','1');
insert into emp values ('07','biddy','clerk','','2000','1');
insert into emp values ('03','jenny','sales','pretty','600','2');
insert into emp values ('04','pretty','sales','','800','2');
insert into emp values ('05','buddy','jishu','canndy','1000','3');
insert into emp values ('06','canndy','jishu','','1500','3');

select * from dept;


select * from emp;


-- 1列出emp表中各部门的部门号,最高工资,最低工资 --
SELECT a.`deptno`,MAX(a.`sal`),MIN(a.`sal`) FROM emp a GROUP BY a.`deptno`

 
-- 2 列出emp表中各部门job为'CLERK'的员工的最低工资,最高工资 --

SELECT job,MIN(sal),MAX(sal) FROM emp WHERE job='clerk' ;
 

-- 3 对于emp中最低工资小于2000的部门,列出job为'CLERK'的员工的部门号,最低工资,最高工资 --
SELECT deptno,MAX(sal),MIN(sal) FROM emp WHERE 2000>(SELECT MIN(sal) FROM emp) AND job='clerk'


-- 4 根据部门号由高而低,工资有低而高列出每个员工的姓名,部门号,工资 --
SELECT ename,job,sal FROM emp ORDER BY deptno DESC,sal;

 
-- 5 列出'buddy'所在部门中每个员工的姓名与部门号 --
SELECT ename,deptno FROM emp WHERE job='jishu'
 

-- 6 列出每个员工的姓名,工作,部门号,部门名 --
SELECT ename,job,emp.`deptno`,dept.`dname` FROM emp LEFT OUTER JOIN dept ON emp.`deptno`=dept.`deptno`
 

-- 7列出emp中工作为'CLERK'的员工的姓名,工作,部门号,部门名 --
SELECT ename,job,emp.`deptno`,dept.`dname` FROM emp LEFT OUTER JOIN dept ON emp.`deptno`=dept.`deptno` WHERE job='clerk'


-- 8对于emp中有管理者的员工,列出姓名,管理者姓名(管理者外键为mgr) --
 SELECT ename,mgr FROM emp WHERE mgr!='';
 

-- 9 对于dept表中,列出所有部门名,部门号,同时列出各部门工作为'CLERK'的员工名与工作 --
SELECT emp.`deptno`,dname,ename,job FROM dept INNER JOIN emp WHERE emp.`deptno`=dept.`deptno`AND job='clerk'
 

-- 10对于工资高于本部门平均水平的员工,列出部门号,姓名,工资,按部门号排序 --
 SELECT deptno,ename,sal FROM emp AS a WHERE sal>(SELECT AVG(sal) FROM emp WHERE emp.`deptno`=a.`deptno`)ORDER BY deptno;


-- 11对于emp,列出各个部门中工资高于本部门平均工资的员工数和部门号,按部门号排序 --
SELECT deptno,COUNT(*) FROM emp AS emp1 WHERE sal>(SELECT AVG(sal) FROM emp WHERE emp.`deptno`=emp1.`deptno`)GROUP BY deptno ORDER BY deptno;



练习3(难)


CREATE TABLE t_stu (
	sid INT PRIMARY KEY AUTO_INCREMENT,
	sName VARCHAR(10)
);

 INSERT INTO `t_stu`(`sid`,`sName`) VALUES ( NULL,'zs');
 INSERT INTO `t_stu`(`sid`,`sName`) VALUES ( NULL,'lisi');
 INSERT INTO `t_stu`(`sid`,`sName`) VALUES ( NULL,'wangwu');

CREATE TABLE t_course (
	cid INT PRIMARY KEY AUTO_INCREMENT,
	cName VARCHAR(20)
);

 INSERT INTO `t_course`(`cid`,`cName`) VALUES ( NULL,'数据库概论');
 INSERT INTO `t_course`(`cid`,`cName`) VALUES ( NULL,'离散数学');
 INSERT INTO `t_course`(`cid`,`cName`) VALUES ( NULL,'算法设计');

CREATE TABLE t_sc1 (
	sid INT,
	cid INT,
	score INT,
	CONSTRAINT PRIMARY KEY(sid,cid),
	CONSTRAINT sid_fk FOREIGN KEY (sid) REFERENCES t_stu(sid),
	CONSTRAINT cid_fk FOREIGN KEY (cid) REFERENCES t_course(cid)
);
 
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 1,1,90);
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 1,2,91);
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 1,3,92);
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 2,1,80);
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 2,2,73);
 INSERT INTO t_sc1(sid,cid,score) VALUES ( 3,3,77);

表t_course:                        表t_sc1:                                表t_stu:



需求:

1.查询出选择了所有课程的学生的姓名
 方法一:
SELECT sName FROM t_stu stu,t_sc1 sc1 WHERE  stu.sid=sc1.sid GROUP BY sName HAVING COUNT(sName)=3 ;
方法二:
SELECT sName FROM t_stu X WHERE NOT EXISTS(
    SELECT * FROM t_course Y WHERE NOT EXISTS (
        SELECT * FROM t_sc1 WHERE sid=x.sid AND cid=Y.cid
    )
);



2.查询选择了2号同学所选择所有课程的同学的名字。

SELECT sName FROM t_stu X WHERE NOT EXISTS(
    SELECT * FROM t_sc1 Y WHERE  sid=2 AND NOT EXISTS (
        SELECT * FROM t_sc1 WHERE sid=x.sid AND cid=Y.cid AND sid <>2
    )
);



3.查询选择了和2号同学相同课程的同学的名字

 第一种做法
 SELECT sName FROM t_stu X WHERE NOT EXISTS(
    SELECT * FROM t_sc1 Y WHERE  sid=2 AND NOT EXISTS (
        SELECT * FROM t_sc1 WHERE sid=x.sid AND cid=Y.cid AND sid <>2
    )
) AND NOT EXISTS(
    SELECT * FROM t_sc1 a WHERE sid= X.sid AND NOT EXISTS(
        SELECT * FROM t_sc1 WHERE sid=2 AND cid=a.cid
    )

);

第二种做法
select sname from t_stu X inner join t_sc1 Z on X.sid=Z.sid where not exists(
    select * from t_sc1 Y where sid=2 and not EXISTS (
            SELECT * FROM t_sc1 WHERE sid=x.sid AND cid=Y.cid AND sid <>2    
    )
) group by X.sid having count(X.sid) = (select count(*) from t_sc1 where sid=2)

因为没有人和2号同学选择的课程完全相同。


猜你喜欢

转载自blog.csdn.net/qq_38962004/article/details/80296754