第7章 关联查询(联合查询)
7.1 什么是关联查询
关联查询:两个或更多个表一起查询。
前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。
比如:员工表和部门表,这两个表依靠“部门编号”进行关联。
7.2 关联查询结果分为几种情况
7.3 关联查询的SQL有几种情况
1、内连接:inner join ... on
结果:A表 ∩ B表
2、左连接:A left join B on
(2)A表全部
(3)A表- A∩B
3、右连接:A right join B on
(4)B表全部
(5)B表-A∩B
4、全外连接:full outer join ... on,但是mysql不支持这个关键字,mysql使用union(合并)结果的方式代替
(6)A表∪B表: (2) A表结果 union (4)B表的结果
(7)A∪B - A∩B (3)A表- A∩B结果 union (5)B表-A∩B结果
/*
(1)凡是联合查询的两个表,必须有“关联字段”,
关联字段是逻辑意义一样,数据类型一样,名字可以一样也可以不一样的两个字段。
比如:t_employee (A表)中did和t_department(B表)中的did。
发现关联字段其实就是“可以”建外键的字段。当然联合查询不要求一定建外键。
(2)联合查询必须写关联条件,关联条件的个数 = n - 1.
n是联合查询的表的数量。
如果2个表一起联合查询,关联条件数量是1,
如果3个表一起联合查询,关联条件数量是2,
如果4个表一起联合查询,关联条件数量是3,
。。。。
否则就会出现笛卡尔积现象,这是应该避免的。
(3)关联条件可以用on子句编写,也可以写到where中。
但是建议用on单独编写,这样呢,可读性更好。
每一个join后面都要加on子句
A inner|left|right join B on 条件
A inner|left|right join B on 条件 inner|left|right jon C on 条件
1、内连接
(1)select 字段列表
from A表 inner join B表 on 关联条件
...;
(2)select 字段列表
from A表 , B表
where 关联条件
...;
(1)和(2)结果一样。但是(2)先产生笛卡尔积再筛选,效率低。
#演示内连接,结果是A∩B
/*
观察数据:
t_employee 看成A表
t_department 看成B表
此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门,
t_department(B表)中有 测试部,在员工表中找不到对应记录的。
*/
#查询所有员工的姓名,部门编号,部门名称
#如果员工没有部门的,不要
#如果部门没有员工的,不要
/*
员工的姓名在t_employee (A表)中
部门的编号,在t_employee (A表)和t_department(B表)都有
部门名称在t_department(B表)中
所以需要联合两个表一起查询。
*/
SELECT ename,did,dname
FROM t_employee INNER JOIN t_department;
#错误Column 'did' in field list is ambiguous
#因为did在两个表中都有,名字相同,它不知道取哪个表中字段了
#有同学说,它俩都是部门编号,随便取一个不就可以吗?
#mysql不这么认为,有可能存在两个表都有did,但是did的意义不同的情况。
#为了避免这种情况,需要在编写sql的时候,明确指出是用哪个表的did
SELECT ename,t_department.did,dname
FROM t_employee INNER JOIN t_department;
#语法对,结果不太对
#结果出现“笛卡尔积”现象, A表记录 * B表记录
/*
(1)凡是联合查询的两个表,必须有“关联字段”,
关联字段是逻辑意义一样,数据类型一样,名字可以一样也可以不一样的两个字段。
比如:t_employee (A表)中did和t_department(B表)中的did。
发现关联字段其实就是可以建外键的字段。当然联合查询不要求一定建外键。
(2)联合查询必须写关联条件,关联条件的个数 = n - 1.
n是联合查询的表的数量。
如果2个表一起联合查询,关联条件数量是1,
如果3个表一起联合查询,关联条件数量是2,
如果4个表一起联合查询,关联条件数量是3,
。。。。
否则就会出现笛卡尔积现象,这是应该避免的。
(3)关联条件可以用on子句编写,也可以写到where中。
但是建议用on单独编写,这样呢,可读性更好。
每一个join后面都要加on子句
A inner|left|right join B on 条件
A inner|left|right join B on 条件 inner|left|right jon C on 条件
*/
SELECT ename,t_department.did,dname
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
SELECT *
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
#查询部门编号为1的女员工的姓名、部门编号、部门名称、薪资等情况
SELECT ename,gender,t_department.did,dname,salary
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did
WHERE t_department.did = 1 AND gender = '女';
#查询部门编号为1的员工姓名、部门编号、部门名称、薪资、职位编号、职位名称等情况
SELECT ename,gender,t_department.did,dname,salary,job_id,jname
FROM t_employee INNER JOIN t_department ON t_employee.did = t_department.did
INNER JOIN t_job ON t_employee.`job_id` = t_job.`jid`
WHERE t_department.did = 1;
2、左连接
#演示左连接
/*
(2)A
(3)A-A∩B
*/
/*
观察数据:
t_employee 看成A表
t_department 看成B表
此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门,
t_department(B表)中有 测试部,在员工表中找不到对应记录的。
*/
#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did;
#查询的是A结果 A left join B
#查询没有部门的员工信息
SELECT ename,salary,t_department.did,dname
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是A - A∩B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。
3、右连接
/*
右连接
(4)B
(5)B - A∩B
*/
#演示右连接
/*
观察数据:
t_employee 看成A表
t_department 看成B表
此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门,
t_department(B表)中有 测试部,在员工表中找不到对应记录的。
*/
#查询所有部门,包括没有对应员工的部门,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did;
#查询的是B结果 A RIGHT join B
#查询没有员工部门的信息
SELECT ename,salary,t_department.did,dname
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是B - A∩B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。
#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_department RIGHT JOIN t_employee
ON t_employee.did = t_department.did;
#查询的是B结果 A RIGHT join B
#查询没有部门的员工信息
SELECT ename,salary,t_department.did,dname
FROM t_department RIGHT JOIN t_employee
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是B - A∩B A right join B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。
4、union
/*
union实现
(6)A∪B
(7)A∪B - A∩B
A-A∩B ∪ B-A∩B
*/
#演示用union合并两个查询结果实现A∪B 和A∪B - A∩B
/*
union合并时要注意:
(1)两个表要查询的结果字段是一样的
(2)UNION ALL表示直接合并结果,如果有重复的记录一并显示
ALL去掉表示合并结果时,如果有重复记录,去掉。
(3)要实现A∪B的结果,那么必须是合并 查询是A表结果和查询是B表结果的select语句。
同样要实现A∪B - A∩B的结果,那么必须是合并查询是A-A∩B结果和查询是B-A∩B的select语句。
*/
#查询所有员工和所有部门,包括没有指定部门的员工和没有分配员工的部门。
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
UNION
SELECT *
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did;
#以下union会报错
SELECT * FROM t_employee
UNION
SELECT * FROM t_department;
/*
错误代码: 1222
The used SELECT statements have a different number of columns
两个Select语句的列数是不同的。
column:列,表中的字段。
columns:很多的字段,即字段列表
select 字段列表 from 表名称;
*/
#联合 查询结果是A表的select 和查询结果是A∩B的select语句,是得不到A∪B
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
UNION
SELECT *
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
#查询那些没有分配部门的员工和没有指定员工的部门,即A表和B表在对方那里找不到对应记录的数据。
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL
UNION
SELECT *
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
7.4 联合查询字段列表问题
#查询字段的问题
#查询每一个员工及其所在部门的信息
#要求:显示员工的编号,姓名,部门编号,部门名称
SELECT eid,ename,did,dname
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
/*
错误代码: 1052
Column 'did' in field list is ambiguous(模糊不清的;引起歧义的)
*/
SELECT eid,ename,t_employee.did,dname
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
#查询每一个员工及其所在部门的信息
#要求,显示员工的编号,姓名,部门表的所有字段
SELECT eid,ename,t_department.*
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;
7.5 自连接
#演示特殊的联合查询/关联查询/多表查询:自连接
#物理上,是一张表
#逻辑上,是两张表
/*
分析表结构:t_employee表
mid:是表示存储员工的领导编号。即该员工归谁管。领导编号其实就是“领导”作为员工身份的员工编号
例如:eid为3的员工邓超远,他的mid是7,表示他的领导是员工编号为7的员工。
eid为7的员工是贾宝玉,他的eid是7,贾宝玉作为员工来说,他的编号是7,作为领导来说,他的编号也是7。
mid的取值范围受到eid字段的限制。mid的值选择必须是eid现有值范围。
可以理解为mid和eid是关联字段,如果要建外键,可以在mid字段上建外键。
foreign key(mid) references t_employee(eid)
此时t_employee既是子表也是父表。
员工表t_employee建立了外键:
CONSTRAINT `t_employee_ibfk_3` FOREIGN KEY (`mid`) REFERENCES `t_employee` (`eid`) ON DELETE SET NULL ON UPDATE CASCADE
*/
#查询每一个员工自己的编号、名字、薪资和他的领导的编号、姓名、薪资。
SELECT emp.eid,emp.ename,emp.salary, mgr.eid,mgr.ename,mgr.salary
FROM t_employee AS emp INNER JOIN t_employee AS mgr
ON emp.mid = mgr.eid;
#把t_employee当成两张表,通过取别名的方式
#t_employee AS emp 把员工表 当成员工表
# t_employee AS mgr 把员工表 当成存储领导信息的领导表
#emp.mid = mgr.eid; 员工表的领导编号就是领导表的员工编号