MySQL自连接查询的深入分析
一般的连接查询使用两张不同的表,指定连接条件,然后进行查询。自连接查询格式和普通的连接查询书写格式完全相同,只不过我们需要把一张表想象成两张表使用。
自连接查询是自己和自己连接,分别给一张表取两个不同的别名,然后附上连接条件。格式如下:
select 字段列表
from table_name alias1 join table_name alias2
on alias1.fieldname1=alias2.fieldname2
......;
一、根据emp(员工)表,查询每个员工的领导,员工表的结构如下:
create table emp(
e_id int primary key,
e_name char(20) not null default '',
phone char(20) not null default '',
leader int default null
);
insert into emp values(1,'张鹏辉','13603735588',2),(2,'王大强','13603736666',2),
(3,'刘莉莉','13603732222',2),(4,'王安全','13603733377',2),
(5,'王江涛','13603733366',5),(6,'刘大江','13603736644',5),
(7,'刘鹏','13603738866',8),(8,'王大鹏','13603732635',8),
(9,'王俊红','13603734468',8),(10,'王海军','13603735525',8);
因为员工的领导也是员工,因此我们可以将emp表想象成两张表,一张为employee,一张为leader。employee表和leader表通过别名来指定,然后利用employee表的字段leader和leader表的字段e_id建立连接即可查询到每个员工的领导。代码及查询结果如下:
mysql> select employee.e_id as 员工编号,employee.e_name as 员工姓名,
-> employee.phone,leader.e_name as 领导
-> from emp employee inner join emp leader
-> on employee.leader=leader.e_id;
+--------------+--------------+-------------+-----------+
| 员工编号 | 员工姓名 | phone | 领导 |
+--------------+--------------+-------------+-----------+
| 1 | 张鹏辉 | 13603735588 | 王大强 |
| 2 | 王大强 | 13603736666 | 王大强 |
| 3 | 刘莉莉 | 13603732222 | 王大强 |
| 4 | 王安全 | 13603733377 | 王大强 |
| 5 | 王江涛 | 13603733366 | 王江涛 |
| 6 | 刘大江 | 13603736644 | 王江涛 |
| 7 | 刘鹏 | 13603738866 | 王大鹏 |
| 8 | 王大鹏 | 13603732635 | 王大鹏 |
| 9 | 王俊红 | 13603734468 | 王大鹏 |
| 10 | 王海军 | 13603735525 | 王大鹏 |
+--------------+--------------+-------------+-----------+
10 rows in set (0.00 sec)
二、area表的结构如下:
create table area(
area_id int primary key,
area_name char(50) not null default '',
parent_id int not null default 0
);
insert into area values(1,'河南省',0),(2,'湖北省',0),(3,'河北省',0),
(101,'郑州市',1),(102,'新乡市',1),(10101,'金水区',101),
(10102,'中牟县',101),(10103,'巩义市',101),(10104,'新郑市',101),
(10201,'红旗区',102),(10202,'牧野区',102),(10203,'辉县市',102),
(10204,'原阳县',102),(10205,'修武县',102),(10206,'获嘉县',102),
(201,'武汉市',2),(202,'宜昌市',2),(20101,'汉阳区',201),(20102,'武昌区',201),
(20103,'青山区',201),(20104,'洪山区',201),(20201,'当阳市',202),(20202,'枝江市',202);
根据area表查询某个地区所属的地区,查询命令及查询结果如下:
mysql> select area.*,superior_area.area_name as superior_area
-> from area left join area superior_area
-> on area.parent_id=superior_area.area_id;
+---------+-----------+-----------+---------------+
| area_id | area_name | parent_id | superior_area |
+---------+-----------+-----------+---------------+
| 1 | 河南省 | 0 | NULL |
| 2 | 湖北省 | 0 | NULL |
| 3 | 河北省 | 0 | NULL |
| 101 | 郑州市 | 1 | 河南省 |
| 102 | 新乡市 | 1 | 河南省 |
| 201 | 武汉市 | 2 | 湖北省 |
| 202 | 宜昌市 | 2 | 湖北省 |
| 10101 | 金水区 | 101 | 郑州市 |
| 10102 | 中牟县 | 101 | 郑州市 |
| 10103 | 巩义市 | 101 | 郑州市 |
| 10104 | 新郑市 | 101 | 郑州市 |
| 10201 | 红旗区 | 102 | 新乡市 |
| 10202 | 牧野区 | 102 | 新乡市 |
| 10203 | 辉县市 | 102 | 新乡市 |
| 10204 | 原阳县 | 102 | 新乡市 |
| 10205 | 修武县 | 102 | 新乡市 |
| 10206 | 获嘉县 | 102 | 新乡市 |
| 20101 | 汉阳区 | 201 | 武汉市 |
| 20102 | 武昌区 | 201 | 武汉市 |
| 20103 | 青山区 | 201 | 武汉市 |
| 20104 | 洪山区 | 201 | 武汉市 |
| 20201 | 当阳市 | 202 | 宜昌市 |
| 20202 | 枝江市 | 202 | 宜昌市 |
+---------+-----------+-----------+---------------+
23 rows in set (0.00 sec)
根据area表查询某个地区所包含的其他地区,查询命令如下:
mysql> select area.area_id,area.area_name,
-> subordinate_area.area_id as subordinate_area_id,
-> subordinate_area.area_name as subordinate_area_name
-> from area subordinate_area right join area
-> on subordinate_area.parent_id=area.area_id;
+---------+-----------+---------------------+-----------------------+
| area_id | area_name | subordinate_area_id | subordinate_area_name |
+---------+-----------+---------------------+-----------------------+
| 1 | 河南省 | 101 | 郑州市 |
| 1 | 河南省 | 102 | 新乡市 |
| 2 | 湖北省 | 201 | 武汉市 |
| 2 | 湖北省 | 202 | 宜昌市 |
| 101 | 郑州市 | 10101 | 金水区 |
| 101 | 郑州市 | 10102 | 中牟县 |
| 101 | 郑州市 | 10103 | 巩义市 |
| 101 | 郑州市 | 10104 | 新郑市 |
| 102 | 新乡市 | 10201 | 红旗区 |
| 102 | 新乡市 | 10202 | 牧野区 |
| 102 | 新乡市 | 10203 | 辉县市 |
| 102 | 新乡市 | 10204 | 原阳县 |
| 102 | 新乡市 | 10205 | 修武县 |
| 102 | 新乡市 | 10206 | 获嘉县 |
| 201 | 武汉市 | 20101 | 汉阳区 |
| 201 | 武汉市 | 20102 | 武昌区 |
| 201 | 武汉市 | 20103 | 青山区 |
| 201 | 武汉市 | 20104 | 洪山区 |
| 202 | 宜昌市 | 20201 | 当阳市 |
| 202 | 宜昌市 | 20202 | 枝江市 |
| 3 | 河北省 | NULL | NULL |
| 10101 | 金水区 | NULL | NULL |
| 10102 | 中牟县 | NULL | NULL |
| 10103 | 巩义市 | NULL | NULL |
| 10104 | 新郑市 | NULL | NULL |
| 10201 | 红旗区 | NULL | NULL |
| 10202 | 牧野区 | NULL | NULL |
| 10203 | 辉县市 | NULL | NULL |
| 10204 | 原阳县 | NULL | NULL |
| 10205 | 修武县 | NULL | NULL |
| 10206 | 获嘉县 | NULL | NULL |
| 20101 | 汉阳区 | NULL | NULL |
| 20102 | 武昌区 | NULL | NULL |
| 20103 | 青山区 | NULL | NULL |
| 20104 | 洪山区 | NULL | NULL |
| 20201 | 当阳市 | NULL | NULL |
| 20202 | 枝江市 | NULL | NULL |
+---------+-----------+---------------------+-----------------------+
37 rows in set (0.00 sec)
还可以利用分组(Group by)改变显示方式,代码如下:
mysql> select area.area_id,area.area_name,
-> group_concat(subordinate_area.area_name) as subordinate_area_name
-> from area subordinate_area right join area
-> on subordinate_area.parent_id=area.area_id
-> group by area.area_id;
+---------+-----------+-------------------------------------------------------------+
| area_id | area_name | subordinate_area_name |
+---------+-----------+-------------------------------------------------------------+
| 1 | 河南省 | 郑州市,新乡市 |
| 2 | 湖北省 | 宜昌市,武汉市 |
| 3 | 河北省 | NULL |
| 101 | 郑州市 | 新郑市,金水区,中牟县,巩义市 |
| 102 | 新乡市 | 修武县,红旗区,获嘉县,牧野区,辉县市,原阳县 |
| 201 | 武汉市 | 青山区,洪山区,汉阳区,武昌区 |
| 202 | 宜昌市 | 当阳市,枝江市 |
| 10101 | 金水区 | NULL |
| 10102 | 中牟县 | NULL |
| 10103 | 巩义市 | NULL |
| 10104 | 新郑市 | NULL |
| 10201 | 红旗区 | NULL |
| 10202 | 牧野区 | NULL |
| 10203 | 辉县市 | NULL |
| 10204 | 原阳县 | NULL |
| 10205 | 修武县 | NULL |
| 10206 | 获嘉县 | NULL |
| 20101 | 汉阳区 | NULL |
| 20102 | 武昌区 | NULL |
| 20103 | 青山区 | NULL |
| 20104 | 洪山区 | NULL |
| 20201 | 当阳市 | NULL |
| 20202 | 枝江市 | NULL |
+---------+-----------+-------------------------------------------------------------+
23 rows in set (0.00 sec)