MySQL——复杂的多表查询——以超市交易数据为例
复杂的多表查询——以超市交易数据为例
之前的内容基本上都是基于单表进行的查询操作,但是在实际工作中,数据往往分散在多个表中,这个时候我们就需要用到多表查询的知识了。
通常来说,多表查询主要有两类:一类是纵向的表合并,也就是将结构相同的表上下拼接起来;另一类是横向的表连接,即将多个表中的字段合并到一张大表中。
(1)纵向表合并
纵向表合并非常好理解,就是把多张相同结构的表按照垂直的方向,将它们进行合并,直白的理解就是上下堆叠(也就是记录的追加)。下面我们以某超市的经营数据为例,来学习一下纵向表合并。
假设有一个大型连锁超市,它有很多家加盟店,各个加盟店将不同月份的经营数据存储在各自的数据表中。每家店数据的存储方式都相同,也就是字段都一样,分别为:门店ID、用户ID(如果没有办理会员,则用户ID为空)、订单ID、交易日期、应付金额、折扣金额、实付金额以及支付类型。
现在的需求是需要把A、B、C三个超市的交易记录合并到一张表里面。
纵向合并表需要用到UNION或者UNION ALL关键词,这两个关键词的功能是一样的(都是合并操作),但是还是有很大区别的:
- UNION ALL 在合并表的时候不做任何附加动作,只是将多个表格简单的首尾相连;
- UNION 合并表格的时候,除了拼接之外还会多一个附加动作——去重(以前旧版本还有排序功能,新版本舍弃了排序功能)
在性能方面,UNION ALL的合并速度要比UNION 快的多,尤其是数据量比较大的时候,两者的合并速度差异还是非常明显的。所以在做纵向表合并的时候,一定要考虑清楚是否需要做去重处理。
# UNION 和 UNION ALL 的区别
SELECT pay_type FROM TransC1805
UNION ALL
SELECT pay_type FROM transD1810; SELECT pay_type FROM TransC1805
UNION
SELECT pay_type FROM transD1810;
下面尝试使用UNION ALL 将三张交易表合并成一个表:
SELECT * from TransA1710
UNION ALL
SELECT * from TransB1801
UNION ALL
SELECT * FROM TransC1805;
当需要合并的表格的字段数量和顺序都一样的时候,这样写是没有问题的。如果其中有一个表的结构不一致(包括数量和排布顺序),这样写就会出问题了。比如,现在有超市D在2018年10月份的交易数据,但是这个门店在记录数据的时候,字段顺序做了一些调整:
# 如果按照上面的方式合并,得到的结果就会有问题:会按照错乱的顺序直接拼接
SELECT * FROM TransC1805
UNION ALL
SELECT * FROM transD1810;
# 正确的写法
SELECT * FROM TransC1805
UNION ALL
SELECT shop_id,uid,order_id,date,amt1,amt2,amt3,pay_type FROM transD1810; SELECT shop_id,uid,order_id,idate,amt1,amt2,amt3,pay_type FROM TransC1805
UNION ALL
SELECT shop_id,uid,order_id,date,amt1,amt2,amt3,pay_type FROM transD1810;
当要合并的表的字段顺序不一致的时候,手动将所有需要的字段都写出来,并且保证顺序一致。
有时候我们并不需要将表中所有记录和字段都合并,这个时候可以更加灵活使用UNION ALL来完成,比如:将3张表中支付方式为现金(pay_type=1)的交易合并起来,并且保留门店ID、用户ID、交易订单号、交易时间和实际交易额信息。
# 将3张表中支付方式为现金(pay_type=1)的交易合并起来,并且保留门店ID、用户ID、交易订单号、交易时间和实际交易额信息。
SELECT shop_id,uid,order_id,idate,amt3 from TransA1710
where pay_type=1
UNION ALL
SELECT shop_id,uid,order_id,idate,amt3 from TransB1801
where pay_type=1
UNION ALL
SELECT shop_id,uid,order_id,idate,amt3 FROM TransC1805
where pay_type=1;
(2)表连接操作
关于表连接操作就稍微有点复杂了,这里会用到各种 JOIN操作,对于初学者来说,特别容易搞混,下面我们通过两个简单的数据集来学习一下JOIN连接操作:
CREATE TABLE TA(K INT, V VARCHAR(10));
INSERT INTO TA VALUES
(1,"AB"), (2,"A");
SELECT * FROM TA; CREATE TABLE TB(K INT, V VARCHAR(10));
INSERT INTO TB VALUES
(1,"AB"),
(3,"B");
SELECT * FROM TB;
K=1的记录在TA和TB两张表中都有,K=2的记录为TA表中独有,K=3的记录为TB表中独有。
- 内连接:INNER JOIN
内连接就是把两张表中共有的数据提取出来。
文氏图:
#内连接
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V
FROM TA
INNER JOIN TB
ON TA.K=TB.K;
- 左连接:LEFT JOIN
左连接查询会返回左表(TA)中所有记录,不管右表中有没有关联的数据,如果右表中有关联的数据会被一并返回(右表中没有的字段,则以NULL值填充)。
文氏图:
#左连接
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V
FROM TA #左表
LEFT JOIN TB #右表
ON TA.K=TB.K;
- 右连接:
右连接查询会返回右表(TB)中所有记录,不管左表中有没有关联的数据,如果左表中有关联的数
据会被一并返回(左表中没有的字段,则以NULL值填充)
文氏图:
#右连接
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V
FROM TA
RIGHT JOIN TB
ON TA.K=TB.K;
- 全连接:FULL OUTER JOIN
FULL OUTER JOIN 一般称为全连接或者外连接,实际查询语句中可以写作 FULL OUTER JOIN 或 FULL JOIN 。外连接查询能返回左右表里的所有记录,其中左右表里能关联起来的记录被连接后
返回。
文氏图:
# MySQL中不支持FULL OUTER JOIN,直接使用会报错
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V
FROM TA
FULL OUTER JOIN TB
ON TA.K=TB.K;
# 可以使用UNION 模拟全连接返回的结果
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V FROM TA
LEFT JOIN TB
ON TA.K=TB.K
UNION
SELECT TA.K AS A_K,TB.K AS B_K,TA.V AS A_V,TB.V AS B_V FROM TA
RIGHT JOIN TB
ON TA.K=TB.K;
以上就是SQL中常见的四种表连接方式了。此外,还有三种延伸用法,大家感兴趣的话,可以自行研究一下。
(3)综合案例——校园一卡通数据的表连接操作
下面我们通过一个综合案例来学习一下SQL的表连接操作。这里使用的数据是关于校园一卡通的流水记录,数据来源于DC竞赛网,此数据集共包含两部分,分别是学生的图书借阅记录(共包含239947 条数据)和消费记录(共包含 条数据)。
- 将数据集导入MySQL中
# 创建学生图书借阅记录表
CREATE TABLE stu_borrow
(stu_id VARCHAR(10),
borrow_date DATE,
book_title VARCHAR(500),
book_number VARCHAR(50) );# 导入图书借阅数据
LOAD DATA local INFILE "/Users/zhucan/Desktop/borrow.csv"
INTO TABLE stu_borrow
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';# 创建学生的一卡通消费表
CREATE TABLE stu_card(
stu_id VARCHAR(10),
custom_class VARCHAR(10),
custom_add VARCHAR(20),
custom_type VARCHAR(20),
custom_date DATETIME,
amt FLOAT,
balance FLOAT );# 导入消费数据
LOAD DATA local INFILE '/Users/zhucan/Desktop/card.txt'
INTO TABLE stu_card111
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
- 简单数据探索
数据全部导入MySQL之后,需要对数据进行探索,了解数据现状:
# 查询某位同学的借书记录
SELECT * FROM stu_borrow
WHERE stu_id = '9708'
ORDER BY borrow_date;
从返回结果来看,图书借阅记录表存在重复记录,根据实际业务情况,这样的数据是不应该出现的,所以后续进行数据处理的时候,需要做去重处理。
# 查询某位学生的消费记录
SELECT * FROM stu_card
WHERE stu_id = '1040'
ORDER BY custom_date;
同样,对于消费记录来说,也存在重复值,后续数据处理需要做去重操作,并且,消费金额还有复数,这里我们理解为充值行为(因为负的消费金额会导致余额的增加)。
- 数据处理
由于两个表中都有重复值,我们首先对数据集进行统计汇总,确保汇总后的结果使得每个学生对应的记录只有一条。对于学生来说,一般以学年为单位进行分析,所以我们选择2014年9月——2015年9月作为统计窗口期。
# 统计2014-9~2015-9学年度每个学生的借书次数以及借阅数量,并将统计结果构成新表
CREATE TABLE borrow_times AS
SELECT stu_id ,COUNT(DISTINCT borrow_date) AS borrow_times ,COUNT(DISTINCT book_title) AS books
FROM stu_borrow
WHERE borrow_date BETWEEN '2014-09-01' AND '2015-08-31'
GROUP BY stu_id;
# 查看统计结果的前5行
SELECT * FROM borrow_times
LIMIT 5;
# 删除stu_card表中重复记录以及消费金额为负的记录,并将清洗结果直接存储到stu_card_distinct表 中
CREATE TABLE stu_card_distinct AS
SELECT DISTINCT * FROM stu_card
WHERE amt>0;
# 如果运行报错:Error Code: 1206. The total number of locks exceeds the lock table size # 说明缓存内存不够(默认为8M),需要设置大一些(比如64M = 64*1024*1024 = 67108864 B)
show variables like "%_buffer_pool_size%";
SET GLOBAL innodb_buffer_pool_size=67108864;
# 统计2014-9~2015-9学年度每个学生的消费总额,最小金额、最大金额和客单价,并将统计结果直接存储到custom表中
CREATE TABLE custom AS
SELECT stu_id ,COUNT(*) AS custom_times ,
SUM(amt) AS custom_amt ,MIN(amt) AS min_amt ,
MAX(amt) AS max_amt ,SUM(amt)/COUNT(*) pct
FROM stu_card_distinct
WHERE custom_date BETWEEN '2014-09-01' AND '2015-08-31'
GROUP BY stu_id;# 查询统计结果的5行信息
SELECT * FROM custom
LIMIT 5;
- 表连接操作
将上面处理好的两张表,整合成一张表。具体选择何种连接方式,需要根据实际业务需求。
# 统计结果的整合
SELECT COUNT(*) FROM custom; # 共计5427条
SELECT COUNT(*) FROM borrow_times; # 共计4158条
# 学生消费表custom作为主表,图书借阅表borrow_times作为辅表(左连接)
SELECT t1.*, t2.borrow_times,t2.books FROM custom AS t1
LEFT JOIN borrow_times AS t2
ON t1.stu_id = t2.stu_id;
# 内连接:提取出两张表中共有的学生记录
SELECT t1.*, t2.borrow_times,t2.books FROM custom AS t1
INNER JOIN borrow_times AS t2
ON t1.stu_id = t2.stu_id;
【补充】LOAD DATA的详细用法
详细解释见MySQL用户手册《refman-8.0-en.a4.pdf》P2449-P2459
MySQL——复杂的多表查询——以超市交易数据为例相关推荐
- MySQL 笔记5 -- 多表查询
MySQL 笔记5 – 多表查询 MySQL 系列笔记是笔者学习.实践MySQL数据库的笔记 课程链接: MySQL 数据库基础入门教程 参考文档: MySQL 官方文档 一.表之间关系 1.一对一 ...
- mysql的联表查询和去重复数据
mysql的联表查询和去重复数据 /* SQLyog Ultimate v10.00 Beta1 MySQL - 5.7.17-log : Database - pusmtnew ********** ...
- 在MySQL中实现交叉表查询2(动态交叉表)
在MySQL中实现交叉表查询2(动态交叉表) 交叉表分为静态交叉表和动态交叉表.其中静态交叉表中的列是固定的,因此相对容易实现:而动态交叉表中的列需要动态生成. 一.静态交叉表的实现 参见上一篇文章: ...
- 在MySQL中实现交叉表查询1(静态交叉表)
在MySQL中实现交叉表查询1(静态交叉表) 一.什么是交叉表 交叉表查询是将来源于某个表中的字段进行分组,一组列在交叉表左侧,一组列在交叉表上部,并在交叉表行与列交叉处显示表中某个字段的各种计算值. ...
- MySQL基础之多表查询
目录 1.多表关系 1.1 一对多 1.2 多对多 1.3 一对一 2.多表查询概述 2.1 数据准备 2.2 概述 2.3 分类 3.内连接 4.外连接 5.自连接 5.1 自连接查询 5.2 联合 ...
- Mysql 索引 与 多表查询性能优化
最近做项目需要用到Luence Whoosh,要定时从数据库中索引出数据来供检索,但是在索引中设计多表查询,速度较慢,因为强迫症,想要做性能优化,因此把Mysql的核心又翻出来研究一遍. 关于MySQ ...
- MySQL数据库应用 多表查询_mysql数据库-多表查询
今日任务 完成对MYSQL数据库的多表查询及建表的操作 教学目标 掌握MYSQL中多表的创建及多表的查询 掌握MYSQL中的表关系分析并能正确建表 昨天内容回顾: 数据库的创建 : create ...
- 基于SpringDataJpa的mysql动态分页多表查询
基于SpringDataJpa的mysql动态分页多表查询 由于这篇文章预计篇幅会很长,关于Spring Data JPA的知识就简短的分享,更多的请自行度娘,JPA 封装了很多查询的接口,但今天要讲 ...
- 记录:数据库(MySQL)之多表查询
数据库(MySQL)之多表查询 1.创建表 -- 多表联合查询 -- 创建三个表,并进行插入数据 -- 创建emp表,并插入数据 CREATE TABLE emp ( eno int(5),-- 员工 ...
最新文章
- 计算机视觉进展二十年 (1995~2015)
- 使用PHP搞定支付宝、微信扫码支付
- Windows 10累积更新发布:RS3正式版前最后一更
- [问答题] 考SQL语句的题,题太长了,实在不好回忆了。
- poj 3278 catch that cow BFS(基础水)
- deeplearning中卷积后尺寸的变化
- 0113——代理模式
- 数百万的 Android 手机预装了危险的恶意软件!
- Home vs2013
- group by having where order by
- 【bzoj2118】 墨墨的等式
- Linux储存结构与磁盘分区详解
- 树莓派Raspberry Pi 4b+实现摄像头拍照和实时监控
- Fibonacci Additions (区间加优化)
- Hazel游戏引擎(005)入口点
- 对数学规划软件 CPLEX 等读取 MPS 文件的理解
- 定义采购申请凭证类型
- 设计模式-备忘录模式(Memento)
- 1.7 线性无关(第1章 线性代数中的线性方程组)
- 【概率论与数理统计】
热门文章
- 单曲循环 翻译_有没有那么一首歌是你的单曲循环?
- string类有可以调换方向的函数吗_C++中的string类的用法小结
- linux 搭建开发stm32 stlink,Ubuntu下搭建stm32+stlink的开发环境
- react ui框架_顶级React组件库推荐
- java exception e抛异常_抛出的异常在上层catch到,但是e.getMessage()为NULL,为什么会这样?...
- 不同表_不同电脑剪视频的速度对比表20200617更新;附素材和方法
- 《基于张量网络的机器学习入门》学习笔记4
- Second Week: Git与Github的使用
- 整合rpc远程调用_远程过程调用(RPC)
- 76. Leetcode 295. 数据流的中位数 (堆-技巧一-固定堆)