多表查询规律总结
1. 连接查询的分类
隐式内连接:(结构里没有inner关键字)
语法结构:select ... from 表1,表2 where 连接条件 [and 其他条件] --(外键的值等于主键的值)
显示内连接:(结构里有inner关键字,inner关键字可省略)
语法结构:select ... from a [inner] join b on 连接条件 [ where 其它条件]
左外连接:以join左边的表为主表,展示主表的所有数据,根据条件查询join右边表的数据,若满足条件则展示,若不满足则以null显示。(也可以理解为在内连接的基础上保证左边表的数据全部显示)
语法结构:select [字段][*] from 左表名 left [outer] join 右表名 on 条件
右外连接:以join右边的表为主表,展示主表的所有数据,根据条件查询join左边表的数据,若满足条件则展示,若不满足则以null显示。(也可以理解为在内连接的基础上保证右边表的数据全部显示)
注意:
- 不管我们查询几张表,表连接查询会产生笛卡尔积,我们需要消除笛卡尔积,拿到正确的数据。我们需要找到表与表之间通过哪个字段关联起来的(通常是外键=主键)
- 消除笛卡尔积规律:2张表需要1个条件,3张表需要2个条件,4张表需要3个条件。(条件数 量=表的数量-1),每张表都要参与进来
2. 多表连接查询步骤:
- 确定要查询哪些表
- 确定表连接关系
- 确定查询字段
- 确定使用什么连接
3.经典案例
准备环境:建表
-- 部门表
CREATE TABLE dept (
id INT PRIMARY KEY PRIMARY KEY, -- 部门id
dname VARCHAR(50), -- 部门名称
loc VARCHAR(50) -- 部门位置
);
-- 添加4个部门
INSERT INTO dept(id,dname,loc) VALUES
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');
-- 职务表,职务名称,职务描述
CREATE TABLE job (
id INT PRIMARY KEY,
jname VARCHAR(20),
description VARCHAR(50)
);
-- 添加4个职务
INSERT INTO job (id, jname, description) VALUES
(1, '董事长', '管理整个公司,接单'),
(2, '经理', '管理部门员工'),
(3, '销售员', '向客人推销产品'),
(4, '文员', '使用办公软件');
-- 员工表
CREATE TABLE emp (
id INT PRIMARY KEY, -- 员工id
ename VARCHAR(50), -- 员工姓名
job_id INT, -- 职务id
mgr INT , -- 上级领导
joindate DATE, -- 入职日期
salary DECIMAL(7,2), -- 工资
bonus DECIMAL(7,2), -- 奖金
dept_id INT, -- 所在部门编号
CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);
-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);
-- 工资等级表
CREATE TABLE salarygrade (
grade INT PRIMARY KEY,
losalary INT,
hisalary INT
);
-- 添加5个工资等级
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);
–>练习1.查询所有员工信息。显示员工编号,员工姓名,工资,职务名称,职务描述 (此题考查2张表)
具体操作:
1.确定要查询哪些表:emp e, job j
SELECT * FROM emp e INNER JOIN job j;
2.确定表连接条件: e.job_id=j.id
SELECT * FROM emp e INNER JOIN job j ON e.job_id=j.id;
3.确定查询字段:员工编号,员工姓名,工资,职务名称,职务描述
SELECT e.`id`, e.`ename`, e.`salary`, j.`jname`, j.`description` FROM emp e INNER JOIN job j ON e.job_id=j.id;
4.确定内连接
select e.id,e.ename,e.salary,j.jname,j.description from emp e,job j where e.job_id=j.id;
–>练习2.查询所有员工信息。显示员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置 (此题考查3张表)
-- 确定要查询哪些表:emp e,job j,dept d;
-- 确定表连接关系:e.job_id=j.id and e.dept_id=d.id;
-- 确定查询字段:e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc;
-- 确定内连接
select e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc from emp e,job j,dept d where e.job_id=j.id and e.dept_id=d.id;
–>练习3.查询所有员工信息。显示员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级 (此题考查4张表)
-- 确定要查询哪些表:emp e,job j,dept d,salarygrade s;
-- 确定表连接关系:e.job_id=j.id and e.dept_id=d.id and e.salary between losalary and hisalary;
-- 确定查询字段:e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc,s.grade;
-- 确定内连接
select e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc,s.grade from emp e,job j,dept d,salarygrade s where e.job_id=j.id and e.dept_id=d.id and e.salary between losalary and hisalary;
–>练习4.查询经理的信息。显示员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级 (此题考查between…and…)
-- 确定要查询哪些表:emp e,job j,dept d,salarygrade s;
-- 确定表连接关系:e.job_id=j.id and e.dept_id=d.id and e.salary between losalary and hisalary and j.jname='经理';
-- 确定查询字段:e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc,s.grade;
-- 确定内连接
select e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc,s.grade from emp e,job j,dept d,salarygrade s where e.job_id=j.id and e.dept_id=d.id and e.salary between losalary and hisalary and hisalary and j.jname='经理';
–>练习5.查询出部门编号、部门名称、部门位置、部门人数 (此题考查左外连接)
-- 子查询:查询部门人数
select dept_id,count(*) from emp e group by dept_id;
-- 确定要查询哪些表:dept d,select dept_id,count(*) total from emp e group by dept_id ;
-- 确定表连接关系:d.id=e.dept_id;
-- 确定查询字段:d.id,d.dname,d.loc,e.total;
-- 确定左外连接
select d.id,d.dname,d.loc,e.total from dept d left outer join (select dept_id,count(*) total from emp group by dept_id ) e on d.id=e.dept_id;
–>练习6.查询所有员工信息。显示员工信息和部门名称,没有员工的部门也要显示 (此题考查右外连接)
-- 确定要查询哪些表:emp e,dept d;
-- 确定表连接关系:e.dept_id=d.id;
-- 确定查询字段:e*,d.dname;
-- 确定右外连接
select e.*,d.dname from emp e right outer join dept d on e.dept_id=d.id;
–>练习7.查询所有员工信息。显示员工姓名,员工工资,职务名称,工资等级,并按工资升序排序 (此题考查排序order by)
-- 确定要查询哪些表:emp e,job j,salarygrade s;
-- 确定表连接关系:e.job_id=j.id and e.salary between s.losalary and s.hisalary and order by e.salary asc ;
-- 确定查询字段:e.ename,e.salary,j.jnamee,s.grade;
-- 确定内连接
select e.ename,e.salary,j.jname,s.grade from emp e,job j,salarygrade s where e.job_id=j.id and e.salary between s.losalary and s.hisalary order by e.salary;
–>练习8.列出所有员工的姓名及其直接上级的姓名,没有领导的员工也需要显示(此题考查一张表当作两张表用,取别名)
-- 确定要查询哪些表,emp e1, emp e2
-- 确定表连接条件 e1.mgr=e2.id
-- 确定查询字段:e1.ename,e2.ename
-- 确定左外连接
select e1.ename,e2.ename from emp e1 left outer join emp e2 on e1.mgr=e2.id;
–>练习9.查询入职期早于直接上级的所有员工编号、姓名、部门名称
-- 确定要查询哪些表,emp e1, emp e2,dept d
-- 确定表连接条件 e1.mgr=e2.id and e1.dept_id=d.id and e1.dept_id=d.id and e1.joindate<e2.joindate
-- 确定查询字段:e1.id,e1.ename,d.dname
-- 确定内连接
select e1.id,e1.ename,d.dname from emp e1, emp e2,dept d where e1.mgr=e2.id and e1.dept_id=d.id and e1.dept_id=d.id and e1.joindate<e2.joindate;
–>练习10.查询工资高于公司平均工资的所有员工信息。显示员工信息,部门名称,上级领导姓名,工资等级
-- 查询公司的平均工资
select avg(salary) from emp;
-- 确定要查询哪些表,emp e1,,emp e2,dept d,salarygrade s
-- 确定表连接条件 e1.mgr=e2.id and e1.dept_id=d.id and e1.salary between s.losalary and s.hisalary and e1.salary>(select avg(salary) from emp)
-- 确定查询字段:e1.*,d.dname,e2.ename,s.grade
-- 确定内连接
select e1.*,d.dname,e2.ename,s.grade from emp e1,emp e2,dept d,salarygrade s where e1.mgr=e2.id and e1.dept_id=d.id and e1.salary between s.losalary and s.hisalary and e1.salary>(select avg(salary) from emp);