多表查询

Posted cmja

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多表查询相关的知识,希望对你有一定的参考价值。

创数据库
mysql> CREATE DATABASE five;
Query OK, 1 row affected
使用数据库
mysql> USE five;
Database changed

一、外键
(1)什么是外键?
外键是指引用另一个表中的一列或多列,被引用的列应该具有主键约束或唯一性约束。
外键用于建立和加强两个表数据之间的链接。接下来,通过两张表来讲解什么是外键。创建数据表1
mysql> CREATE TABLE grade(
    -> id int(4) NOT NULL PRIMARY KEY,
    -> name varchar(36)
    -> );
Query OK, 0 rows affected
创建数据表2
mysql> CREATE TABLE student(
    -> sid int(4) NOT NULL PRIMARY KEY,
    -> sname varchar(36),
    -> gid int(4) NOT NULL
    -> );
Query OK, 0 rows affected
学生表(student)中的gid是学生所在的班级id,是引入了班级表(grade)中的主键id。那么gid就可以作为表student的外键。被引用的表,即表grade是主表;引用外键的表,即表student是从表,两个表是主从关系。表student用gid可以连接表grade中的信息,从而建立了两个表数据之间的连接。引入外键后,外键列只能插入参照列存在的值,参照列被参照的值不能被删除,这就保证了数据的参照完整性。
(2)为表添加外键约束:
mysql> alter table grade add constraint FK_ID foreign key(id)REFERENCES student (id);
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0

FK_ID:创建的这个外键的名称
foreign key (id) 这里的id表示本表,也就是grade add表你要建立外键的字段名
references student (id) 这一句是指定你参照的表和列名

注意:
建立外键的表必须是InnoDB型,不能是临时表。因为在MySQL中只有InnoDB类型的表才支持外键。定义外键名时,不能加引号。如:constraint ‘FK_ID‘ 或 constraint " FK_ID "都是错误的。
(3)删除外键约束
mysql> alter table student drop foreign key FK_ID;Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0

二、操作关联表
(1)关联关系
1、多对一
多对一是数据表中最常见的一种关系。比如,员工与部门之间的关系,一个部门可以有多个员工,而一个员工不能属于多个部门,也就是说部门表中的一行在员工表中可以有许多匹配行,但员工表中的一行在部门表中只能有一个匹配行。
在多对一的表关系中,应该将外键建在多的一方,否则会造成数据的冗余。2、多对多
多对多也是数据表中的一种关系。比如学生与课程之间的关系,一个学生可以选择多门课程,当然一门课程也供多个学生选择,也就是说学生表中的一行在课程表中可以有许多匹配行,课程表中的一行在学生表中也有许多匹配行。通常情况下,为了实现这种关系需要定义一张中间表(称为连接表),该表会存在两个外键,分别参照课程表和学生表。
在多对多的关系中,需要注意的是,连接表的两个外键都是可以重复的,但是两个外键之间的关系是不能重复的,所以这两个外键又是连接表的联合主键。3、一对一
一对一关系在实际生活中比较常见,例如人与身份证之间就是一对一的关系,一个人对应一张身份证,一张身份证只能匹配一个人。
一对一关系的两张表在建立外键时,首先要分清主从关系。从表需要主表的存在才有意义,身份证需要人的存在才有意义。因此人为主表,身份证为从表,要在身份证表中建立外键。由实际经验可知,身份证中的外键必须是非空唯一的,因此通常会直接用从表(表身份证)中的主键作为外键。
需要注意的是,一对一关系在数据库中并不常见,因为以这种方式存储的信息通常会放在一个表中。
在实际开发中,一对一关联关系可以应用于:
(1)分割具有很多列的表
(2)由于安全原因而隔离表的一部分
(3)保存临时的数据,并且可以毫不费力地通过删除该表而删除这些数据
(2)添加数据:
为表student和表grade中添加外键约束来建立两个表的关联关系
mysql> alter table student add constraint FK_ID foreign key(gid)REFERENCES grade (id);
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0
查看:
mysql> show create table student;
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                                                              |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| student | CREATE TABLE `student` (
  `sid` int(4) NOT NULL,
  `sname` varchar(36) DEFAULT NULL,
  `gid` int(4) NOT NULL,
  PRIMARY KEY (`sid`),
  KEY `FK_ID` (`gid`)
  CONSTRAINT `FK_ID` FOREIGN KEY(`gid`)REFERENCES `grade` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set
此时,两表间就是多对一的关系了
因为,外键列只能插入参照列中存在的值,所以,如果要为两个表添加数据,需要先为主表grade添加数据
mysql> INSERT INTO grade(id,name)VALUES(1,‘移动一班‘);Query OK, 1 row affected
mysql> INSERT INTO grade(id,name)VALUES(2,‘移动二班‘);Query OK, 1 row affected
由于student表的外键与grade表的主键关联,因此在为student表添加数据时,gid的值只能是1或2,不能使用其他的值
为从表student添加数据
mysql> INSERT INTO student(sid,sname,gid)VALUES(1,‘红‘,1);Query OK, 1 row affected
mysql> INSERT INTO student(sid,sname,gid)VALUES(2,‘绿‘,2);Query OK, 1 row affected
mysql> INSERT INTO student(sid,sname,gid)VALUES(3,‘蓝‘,1);
    -> (4,‘紫‘,2),
    -> (5,‘黄‘,1);
Query OK, 3 rows affected
Records: 3  Duplicates: 0  Warnings: 0
证明是否添加成功:
mysql> select * from student;
+-----+-------+-----+
| sid | sname | gid |
+-----+-------+-----+
|   1 | 红    |   1 |
|   2 | 绿    |   2 |
|   3 | 蓝    |   1 |
|   4 | 紫    |   2 |
|   5 | 黄    |   1 |
+-----+-------+-----+
5 rows in set
假如要查询移动一班有哪些学生,首先需要查询移动一班的id,然后根据这个id在student表中查询该班级有哪些学生:
mysql> SELECT id FROM grade WHERE name=‘移动一班‘;
+----+
| id |
+----+
|  1 |
+----+
1 row in set
查看验证:
mysql> select * from grade;
+----+----------+
| id | name     |
+----+----------+
|  1 | 移动一班 |
|  2 | 移动二班 |
+----+----------+
2 rows in set
从上可以看出移动一班的id为1;
在student表中,查询gid=1的学生,即为移动一班的学生
mysql> SELECT sname FROM student WHERE gid=1;
+-------+
| sname |
+-------+
| 红    |
| 蓝    |
| 黄    |
+-------+
3 rows in set
(3)删除数据
由于grade表和student表之间具有关联关系。参照列被参照的值是不能被删除的,因此,在删除移动一班时,一定要先删除该班级的所有学生,然后再删除班级
将移动一班的所有学生全部删除
mysql> delete from student where sname=‘红‘;
Query OK, 1 row affected
mysql> delete from student where sname=‘蓝‘;
Query OK, 1 row affected
mysql> delete from student where sname=‘黄‘;
Query OK, 1 row affected
mysql> select * from student where gid=1;
Empty set
从上述语句可以看出,student表中已经没有任何学生的记录
在grade表中,将移动一班删除
mysql> DELETE FROM grade WHERE id=1;
Query OK, 1 row affected
mysql> select * from grade;
+----+----------+
| id | name     |
+----+----------+
|  2 | 移动二班 |
+----+----------+
1 row in set
从查询结果可以看出,移动一班被成功的删除了。这样就删除了关联表中的数据。

三、连接查询
(1)交叉连接
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔积,也就是返回第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数,例如department表中有4个部门,employee表中有4个员工,则交叉连接的结果就有4*4=16条数据。
语法格式:SELECT * from 表1 CROSS JOIN 表2;
首先在chapter05数据库中创建两个表,department和employee
mysql> USE five;
Database changed
mysql> CREATE TABLE department(
    -> did int(4) NOT NULL PRIMARY KEY,
    -> dname varchar(36)
    -> );
Query OK, 0 rows affected
mysql> CREATE TABLE employee(
    -> id int(4) NOT NULL PRIMARY KEY,
    -> name varchar(36),
    -> age int(2),
    -> did int(4) NOT NULL
    -> );
Query OK, 0 rows affected
在两个表中分别插入相关数据
mysql> INSERT INTO department(did,dname)VALUES(1,"网络部");
Query OK, 1 row affected
mysql> INSERT INTO department(did,dname)VALUES(2,"媒体部"),(3,"研发部"),(4,"人事部");
Query OK, 3 rows affected
Records: 3  Duplicates: 0  Warnings: 0
mysql> INSERT INTO employee(id,name,age,did)VALUES(1,"红",20,1),(2,"蓝",22,1),(3,"紫",20,2),(4,"绿",25,4);
Query OK, 4 rows affected
Records: 4  Duplicates: 0  Warnings: 0
数据添加成功后
接下来就使用相关语句执行交叉连接
mysql> SELECT * from department CROSS JOIN employee;
+-----+--------+----+------+-----+-----+
| did | dname  | id | name | age | did |
+-----+--------+----+------+-----+-----+
|   1 | 网络部 |  1 | 红   |  20 |   1 |
|   2 | 媒体部 |  1 | 红   |  20 |   1 |
|   3 | 研发部 |  1 | 红   |  20 |   1 |
|   4 | 人事部 |  1 | 红   |  20 |   1 |
|   1 | 网络部 |  2 | 蓝   |  22 |   1 |
|   2 | 媒体部 |  2 | 蓝   |  22 |   1 |
|   3 | 研发部 |  2 | 蓝   |  22 |   1 |
|   4 | 人事部 |  2 | 蓝   |  22 |   1 |
|   1 | 网络部 |  3 | 紫   |  20 |   2 |
|   2 | 媒体部 |  3 | 紫   |  20 |   2 |
|   3 | 研发部 |  3 | 紫   |  20 |   2 |
|   4 | 人事部 |  3 | 紫   |  20 |   2 |
|   1 | 网络部 |  4 | 绿   |  25 |   4 |
|   2 | 媒体部 |  4 | 绿   |  25 |   4 |
|   3 | 研发部 |  4 | 绿   |  25 |   4 |
|   4 | 人事部 |  4 | 绿   |  25 |   4 |
+-----+--------+----+------+-----+-----+
16 rows in set
(2)内连接
内连接(INNER JOIN)又称简单连接或自然连接,是一种常见的连接查询。
内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组合成新的记录,也就是说在内连接查询中,只有满足条件的记录才能出现的查询结果中。
内连接查询的语法格式:SELECT 查询字段 FROM 表1 [INNER] JOIN 表2 ON 表1.关系字段 = 表2.关系字段(INNER可以省略)
在department表和employee表之间使用内连接查询
mysql> SELECT employee.name,department.dname FROM department JOIN employee ON department.did = employee.did;
+------+--------+
| name | dname  |
+------+--------+
| 红   | 网络部 |
| 蓝   | 网络部 |
| 紫   | 媒体部 |
| 绿   | 人事部 |
+------+--------+
4 rows in set
在department表和employee表之间使用WHERE
mysql> SELECT employee.name,department.dname FROM department,employee WHERE department.did=employee.did;
+------+--------+
| name | dname  |
+------+--------+
| 红   | 网络部 |
| 蓝   | 网络部 |
| 紫   | 媒体部 |
| 绿   | 人事部 |
+------+--------+
4 rows in set
在department表和employee表之间使用自连接查询
mysql> SELECT pl.*FROM employee pl JOIN employee p2 ON pl.did=p2.did WHERE p2.name="红";
+----+------+-----+-----+
| id | name | age | did |
+----+------+-----+-----+
|  1 | 红   |  20 |   1 |
|  2 | 蓝   |  22 |   1 |
+----+------+-----+-----+
2 rows in set
(3)外连接
外连接的语法格式:SELECT 所查字段 FROM 表1 LEFT|RIGHT [OUTER] JOIN 表2
ON 表1.关系字段 = 表2.关系字段 WHERE 条件
外连接分为左连接和右连接。
在使用左连接和右连接查询时,查询结果是不一致的,具体如下:
LEFT JOIN(左连接):返回包括左表中的所有记录和右表中符合连接条件的记录。
左连接的结果包括LEFT JOIN子句中指定的左表的所有记录,和所有满足连接条件的记录。如果左表的某条记录在右表中不存在,则在右表中显示为空。
在department表和employee表之间使用左连接查询:
mysql> SELECT department.did,department.dname,employee.name FROM department LEFT JOIN employee ON department.did=employee.did;
+-----+--------+------+
| did | dname  | name |
+-----+--------+------+
|   1 | 网络部 | 红   |
|   1 | 网络部 | 蓝   |
|   2 | 媒体部 | 紫   |
|   3 | 研发部 | NULL |
|   4 | 人事部 | 绿   |
+-----+--------+------+
5 rows in set
从上述结果中可以看出,研发部没有did为3的员工。
RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中符合连接条件的记录。
右连接与左连接正好相反,返回右表中所有指定的记录和所有满足连接条件的记录。如果右表的某条记录在左表中没有匹配,则左表将返回空值。
在department表和employee表之间使用右连接查询:
mysql> SELECT department.did,department.dname,employee.name FROM department RIGHT JOIN employee ON department.did=employee.did;
+-----+--------+------+
| did | dname  | name |
+-----+--------+------+
|   1 | 网络部 | 红   |
|   1 | 网络部 | 蓝   |
|   2 | 媒体部 | 紫   |
| NULL| NULL   | 绿   |
+-----+--------+------+
4 rows in set
从上述结果中可以看出,绿没有被分配部门。
(4) 复合条件连接查询
复合条件连接查询就是在连接查询的过程中,通过添加过滤条件来限制查询结果,使查询结果更加精确。在department表和employee表之间使用复合条件查询,在department表和employee表之间使用内连接查询并将查询结果按照年龄的升序排序:
mysql> SELECT employee.name,employee.age,department.dname FROM department JOIN employee ON department.did=employee.did order by age;
+------+-----+--------+
| name | age | dname  |
+------+-----+--------+
| 红   |  20 | 网络部 |
| 紫   |  20 | 媒体部 |
| 蓝   |  22 | 网络部 |
| 绿   |  25 | 人事部 |
+------+-----+--------+
4 rows in set

四、子查询
(1)带IN关键字的子查询
使用IN关键字进行子查询时,内层查询语句仅仅返回一个数据列,这个数据列中的值将供外层查询语句进行比较操作。查询存在年龄为20岁的员工的部门:
mysql> SELECT * FROM department WHERE did IN(SELECT did FROM employee WHERE age=20);
+-----+--------+
| did | dname  |
+-----+--------+
|   1 | 网络部 |
|   2 | 媒体部 |
+-----+--------+
2 rows in set
SELECT语句中还可以使用NOT IN关键字,其作用正好与IN相反。
查询不存在年龄为20岁的员工的部门:
mysql> SELECT * FROM department WHERE did NOT IN(SELECT did FROM employee WHERE age=20);
+-----+--------+
| did | dname  |
+-----+--------+
|   3 | 研发部 |
|   4 | 人事部 |
+-----+--------+
2 rows in set
(2)带EXISTS关键字的子查询
EXISTS关键字后面的参数可以是任意一个子查询,这个子查询的作用相当于测试,它不产生任何数据,只返回TRUE或FALSE,当返回值为TRUE时,外层查询才会执行。
查询employee表中是否存在年龄大于21岁的员工,如果存在,则查询department表中的所有记录:
mysql> SELECT * FROM department WHERE EXISTS(SELECT did FROM employee WHERE age>21);
+-----+--------+
| did | dname  |
+-----+--------+
|   1 | 网络部 |
|   2 | 媒体部 |
|   3 | 研发部 |
|   4 | 人事部 |
+-----+--------+
4 rows in set
注意的是,EXISTS关键字比IN关键字的运行效率高,所以在实际开发中,特别是大数据量时,推荐使用EXISTS关键字。
(3)带ANY关键字的子查询
ANY关键字表示满足其中任意一个条件,它允许创建一个表达式对子查询的返回值列表进行比较,只要满足内层子查询中的任意一个比较条件,就返回一个结果作为外层查询条件。使用带ANY关键字的子查询,查询满足条件的部门:
mysql> SELECT * FROM department WHERE did>any(SELECT did FROM employee);
+-----+--------+
| did | dname  |
+-----+--------+
|   2 | 媒体部 |
|   3 | 研发部 |
|   4 | 人事部 |
+-----+--------+
3 rows in set
(4)带ALL关键字的子查询
ALL关键字与ANY有点类似,只不过带ALL关键字的子查询返回的结果需同时满足所有内层查询条件。
使用带ALL关键字的子查询,查询满足条件的部门:
mysql> SELECT * FROM department WHERE did>all(SELECT did FROM employee);
Empty set
无,有则会显示;
(5)带比较运算符的子查询
子查询中可以使用比较运算符,如“>”、“<”、“>=”、“=”、“!=”等。
使用带比较运算符的子查询,查询紫是哪个部门的员工:
mysql> SELECT * FROM department WHERE did=(SELECT did FROM employee WHERE name="紫");
+-----+--------+
| did | dname  |
+-----+--------+
|   2 | 媒体部 |
+-----+--------+
1 row in set

以上是关于多表查询的主要内容,如果未能解决你的问题,请参考以下文章

MySQL-04-笔记

Oracle笔记 多表查询

SQL语句 - 多表查询使用详细介绍

多表查询_左连接多表操作_子查询

多表查询

MySQL 如何多表查询