MySQL之DQL(数据查询语言)- 表连接查询

目录

    • 一、简介
      • 1.1、男生表
      • 1.2、女生表
    • 二、左连接查询(左外连接)
    • 三、右连接查询(右外连接)
    • 四、内连接查询
    • 五、全连接查询
    • 五、自连接查询
    • 六、子查询
    • 七、伪表查询
    • 学习计划:

一、简介

  本文主要讲解mysql中一些表的连接查询,主要有

  • 左连接查询
  • 右连接查询
  • 内连接查询
  • 自连接查询
  • 子查询
  • 伪表查询

  为了让大家理解,我这里准备了两个表: tb_boy tb_girl

1.1、男生表

  男生创表语句:

CREATE TABLE `tb_boy` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `boy_code` int unsigned NOT NULL DEFAULT '0' COMMENT '男孩编号',
  `boy_name` varchar(20) NOT NULL DEFAULT '' COMMENT '男孩姓名',
  `desp` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
  `girl_code` int DEFAULT NULL COMMENT '女孩编号',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_boy_code` (`boy_code`),
  KEY `idx_girl_code` (`girl_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='男生信息表';

  男生表里的数据:

+----+----------+-----------+-----------------+-----------+
| id | boy_code | boy_name  | desp            | girl_code |
+----+----------+-----------+-----------------+-----------+
|  1 |     1001 | 王宝强    | 殿堂级演技      |      NULL |
|  2 |     1002 | 邓超      | 搞笑派明星      |      2002 |
|  3 |     1003 | 陈小春    | 古惑仔精髓      |      2003 |
+----+----------+-----------+-----------------+-----------+

1.2、女生表

  女生创表语句:

CREATE TABLE `tb_girl` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `girl_code` int unsigned DEFAULT '0' COMMENT '女孩编号',
  `girl_name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '女孩名字',
  `desp` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '描述',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_girl_code` (`girl_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='女生信息表';

  男生表里的数据:

+----+-----------+-----------+-----------------+
| id | girl_code | girl_name | desp            |
+----+-----------+-----------+-----------------+
|  1 |      2001 | 马蓉      | 婚内出轨         |
|  2 |      2002 | 孙俪      | 实力派演员       |
|  3 |      2003 | 应采儿    | 山鸡哥老婆       |
|  4 |      2004 | 凤姐      | 在美国等死       |
+----+-----------+-----------+-----------------+

  基于上面的数据我们开始我们的相亲大会

二、左连接查询(左外连接)

  平常我们说的 左连接 ,实际上它是外链接中 左外连接 的的一个简称,对应说明如下:

名词 语法表示
左连接 LEFT JOIN
左外连接 LEFT OUTER JOIN

   左连接 的查询结构如下( l r 是别名):

SELECT * FROM 左表 l LEFT JOIN 右表 r ON l.左表关联字段=r.右表被关联字段;

  相亲大会开始,由男生去选自己的对象,我们这里把男生当作左表,女生当作右表,采用 左连接查询 ,具体的查询语句如下:

 SELECT 
	 b.`boy_code` AS '男生编号',
	 b.`boy_name` AS '男生姓名',
	 b.`girl_code` AS '男生对象的编号',
	 g.`girl_code` AS '女生编号',
	 g.`girl_name` AS '女生姓名'
 FROM `tb_boy` b LEFT JOIN `tb_girl` g 
 ON b.`girl_code`= g.`girl_code`;

结果如下:

+--------------+--------------+-----------------------+--------------+--------------+
| 男生编号     | 男生姓名     | 男生对象的编号        | 女生编号     | 女生姓名     |
+--------------+--------------+-----------------------+--------------+--------------+
|         1001 | 王宝强       |                  NULL |         NULL | NULL         |
|         1002 | 邓超         |                  2002 |         2002 | 孙俪         |
|         1003 | 陈小春       |                  2003 |         2003 | 应采儿       |
+--------------+--------------+-----------------------+--------------+--------------+

  从这里可以看到,使用左连接查询,左表的数据都会显示出来,而右表只会显示符合搜索条件的记录,不符合条件的都会显示为NULL。

三、右连接查询(右外连接)

  平常我们说的 右连接 ,实际上它是外链接中 右外连接 的的一个简称,对应说明如下:

名词 语法表示
右连接 RIGHT JOIN
右外连接 RIGHT OUTER JOIN

   右连接 的查询结构如下:

SELECT * FROM 左表 l RIGHT JOIN 右表 r ON l.左表关联字段=r.右表被关联字段;

  现在社会开放了,女生也想自己主动去选自己的对象,我们这里还是把男生当作左表,女生当作右表,采用 右连接查询 ,具体的查询语句如下:

 SELECT
	 b.`boy_code` AS '男生编号',
	 b.`boy_name` AS '男生姓名',
	 b.`girl_code` AS '男生对象的编号',
	 g.`girl_code` AS '女生编号',
	 g.`girl_name` AS '女生姓名'
 FROM `tb_boy` b RIGHT JOIN `tb_girl` g ON b.`girl_code`=g.`girl_code`;

结果如下:

+--------------+--------------+-----------------------+--------------+--------------+
| 男生编号     | 男生姓名     | 男生对象的编号        | 女生编号     | 女生姓名     |
+--------------+--------------+-----------------------+--------------+--------------+
|         NULL | NULL         |                  NULL |         2001 | 马蓉         |
|         1002 | 邓超         |                  2002 |         2002 | 孙俪         |
|         1003 | 陈小春       |                  2003 |         2003 | 应采儿       |
|         NULL | NULL         |                  NULL |         2004 | 凤姐         |
+--------------+--------------+-----------------------+--------------+--------------+

  从这里可以看到,使用右连接查询,左表的数据都会显示出来,而左表表只会显示符合搜索条件的记录,不符合条件的也会显示为NULL。

四、内连接查询

  我们看完这两个查询,发现使用 左连接查询 男生选对象的时候,我们的影帝 王宝强 老婆跑了,在相亲大会上狠狠吃了把狗粮。

  使用 右连接查询 女生选对象时, 马蓉 因为出轨找不到对象, 凤姐 要没找到她心目中的男神 陈冠希

  这样相亲着实有点尴尬啊,怎么样避免尴尬呢,那就是我们的 内连接查询 ,它的查询结构如下( l r 是别名):

SELECT * FROM 左表 l INNER JOIN 右表 r ON l.左表关联字段=r.右表被关联字段;

  我们希望没匹配的就别再台上吃狗粮和傻站着了,开始我们的查询

 SELECT
 b.`boy_code` AS '男生编号',
 b.`boy_name` AS '男生姓名',
 b.`girl_code` AS '男生对象的编号',
 g.`girl_code` AS '女生编号',
 g.`girl_name` AS '女生姓名'
 FROM `tb_boy` b INNER JOIN `tb_girl` g ON b.`girl_code`=g.`girl_code`;

结果如下:

+--------------+--------------+-----------------------+--------------+--------------+
| 男生编号     | 男生姓名     | 男生对象的编号        | 女生编号     | 女生姓名     |
+--------------+--------------+-----------------------+--------------+--------------+
|         1002 | 邓超         |                  2002 |         2002 | 孙俪         |
|         1003 | 陈小春       |                  2003 |         2003 | 应采儿       |
+--------------+--------------+-----------------------+--------------+--------------+

  从这里可以看到,使用内连接查询,只会返回两个表匹配的数据,只要任意一个表的不匹配都不会显示。现在没有尴尬了,台上只有模范夫妻了。

五、全连接查询

  为了演示这个我们准备点数据,假设小伙子, 唐鹏 非常喜欢抽烟,嚼槟榔(习惯一点也不好),每年都抽,我们把他购买的记录按年存下来了。我这里准备了两个表:当前年的表 tb_record 和历史年的表 tb_record_2021

 CREATE TABLE `tb_record` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `order_id` INT NOT NULL COMMENT '订单编号',
  `amount` DOUBLE(8,2) NOT NULL COMMENT '订单金额',
  `pay_time` DATE NOT NULL COMMENT '支付时间',
  `title` VARCHAR(100) NOT NULL COMMENT '订单标题',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_order_id` (`order_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT '订单表';

 CREATE TABLE `tb_record_2021` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `order_id` INT NOT NULL COMMENT '订单编号',
  `amount` DOUBLE(8,2) NOT NULL COMMENT '订单金额',
  `pay_time` DATE NOT NULL COMMENT '支付时间',
  `title` VARCHAR(100) NOT NULL COMMENT '订单标题',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_order_id` (`order_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT '2021订单表';

订单表当前年数据如下:

+----+----------+--------+------------+-----------------+
| id | order_id | amount | pay_time   | title           |
+----+----------+--------+------------+-----------------+
|  1 | 20220001 |  25.00 | 2022-03-05 | 一包芙蓉王      |
|  2 | 20220002 |   2.00 | 2022-03-04 | 一个打火机      |
|  3 | 20220003 |  20.00 | 2022-03-03 | 一包槟榔        |
+----+----------+--------+------------+-----------------+

订单表2021年数据如下:

+----+----------+--------+------------+-----------------+
| id | order_id | amount | pay_time   | title           |
+----+----------+--------+------------+-----------------+
|  1 | 20210001 |  20.00 | 2021-06-01 | 一包芙蓉王      |
|  2 | 20210002 |   2.00 | 2021-08-01 | 一个打火机      |
+----+----------+--------+------------+-----------------+

  我们先看看 唐鹏 所有的消费。这里涉及到两个表的记录,我们用一个语句得出来。需要用到 全连接 ,关键字是 UNION ALL ,语法结构如下:

(SELECT 字段1,字段2,...,字段n FROM 表A)
UNION ALL
(SELECT 字段1,字段2,...,字段n FROM 表B);

或者

(SELECT 字段1,字段2,...,字段n FROM 表A)
UNION
(SELECT 字段1,字段2,...,字段n FROM 表B);

  现在开始我们的查询

(SELECT * FROM `tb_record`)
UNION ALL
(SELECT * FROM `tb_record_2021`);

运行结果如下:

+----+----------+--------+------------+-----------------+
| id | order_id | amount | pay_time   | title           |
+----+----------+--------+------------+-----------------+
|  1 | 20220001 |  25.00 | 2022-03-05 | 一包芙蓉王      |
|  2 | 20220002 |   2.00 | 2022-03-04 | 一个打火机      |
|  3 | 20220003 |  20.00 | 2022-03-03 | 一包槟榔        |
|  1 | 20210001 |  20.00 | 2021-06-01 | 一包芙蓉王      |
|  2 | 20210002 |   2.00 | 2021-08-01 | 一个打火机      |
+----+----------+--------+------------+-----------------+

  从结果我们就可以看到我们把 唐鹏 所有的消费记录重复了,现在我们需要看看 唐鹏 ,买的商品及价格是哪些,这就可能会涉及到重复的问题,我们使用 UNION ,就会达到去重的效果,具体查询如下:

(SELECT title,amount FROM `tb_record`)
UNION
(SELECT title,amount FROM `tb_record_2021`);

运行结果如下:

+-----------------+--------+
| title           | amount |
+-----------------+--------+
| 一包芙蓉王      |  25.00 |
| 一个打火机      |   2.00 |
| 一包槟榔        |  20.00 |
| 一包芙蓉王      |  20.00 |
+-----------------+--------+

  从结果我们就可以看到打火机都是2块一个,只显示了一个,为什么芙蓉王有两条,因为芙蓉王涨价了!!!

UNION ALL UNION

  • 两个表全连接时,所查询的字段的列数要一样
  • 两个表查询的列名不一样也可以,最终显示的列名是最前面那个表查询的字段,但是值还是各个子查询查询的值
  • UNION ALL 把所有的的记录合并,不会去除重复的
  • UNION 把所有记录合并,并且会去除重复的记录( 必须每一个查询出来的列字段的值都要相等才算重复
  • 非必要不用 UNION ,因为耗时

  如果是要进行排序,则不用谢子语句中,但是要排序的字段一定要是第一个表查询所存在的列,比如:

(SELECT * FROM `tb_record`)
UNION ALL
(SELECT * FROM `tb_record_2021`) ORDER BY id;

运行结果:

+----+----------+--------+------------+-----------------+
| id | order_id | amount | pay_time   | title           |
+----+----------+--------+------------+-----------------+
|  1 | 20220001 |  25.00 | 2022-03-05 | 一包芙蓉王      |
|  1 | 20210001 |  20.00 | 2021-06-01 | 一包芙蓉王      |
|  2 | 20220002 |   2.00 | 2022-03-04 | 一个打火机      |
|  2 | 20210002 |   2.00 | 2021-08-01 | 一个打火机      |
|  3 | 20220003 |  20.00 | 2022-03-03 | 一包槟榔        |
+----+----------+--------+------------+-----------------+

错误示范(必须第一个表中有那个字段):

(SELECT id,amount,pay_time FROM `tb_record`)
UNION ALL
(SELECT id,amount,title FROM `tb_record_2021`) ORDER BY title;

五、自连接查询

  假设我们有个员工表 tb_employee

CREATE TABLE `tb_employee` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `emp_code` INT UNSIGNED DEFAULT NULL COMMENT '员工编码',
  `emp_name` VARCHAR(20) DEFAULT NULL COMMENT '员工姓名',
  `gender` CHAR(1) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '性别',
  `dep_code` INT DEFAULT NULL COMMENT '部门',
  `job` VARCHAR(20) DEFAULT '' COMMENT '工作',
  `salary` DOUBLE(8,2) DEFAULT NULL COMMENT '工资',
  `manage_code` INT DEFAULT NULL COMMENT '所属领导',
  PRIMARY KEY (`id`),
  KEY `idx_emp_code` (`emp_code`),
  KEY `idx_manage_code` (`manage_code`)
) ENGINE=INNODB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT '员工信息表';

  什么叫 自连接 呢,也就是表自己连接自己
比如我们要查询部门编号为12且工资大于3万的员工编号,姓名,职务及领导姓名

SELECT 
e.emp_code AS '员工编号',
e.emp_name AS '员工姓名',
e.job AS '职务',
e.salary AS '薪资',
m.emp_code AS '领导编号',
m.emp_name AS '领导姓名'
FROM tb_employee e, tb_employee m
WHERE  e.manage_code=m.emp_code AND e.dep_code=12 AND  e.salary>30000;

或者

SELECT 
e.emp_code AS '员工编号',
e.emp_name AS '员工姓名',
e.job AS '职务',
e.salary AS '薪资',
m.emp_code AS '领导编号',
m.emp_name AS '领导姓名'
FROM tb_employee e JOIN tb_employee m
ON e.manage_code=m.emp_code
WHERE e.dep_code=12 AND  e.salary>30000;

运行结果:

+--------------+--------------+--------------+----------+--------------+--------------+
| 员工编号     | 员工姓名     | 职务         | 薪资     | 领导编号     | 领导姓名     |
+--------------+--------------+--------------+----------+--------------+--------------+
|         8001 | 谢又蓝       | 技术总监     | 50000.00 |         8000 | 赖晓畅       |
|         8002 | 吕芸溪       | 技术经理     | 34000.00 |         8001 | 谢又蓝       |
|         8003 | 谭淑慧       | 架构师       | 40000.00 |         8002 | 吕芸溪       |
|         8004 | 许米琪       | 架构师       | 35000.00 |         8002 | 吕芸溪       |
|         8005 | 常采枫       | 架构师       | 33000.00 |         8002 | 吕芸溪       |
+--------------+--------------+--------------+----------+--------------+--------------+

六、子查询

  我们要查询技术部门里(假设本次查询的技术部门编号为12)工资比部门平均工资高的员工编号,姓名,职务及工资
我们知道查询部门平均工资

SELECT AVG(salary) FROM tb_employee WHERE dep_code=12;
+--------------+
| AVG(salary)  |
+--------------+
| 15465.000000 |
+--------------+

  因为这个语句查出的就是一个结果值,只要我们在条件判断时大于这个值即可

SELECT 
emp_code AS '员工编号',
emp_name AS '员工姓名',
job AS '职务',
salary AS '薪资'
FROM tb_employee 
WHERE salary >(SELECT AVG(salary) FROM tb_employee WHERE dep_code=12) 
AND dep_code=12;

运行结果:

+--------------+--------------+-----------------------+----------+
| 员工编号      | 员工姓名     | 职务                  | 薪资     |
+--------------+--------------+-----------------------+----------+
|         8001 | 谢又蓝       | 技术总监              | 50000.00 |
|         8002 | 吕芸溪       | 技术经理              | 34000.00 |
|         8003 | 谭淑慧       | 架构师                | 40000.00 |
|         8004 | 许米琪       | 架构师                | 35000.00 |
|         8005 | 常采枫       | 架构师                | 33000.00 |
|         8006 | 吕雨文       | 高级开发工程师        | 28000.00 |
|         8007 | 薛滢渟       | 高级开发工程师        | 26000.00 |
|         8008 | 彭骊艳       | 高级开发工程师        | 24000.00 |
|         8009 | 曾恬美       | 高级开发工程师        | 20000.00 |
|         8010 | 常静山       | 高级开发工程师        | 20000.00 |
|         8011 | 戴芳蕙       | 中级开发工程师        | 18000.00 |
|         8012 | 刘蓉城       | 中级开发工程师        | 18000.00 |
|         8013 | 姜谷蓝       | 中级开发工程师        | 17000.00 |
|         8014 | 叶香之       | 中级开发工程师        | 17000.00 |
|         8015 | 孔愉心       | 中级开发工程师        | 17000.00 |
+--------------+--------------+-----------------------+----------+

  从上面的语句我们可以看到,where条件里有一个查询语句,这个就是我们说的子查询。它就是 把一个SQL语句查询的结果当作另外一个SQL语句的条件
  如果我们要查询的是比运维部门里(部门编号14)所有员工工资都高的员工编号,姓名,职务及工资

SELECT salary FROM tb_employee WHERE dep_code=14

  很明显会有多个结果,本次查询就可以使用 >ALL

SELECT 
emp_code AS '员工编号',
emp_name AS '员工姓名',
job AS '职务',
salary AS '薪资'
FROM tb_employee 
WHERE salary > ALL(SELECT salary FROM tb_employee WHERE dep_code=14);

运行结果:

+--------------+--------------+--------------+-----------+
| 员工编号     | 员工姓名     | 职务         | 薪资      |
+--------------+--------------+--------------+-----------+
|         8000 | 赖晓畅       | Boss         | 100000.00 |
|         8001 | 谢又蓝       | 技术总监     |  50000.00 |
|         8003 | 谭淑慧       | 架构师       |  40000.00 |
|         8004 | 许米琪       | 架构师       |  35000.00 |
|         8041 | 卢敏叡       | 测试总监     |  35000.00 |
+--------------+--------------+--------------+-----------+

如果查出很多的结果,那怎么办呢?我们可以使用 ALL ANY SOME 等就行了

  • ALL 大于所有 或者小于所有
  • ANY 比最大的小或者比最小的大
  • SOME 大于部分或者小于部分
  • IN 是指定的某个值

七、伪表查询

  现在有个新的需求了,我们需要查出所有员工比自己部门的平均工资还高1.5倍的员工编号,姓名,职务及工资。
  我们先看看部门的平均工资

SELECT 
dep_code,
AVG(salary) AS avgSalary 
FROM tb_employee 
GROUP BY dep_code;

运行结果:

+----------+---------------+
| dep_code | avgSalary     |
+----------+---------------+
|       12 |  15465.000000 |
|       13 |  14350.000000 |
|       14 |  14900.000000 |
+----------+---------------+

  返回的结果是多列,我们是没有办法直接用算术表达式的。那怎么办呢,我们把它当做一个结果集,和员工进行连接,这个结果集就相当于一个 伪表

SELECT 
e.emp_code AS '员工编号',
e.emp_name AS '员工姓名',
e.dep_code AS '部门编号',
e.job AS '职务',
e.salary AS '薪资'
FROM tb_employee e
JOIN (SELECT dep_code,AVG(salary) AS avgSalary FROM tb_employee GROUP BY dep_code) AS da 
ON (e.dep_code=da.dep_code AND e.salary>1.5*avgSalary);

运行结果:

+--------------+--------------+--------------+-----------------------+----------+
| 员工编号     | 员工姓名     | 部门编号     | 职务                  | 薪资     |
+--------------+--------------+--------------+-----------------------+----------+
|         8001 | 谢又蓝       |           12 | 技术总监              | 50000.00 |
|         8002 | 吕芸溪       |           12 | 技术经理              | 34000.00 |
|         8003 | 谭淑慧       |           12 | 架构师                | 40000.00 |
|         8004 | 许米琪       |           12 | 架构师                | 35000.00 |
|         8005 | 常采枫       |           12 | 架构师                | 33000.00 |
|         8006 | 吕雨文       |           12 | 高级开发工程师        | 28000.00 |
|         8007 | 薛滢渟       |           12 | 高级开发工程师        | 26000.00 |
|         8008 | 彭骊艳       |           12 | 高级开发工程师        | 24000.00 |
|         8041 | 卢敏叡       |           13 | 测试总监              | 35000.00 |
|         8043 | 廖忆灵       |           13 | 高级测试工程师        | 28000.00 |
|         8044 | 周斯乔       |           13 | 高级测试工程师        | 28000.00 |
|         8045 | 苏醉柳       |           13 | 高级测试工程师        | 26000.00 |
|         8046 | 韩南莲       |           13 | 高级测试工程师        | 25000.00 |
|         8047 | 韦姣丽       |           13 | 高级测试工程师        | 22000.00 |
|         8048 | 周忆秋       |           13 | 高级测试工程师        | 22000.00 |
|         8049 | 唐欣悦       |           13 | 高级测试工程师        | 22000.00 |
|         8081 | 韩隽雅       |           14 | 运维总监              | 34000.00 |
|         8083 | 萧傲霜       |           14 | 高级运维工程师        | 32000.00 |
|         8084 | 严晴波       |           14 | 高级运维工程师        | 28000.00 |
|         8085 | 牛凝芙       |           14 | 高级运维工程师        | 26000.00 |
+--------------+--------------+--------------+-----------------------+----------+

  我这里就没有继续去连表了

  • 12 技术部
  • 13 测试部
  • 14 运维部

学习计划:

  • CentOS 8中Docker安装MySQL8
  • MySQL之DCL(数据控制语言)
  • MySQL之DDL(数据定义语言)
  • MySQL之DML(数据操纵语言)
  • MySQL之DQL(数据查询语言)-常见关键字
  • MySQL之DQL(数据查询语言)-连接查询
  • MySQL之DQL(数据查询语言)-常见函数
  • MySQL之数据库设计三范式

你可能感兴趣的:(MySQL数据库,mysql,DQL,数据查询语言,左连接,右连接)