本文主要讲解mysql中一些表的连接查询,主要有
为了让大家理解,我这里准备了两个表: tb_boy 和 tb_girl 。
男生创表语句:
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 |
+----+----------+-----------+-----------------+-----------+
女生创表语句:
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
如果是要进行排序,则不用谢子语句中,但是要排序的字段一定要是第一个表查询所存在的列,比如:
(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 等就行了
现在有个新的需求了,我们需要查出所有员工比自己部门的平均工资还高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 |
+--------------+--------------+--------------+-----------------------+----------+
我这里就没有继续去连表了