mysql多表查询

查询加强


查询到的表的结构

-- 查询加强

-- 使用where语句

-- 1.如果查找1991.1.1后入职的员工
-- 注意:mysql,日期类型可以直接比较,需要注意和表中的格式一致
SELECT * FROM  emp
WHERE hiredate>'1991.1.1';

-- 2.使用like操作符(模糊)
-- %:表示0到多个任意字符  _:表示单个任意字符
-- 2.如何显示首字母为S的员工的姓名和工资
SELECT ename,sal FROM emp
WHERE ename LIKE 'S%';

-- 3.如果显示第三个字符为大写o的所有员工的姓名和工资
SELECT ename,sal FROM emp
WHERE ename LIKE '__O%';

-- 4.如果显示没有上级的员工的情况(mgr记录了雇员上级的编号)

SELECT *FROM emp
WHERE mgr IS NULL;

-- 5.查询表的结构
DESC emp;

-- 使用order by 子句
-- 1.如何按照工资从低到高的顺序【升序】,显示雇员信息
SELECT * FROM emp 
ORDER BY sal ASC;-- 默认的,asc写不写都可以
-- 2.按照部门号升序而雇员的工资降序排列,显示雇员信息(排序之后再排序)
-- (这个排序分顺序,前面的前排)意思是先按照部门号进行升序,然后按照相同部门号的内部的薪水进行降序(分组来看)


SELECT * FROM emp
ORDER BY deptno ASC,sal DESC ;


  • 排序第二题的结果

分页查询

在实际开发中的查询中,如果一个表有10w条记录,我们查询这个表,不可能将10w张记录同时返回,这就设计到分页操作

-- 分页查询
-- 按照雇员的工号升序取出,每页显示3条记录,分别显示第一页,第二页,第三页
-- limit start,rows 表示每次从start+1行开始取,每次取出rows行 

-- 第一页
SELECT * FROM emp
 ORDER BY empno
 LIMIT 0,3;-- 1~3
 
 -- 第二页
 SELECT * FROM emp
 ORDER BY empno
 LIMIT 3,3;-- 4~6
 
 -- 第三页
SELECT * FROM emp
ORDER BY empno 
LIMIT 6,3; -- 7~9
 
 

练习题

-- 练习
 -- 按照雇员的工号降序取出,每页显示5条记录,请分别显示第三页 第五页对应的sql语句
 
 -- 第三页
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 10,5; 
 -- 第五页
 SELECT * FROM emp
 ORDER BY empno DESC
 LIMIT 20,5;
 
 
 
 

分组加强(听起来很迷糊省略)


多子句查询


注意各个子句的顺序

多表查询

多表笛卡尔集


从上面的评论内容可以看成,这个评论的内容一定是来自一个评论表和一个商品表



多表查询的关键在于对笛卡尔集进行过滤操作,而上面2个表都有部门号这个列,必须部门号相同才是有效的记录,所有可以通过部门号相同来进行过滤


注意上面的建议:多表查询的条件不能少于表的个数-1。2张表的过滤条件至少1个,3张表查询过滤条件至少2个,依次类推

多表查询

-- 1.如果显示部门号为10的部门名,员工名和工资(部门号也一起显示)

/*
	员工名 工资 emp表
	部门名 部门号 dept表
*/

-- 过滤条件为工资必须在相应的级别内
SELECT ename,sal,dname,emp.`deptno` FROM emp,dept
WHERE emp.`deptno`= dept.`deptno` AND emp.`deptno`=10;

-- 2.显示各个员工的姓名,工资,及其工资的级别
-- 姓名和工资-->emp表
-- 工资级别--->salgrade表
SELECT ename,sal,grade FROM emp,salgrade 
WHERE sal BETWEEN losal AND hisal;



写一个sql先写一个简单的,然后进行条件的叠加

  • 练习

-- 练习
-- 显示雇员名,雇员工资以及所在部门的名字,并按照部门排序[降序排]

-- 雇员名,雇员工资 emp表
-- 部门名 dept表

SELECT  ename,sal,dname , emp.`deptno` FROM emp,dept

WHERE emp.`deptno` = dept.`deptno` ORDER BY emp.`deptno` DESC;




order 放在 where后面

自连结


alias:别名

  • 当我们使用同一个表名时将会报错

    1.起的别名不需要使用引号引起来2.别名的as可以省略
-- 多表查询的自连接
-- 显示公司员工名字和他的上级名字

SELECT worker.`ename` AS '职员名',boss.`ename` AS   '上级名'
 FROM   emp   worker ,  emp  boss
 WHERE worker.`mgr`= boss.`empno`;
 
SELECT * FROM emp;
 

将1张表当成2张表使用,过滤条件是雇员的上级编号=上级的员工号
查询结果

  • 总结

    必须对分成的2张表分别取别名,否则将会报错

多行子查询


单行子查询

--  子查询演示
-- :如何显示与SMITH同一部门的所有员工
# 分析:我们在查询时限制条件,肯定是部门号等于SMTH的部门号
-- 1.可以先查询到SMITH的部门号
-- 2.把上面的select语句当作一个子查询来使用


-- 1.查询到SMITH的部门号
SELECT deptno 
FROM emp WHERE ename = 'SMITH';

-- 2.将上面的SQL进行嵌套


## 因为该子查询返回的只有一行,所有也称为单行子查询
SELECT ename,deptno 
FROM emp WHERE deptno=(SELECT deptno 
FROM emp WHERE ename = 'SMITH');

  • 子句返回结果
  • 嵌套后返回的结果

    在mysql中等于用=表示,不等使用!=或者<>表示

多行子查询

-- 多行子查询

-- 如何查询和部门10的工作相同的雇员的
-- 名字 岗位 工资 部门号 但是不含10号部门自己的雇员
-- 思路:
-- 1.先查询到部门=10有哪些工作(需要注意对返回的工作进行去重)
SELECT DISTINCT job FROM emp
WHERE deptno =10;
-- 2.将上面查询的结果当作子查询使用
SELECT job,sal,deptno FROM emp
WHERE job IN(SELECT DISTINCT job FROM emp
WHERE deptno =10);-- job必须是部门号=10中的一个

-- 进一步优化,不能包含10号部门自己的员工

SELECT job,sal,deptno FROM emp
WHERE job IN(SELECT DISTINCT job FROM emp
WHERE deptno =10)AND deptno !=10;

子查询临时表

子句运行结果:先找到各个类别,价格最高的商品 ,并将其作为一个临时表

然后将临时表和esc_goods表进行多表查询并过滤即可得到结果

经过分析:2个表的过滤条件应该是临时表是最高价格=商品表的价格 并且要求cat_id相同

  • 完整代码
-- 查询ecshop中各个类别中,价格最高的商品

-- 1.查询商品表
-- 先得到各个类别中,价格最高的商品 max+grop by cat_id,当做临时表
 SELECT cat_id,MAX(shop_price) 
	FROM ecs_goods
	GROUP BY cat_id;
	
	
	
# 2.将前面的表作为临时表

SELECT goods_id,ecs_goods.cat_id,goods_name,shop_price
FROM (

	SELECT cat_id,MAX(shop_price) AS max_price-- 将该列起别名
	FROM ecs_goods
	GROUP BY cat_id)temp,-- 将临时表起个别名temp
	ecs_goods
	WHERE temp.cat_id = esc_goods.cat_id
	AND  temp.max_price = esc_goods.shop_price;
	
  • 最终运行结果

all 和any

子句:显示部门号=30的所有薪资

SELECT * FROM emp;

**all需要和其中的所有对比**

-- all和any的使用


-- 要求:显示工资比部门30的所有员工的工资高的员工的姓名 工资和部门号

# 子查询

SELECT sal FROM emp
WHERE deptno=30;

-- 子查询将会返回多个结果,直接>将会出错,用all指向所有
-- 方案一
SELECT ename,sal,deptno FROM emp

WHERE sal>ALL(SELECT sal FROM emp
WHERE deptno=30
);

-- 方案二(在子句中直接查询最高工资)
SELECT ename,sal,deptno FROM emp
WHERE sal>(SELECT MAX(sal)FROM emp WHERE deptno =30);

any只用和其中的一个对比

-- 演示any的使用

-- 要求:显示工资比部门30其中的一个员工的工资高的员工的姓名 工资和部门
-- 方案一
SELECT ename,sal,deptno FROM emp

WHERE sal>ANY(SELECT sal FROM emp
WHERE deptno=30
);
-- 方案二(比其中的一个高,一定是比其中的最低工资高)
SELECT ename,sal,deptno FROM emp

WHERE sal>(SELECT MIN(sal) FROM emp
WHERE deptno=30
);

多列子查询

多行子查询的意思是子查询的结果返回的是多列的数据

  -- 多列子查询
  -- 思考如何查询与ALLEN的部门和岗位完全相同的所有雇员(并且不含
  -- ALLEN本人)
  -- 对于该种情况的格式(和子查询字段一一对应)
  -- (字段1,字段2...)=(select 字段1,字段2...)
  -- 子查询:找到ALLEN的部门和岗位
  
   SELECT job,deptno FROM emp
   WHERE ename='SMITH'
   
   
   SELECT * FROM emp
   
   WHERE(job,deptno)=( SELECT job,deptno FROM emp
   WHERE ename='ALLEN')AND ename!='ALLEN';

练习

  • 数据来源
-- 查询和宋江数学 英语 语文成绩完全相同的学生

-- 子查询:查询出宋江的数学 英语 语文成绩

SELECT math,chinese,english FROM student
WHERE `name`='宋江';

-- 复合句
SELECT * FROM student
WHERE (math,chinese,english)=(SELECT math,chinese,english FROM student
WHERE `name`='宋江');

子查询练习

# 在from 中使用子查询
-- 查找每个部门工资高于本部门平均工资的人的资料
-- 提示将子查询当成一个临时表使用(很自然的想到)
-- 子查询
-- 1.找到每个部门的平均工资

SELECT deptno,AVG(sal)AS avg_sal FROM emp
GROUP BY deptno;
-- 2.将上面的子查询作为一个临时表和emp双表笛卡尔积

SELECT ename,job,mgr,hiredate,sal,comm,emp.`deptno`

FROM emp,(SELECT deptno,AVG(sal)AS avg_sal FROM emp
GROUP BY deptno)AS temp
WHERE emp.`deptno`=temp.`deptno`AND sal>temp.avg_sal;

-- 查找每个部门工资最高的人的详细资料

-- 1.先找到每个部门工资最高的人

SELECT MAX(sal)AS max_sal,deptno FROM emp

GROUP BY deptno;


-- 2.进行多表查询

SELECT ename,job,hiredate,sal,comm,emp.`deptno`
FROM emp,(SELECT MAX(sal)AS max_sal,deptno FROM emp

GROUP BY deptno)AS temp

WHERE emp.`deptno`=temp.`deptno`AND sal=max_sal

-- 查询每个部门的信息(包含:部门名 部门编号 地址)
-- 和人员数量
-- 1.部门名 部门编号 地址来自dept表
-- 2.每个部门的人员数量-->构造一个临时表
-- 1.临时表sql
SELECT COUNT(*)AS '部门人员数量',deptno FROM emp
GROUP BY deptno
-- 2.嵌套操作
SELECT dname,dept.deptno,loc,部门人员数量 FROM dept,
(SELECT COUNT(*)AS '部门人员数量',deptno FROM emp
GROUP BY deptno)AS temp
WHERE dept.`deptno`=temp.`deptno`;



  • 运行结果

    另一种写法(简易显示列)
-- 另一种写法( 可以将一张表中的所有的列都显示出来)
-- 表.*表示将该表所有的列都显示出来
SELECT temp.*,loc,dname FROM dept,
(SELECT COUNT(*)AS '部门人员数量',deptno FROM emp
GROUP BY deptno)AS temp
WHERE dept.`deptno`=temp.`deptno`;

** 如果2个表时只写一个*,就是将2张表的列都显示出来了**

当2张表的列名不重复的时候,才可以直接写列名

表复制和去重


desc+表名 查看表的结构

-- 表的复制
-- 为了对某条sql语句进行效率测试,我们需要海量的数据时,可以使用此法为表
-- 创建海量的数据
-- 1.创建一张表
CREATE TABLE my_tab02
(
id INT ,
`name` VARCHAR(32),
sal DOUBLE ,
job VARCHAR(32),
deptno INT);

-- 2.将emp表的记录复制到my_tab02
-- 将emp表对应的列的信息复制的my_tab02表的对应字段
INSERT INTO my_tab02 -- 所需要的列要匹配
(id,`name`,sal,job,deptno)
SELECT empno,ename,sal,job,deptno FROM emp;
-- 3.进行自我复制
-- 反复执行该语句,数据将会成倍增加
INSERT INTO my_tab02-- 此时
SELECT * FROM my_tab02




删除一张表的重复记录

-- 如何删除一张表重复的记录
-- 1.先创建一张表my_tab03
CREATE TABLE my_tab03 LIKE emp;-- 将emp表的结构(列信息)复制给my_tab03

-- 2.使用自我复制使得该表有重复记录

-- 将下面的复制语句执行2遍就已经有了13条重复的记录(总共26条记录)
INSERT INTO my_tab03
SELECT * FROM emp;

-- 3.考虑去重my_tab03的记录

-- 思路:
/*
1.先创建一张临时表my_temp,该表的结构和my_tab03一样
2.把my_tab03的记录通过disdtinct处理后复制到my_temp中
3。清除掉my_tab03的记录
4.把my_temp的记录复制到my_tab03中
5.drop掉临时表my_temp
*/

-- 1.先创建一张临时表my_temp,该表的结构和my_tab03一样
CREATE TABLE my_temp LIKE my_tab03
-- 2.把my_tab03的记录通过disdtinct处理后复制到my_temp中

INSERT my_temp 
SELECT DISTINCT * FROM my_tab03

-- 此时my_temp包含13条不重复的记录
-- 3.清除掉my_tab03的记录
DELETE FROM my_tab03

-- 4.把my_temp的记录复制到my_tab03中
INSERT my_tab03 
SELECT * FROM my_temp;
-- 5.删除掉临时表my_temp

DROP my_temp;


有点想java中交换2个变量的值,使用一个中间变量进行过度

其实当将my_tab03表中的数据数据复制到的temp时,可以直接原表删除,然后将临时表该名为我们之间的表即可

合并查询

  • 语句一结果
  • 语句二结果

    使用union all合并

    只能进行简单的合并,并不能去重,而且只有当2个子句查询的列是一样的才能进行合并
    union合并

    可以将相同的记录去除

外连接需求

  • 多表查询思路


思路概括为多表笛卡尔积,然后使用使用where子句进行过滤
是根据关联条件,显示所有匹配的记录,如果匹配不上将不会显示

使用前面的多表查询实现

  • dept表
  • emp表

    使用多表查询的结果

这样的方式不能显示没有员工的部门SALES,emp表没有和dept关联的deptno

由此可以引出我们的外连接来解决这个问题


左外连接:左边这个表即使没有匹配的记录也会将左边这个表的记录显示出来
右外连接:右边这个表即使没有匹配的记录也会将右边这个表的记录显示出来

左外连右外连

exam和stu表

-- 左外连右外连

-- 1.创建stu
CREATE TABLE stu (
	id INT,
	`name` VARCHAR(32));
INSERT INTO stu VALUES(1,'jack'),(2,'tom'),(3,'kity'),(4,'nono');

-- 2.创建exam

CREATE TABLE exam(
	id INT ,
	grade INT);

INSERT INTO exam VALUES (1,56),(2,76),(11,8);
SELECT * FROM stu;


-- 要求:显示所有人的成绩,姓名和id号,如果没有成绩也要显示该人的
-- 姓名和id,成绩显示为空
-- 1.直接使用双表笛卡尔积,然后按照id号相同过滤

SELECT `name`,stu.`id`,grade 
FROM stu,exam
WHERE stu.`id`=exam.`id`

-- 此时发现没有成绩的学生将没有显示
-- 2.改成左外连接(where去除,表之间的,不要)
-- 此时左边的表就是左表,右边就是右表
SELECT `name`,stu.`id`,grade 
FROM stu LEFT JOIN exam ON
 stu.`id`=exam.`id`

-- 此时左表即使没有匹配上也将会显示出来

-- 使用右外连接(显示所有成绩,即使没有名字匹配,显示空)

SELECT `name`,stu.`id`,grade 
FROM stu RIGHT JOIN exam ON
 stu.`id`=exam.`id`





练习

-- 列出部门名称和这些部门的员工信息(名字和工作),同时列出
-- 那些没有员工的部门名
-- 1.使用右外连接实现
SELECT dname,ename,job
FROM emp RIGHT JOIN dept
 ON dept.`deptno`=emp.`deptno`
 -- 2.使用左外连接实现-
 -- 将2个表交换一下位置就可以了
 SELECT dname,ename,job
FROM dept LEFT JOIN emp
 ON dept.`deptno`=emp.`deptno`
 

 
 

posted @ 2023-04-13 17:27  一往而深,  阅读(60)  评论(0编辑  收藏  举报