阅读整理自《MySQL 必知必会》- 朱晓峰,详细内容请登录 极客时间 官网购买专栏。
一个超市项目需求:超市经营者提出,他们需要统计某个门店,每天、每个单品的销售情况,包括销售数量和销售金额等。这里涉及 3 个数据表,具体信息如下所示:
销售明细表(demo.transactiondetails
)
transactionid | itemnumber | quantity | price | salesvalue |
---|---|---|---|---|
1 | 1 | 2 | 89 | 178 |
1 | 2 | 5 | 5 | 25 |
2 | 1 | 3 | 89 | 267 |
2 | 2 | 6 | 5 | 30 |
3 | 1 | 1 | 89 | 89 |
3 | 2 | 10 | 5 | 50 |
销售单头表(demo.transactionhead
)
transactionid | transactionno | cashierid | memberid | operatorid | transdate |
---|---|---|---|---|---|
1 | 0120201201000001 | 1 | 1 | 1 | 2020-12-01 14:25:56 |
2 | 0120201202000001 | 1 | NULL | 2 | 2020-12-02 10:50:50 |
3 | 0120201202000002 | 1 | NULL | 1 | 2020-12-02 12:10:05 |
商品信息表(demo.goodsmaster
)
itemnumber | barcode | goodsname | specification | unit | salesprice |
---|---|---|---|---|---|
1 | 1 | 书 | 16开 | 本 | 89 |
2 | 2 | 笔 | 10支装 | 包 | 5 |
Mysql 数据准备:
demo.transactiondetails
create table demo.transactiondetails
(
transactionid int,
itemnumber int,
quantity decimal(10,3),
price decimal(5,2),
salesvalue decimal(5,2)
);
describe transactiondetails;
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (1, 1, 2, 89, 178);
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (1, 2, 5, 5, 25);
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (2, 1, 3, 89, 267);
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (2, 2, 6, 5, 30);
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (3, 1, 1, 89, 89);
insert into demo.transactiondetails (transactionid, itemnumber, quantity, price, salesvalue) values (3, 2, 10, 5, 50);
mysql> select * from demo.transactiondetails;
+---------------+------------+----------+-------+------------+
| transactionid | itemnumber | quantity | price | salesvalue |
+---------------+------------+----------+-------+------------+
| 1 | 1 | 2.000 | 89.00 | 178.00 |
| 1 | 2 | 5.000 | 5.00 | 25.00 |
| 2 | 1 | 3.000 | 89.00 | 267.00 |
| 2 | 2 | 6.000 | 5.00 | 30.00 |
| 3 | 1 | 1.000 | 89.00 | 89.00 |
| 3 | 2 | 10.000 | 5.00 | 50.00 |
+---------------+------------+----------+-------+------------+
demo.transactionhead
create table demo.transactionhead
(
transactionid int,
transactionno text,
cashierid int,
memberid int,
operatorid int,
transdate datetime
);
describe transactionhead;
insert into demo.transactionhead (transactionid, transactionno, cashierid, memberid, operatorid, transdate) values (1, '0120201201000001', 1, 1, 1, '2020-12-01 14:25:56');
insert into demo.transactionhead (transactionid, transactionno, cashierid, memberid, operatorid, transdate) values (2, '0120201202000001', 1, null, 2, '2020-12-02 10:50:50');
insert into demo.transactionhead (transactionid, transactionno, cashierid, memberid, operatorid, transdate) values (3, '0120201202000002', 1, null, 1, '2020-12-02 12:10:05');
select * from demo.transactionhead;
mysql> select * from demo.transactionhead;
+---------------+------------------+-----------+----------+------------+---------------------+
| transactionid | transactionno | cashierid | memberid | operatorid | transdate |
+---------------+------------------+-----------+----------+------------+---------------------+
| 1 | 0120201201000001 | 1 | 1 | 1 | 2020-12-01 14:25:56 |
| 2 | 0120201202000001 | 1 | NULL | 2 | 2020-12-02 10:50:50 |
| 3 | 0120201202000002 | 1 | NULL | 1 | 2020-12-02 12:10:05 |
+---------------+------------------+-----------+----------+------------+---------------------+
demo.goodsmaster
create table demo.goodsmaster
(
itemnumber int,
barcode int,
goodsname text,
specification text,
unit text,
salesprice decimal(10,3)
);
insert into demo.goodsmaster (itemnumber, barcode, goodsname, specification, unit, saleprice) values (1, 1, 书, 16开, 本, 89.00);
insert into demo.goodsmaster (itemnumber, barcode, goodsname, specification, unit, saleprice) values (2, 2, 笔, 10支装, 支, 5.00);
mysql> select * from demo.goodsmaster;
+------------+---------+-----------+---------------+------+-----------+
| itemnumber | barcode | goodsname | specification | unit | saleprice |
+------------+---------+-----------+---------------+------+-----------+
| 1 | 0001 | 书 | 16开 | 本 | 89.00 |
| 2 | 0002 | 笔 | 10支装 | 支 | 5.00 |
+------------+---------+-----------+---------------+------+-----------+
SUM()函数可以返回指定字段值的和。
可以用它来获得用户某个门店,每天,每种商品的销售总计数据:
mysql> select left(b.transdate, 10), c.goodsname, sum(a.quantity), sum(a.salesvalue)
-> from demo.transactiondetails as a
-> join demo.transactionhead as b on (b.transactionid = a.transactionid)
-> join demo.goodsmaster as c on (c.itemnumber = a.itemnumber)
-> group by left(b.transdate, 10), c.goodsname
-> order by left(b.transdate, 10), c.goodsname;
+-----------------------+-----------+-----------------+-------------------+
| left(b.transdate, 10) | goodsname | sum(a.quantity) | sum(a.salesvalue) |
+-----------------------+-----------+-----------------+-------------------+
| 2020-12-01 | 书 | 2.000 | 178.00 |
| 2020-12-01 | 笔 | 5.000 | 25.00 |
| 2020-12-02 | 书 | 4.000 | 356.00 |
| 2020-12-02 | 笔 | 16.000 | 80.00 |
+-----------------------+-----------+-----------------+-------------------+
left(str,n)
:表示返回字符串 str 最左边的 n 个字符。这里 left(a.transdate,10)
表示返回交易时间字符串最左边的 10 个字符。在 MySQL 中,DATETIME 类型的默认格式是:YYYY-MM-DD,也就是说,年份 4 个字符,之后是“-”,然后是月份 2 个字符,之后又是“-”,然后是日 2 个字符,所以完整的年月日是 10 个字符。用户要求按照日期统计,所以,我们需要从日期时间数据中,把年月日的部分截取出来。
order by
:表示按照指定的字段排序。超市经营者指定按照日期和单品统计,那么,统计的结果按照交易日期和商品名称的顺序排序,会更加清晰。
这个查询是如何执行的,用图表来直观地演示一下各个步骤:
完成 3 个表的连接(省略了一些在这一步不重要的字段):
对结果集按照交易时间和商品名称进行分组,可以分成下面 4 组:
12.1号-商品1
12.1号-商品2
12.2号-商品1
12.2号-商品2
对各组的销售数量和销售金额进行统计,并且按照交易日期和商品名称排序:
+-----------------------+-----------+-----------------+-------------------+
| left(b.transdate, 10) | goodsname | sum(a.quantity) | sum(a.salesvalue) |
+-----------------------+-----------+-----------------+-------------------+
| 2020-12-01 | 书 | 2.000 | 178.00 |
| 2020-12-01 | 笔 | 5.000 | 25.00 |
| 2020-12-02 | 书 | 4.000 | 356.00 |
| 2020-12-02 | 笔 | 16.000 | 80.00 |
+-----------------------+-----------+-----------------+-------------------+
如果用户需要知道全部商品销售的总计数量和总计金额,我们也可以把数据集的整体看作一个分组,进行计算。这样就不需要分组关键字 GROUP BY,以及排序关键字 ORDER BY 了,甚至不需要从关联表中获取数据,也就不需要连接了。就像下面这样:
mysql> select sum(quantity), sum(salesvalue)
-> from demo.transactiondetails;
+---------------+-----------------+
| sum(quantity) | sum(salesvalue) |
+---------------+-----------------+
| 27.000 | 639.00 |
+---------------+-----------------+
求和函数获取的是分组中的合计数据,所以要对分组的结果有准确的把握,否则就很容易搞错。
这也就是说,要知道是按什么字段进行分组的。如果是按多个字段分组,要知道字段之间有什么样的层次关系;如果是按照以字段作为变量的某个函数进行分组的,要知道这个函数的返回值是什么,返回值又是如何影响分组的等。
通过计算分组内指定字段值的和,以及分组内的记录数,算出分组内指定字段的平均值。
举例,用户需要计算每天、每种商品,平均一次卖出多少个、多少钱:
mysql> select left(b.transdate, 10), c.goodsname, avg(a.quantity), avg(a.salesvalue)
-> from demo.transactiondetails as a
-> join demo.transactionhead as b on (b.transactionid = a.transactionid)
-> join demo.goodsmaster as c on (c.itemnumber = a.itemnumber)
-> group by left(b.transdate, 10), c.goodsname
-> order by left(b.transdate, 10), c.goodsname;
+-----------------------+-----------+-----------------+-----------------+
| left(b.transdate, 10) | goodsname | avg(a.quantity) | avg(salesvalue) |
+-----------------------+-----------+-----------------+-----------------+
| 2020-12-01 | 书 | 2.0000000 | 178.000000 |
| 2020-12-01 | 笔 | 5.0000000 | 25.000000 |
| 2020-12-02 | 书 | 2.0000000 | 178.000000 |
| 2020-12-02 | 笔 | 8.0000000 | 40.000000 |
+-----------------------+-----------+-----------------+-----------------+
MAX() 表示获取指定字段在分组中的最大值,MIN() 表示获取指定字段在分组中的最小值。
假如用户要求计算每天里的一次销售的最大数量和最大金额:
mysql> select left(b.transdate, 10), max(a.quantity), max(a.salesvalue)
-> from demo.transactiondetails as a
-> join demo.transactionhead as b on (b.transactionid = a.transactionid)
-> group by left(b.transdate, 10)
-> order by left(b.transdate, 10);
+-----------------------+-----------------+-------------------+
| left(b.transdate, 10) | max(a.quantity) | max(a.salesvalue) |
+-----------------------+-----------------+-------------------+
| 2020-12-01 | 5.000 | 178.00 |
| 2020-12-02 | 10.000 | 267.00 |
+-----------------------+-----------------+-------------------+
千万不要以为 max(a.quantity), max(a.salesvalue)
算出的结果一定是同一条记录的数据。实际上,MySQL 是分别计算的。
max(字段)
这个函数返回分组集中最大的那个值。如果要查询max(字段1)
和max(字段2)
,且它们是相互独立、分别计算的,那么就不要想当然地认为结果在同一条记录上。
要计算记录数,就要用到 COUNT() 函数了。这个函数有两种情况:
如果 count(*)
与 group by
一起使用,就表示统计分组内有多少条数据。它也可以单独使用,这就相当于数据集全体是一个分组,统计全部数据集的记录数。
mysql> select count(*) from demo.transactiondetails;
+----------+
| count(*) |
+----------+
| 6 |
+----------+
超市经营者想知道,每天、每种商品都有几次销售,我们就需要按天、按商品名称,进行分组查询:
mysql> select left(b.transdate, 10), c.goodsname, count(*)
-> from demo.transactiondetails as a
-> join demo.transactionhead as b on (b.transactionid = a.transactionid)
-> join demo.goodsmaster as c on (c.itemnumber = a.itemnumber)
-> group by left(b.transdate, 10), c.goodsname
-> order by left(b.transdate, 10), c.goodsname;
+-----------------------+-----------+----------+
| left(b.transdate, 10) | goodsname | count(*) |
+-----------------------+-----------+----------+
| 2020-12-01 | 书 | 1 |
| 2020-12-01 | 笔 | 1 |
| 2020-12-02 | 书 | 2 |
| 2020-12-02 | 笔 | 2 |
+-----------------------+-----------+----------+
COUNT(字段)用来统计分组内这个字段的值出现了多少次。如果字段值是空,就不统计。
mysql> select * from demo.goodsmaster;
+------------+---------+-----------+---------------+------+-----------+
| itemnumber | barcode | goodsname | specification | unit | saleprice |
+------------+---------+-----------+---------------+------+-----------+
| 1 | 0001 | 书 | 16开 | 本 | 89.00 |
| 2 | 0002 | 笔 | 10支装 | 支 | 5.00 |
| 3 | 003 | 纸 | NULL | 张 | 0.10 |
+------------+---------+-----------+---------------+------+-----------+
3 rows in set (0.01 sec)
mysql> select count(goodsname) from demo.goodsmaster;
+------------------+
| count(goodsname) |
+------------------+
| 3 |
+------------------+
1 row in set (0.00 sec)
mysql> select count(specification) from demo.goodsmaster;
+----------------------+
| count(specification) |
+----------------------+
| 2 |
+----------------------+
1 row in set (0.00 sec)
文章浏览阅读1.1k次,点赞2次,收藏6次。本地的代码修改后,想要放弃本地修改,然后拉取代码。1. 未使用git add 缓存代码使用git checkout -- filename,注意中间有--git checkout -- filename放弃所有文件修改 git checkout .git checkout .此命令用来放弃掉所有还没有加入到缓存区(就是 git add 命令)的修改:内容修改与整个文件删除 此命令不会删除新建的文件,因为新建的文件还没加入git管理系统中,所以对git来说是未知,只需..
文章浏览阅读530次。1 A+Bimport java.util.Scanner;/** * Created by m1786 on 2017/3/3. */public class Main{ public static void main(String args[]){ int a,b; Scanner sc=new Scanner(Sy
文章浏览阅读807次。机器学习笔试题
文章浏览阅读958次。error C4430: missing type specifier - int assumed. Note: C++ does not ...(转) 2011-07-06 23:37:58| 分类:编程 |举报|字号 订阅AdapterBase.cppWINVER not defined. Defaulting to 0x0502 (Wi
文章浏览阅读9.2k次,点赞11次,收藏56次。wx.chooseImage({ // count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { // 返回选定照片的本地文件路径列表,tempFilePath可以_扫码上传图片到指定服务器
文章浏览阅读3.3k次。车祸现场一type Exception reportmessage Servlet.init() for servlet DispatcherServlet threw exceptiondescription The server encountered an internal error that prevented it from fulfilling this request....
文章浏览阅读2.7k次,点赞2次,收藏17次。目 录:1. 小程序自动化方案 1.1 小程序的基本构成 1.2 原生组件定位之UiSelector 1.2.1 原生组件定位之UiSelector 1.2.2 web元素定位 1.3 小程序自动化准备 1.3.1 安装appium 1.3.2 安装adb tools 1.3.3 chromedriver.exe版本 1.3.4..._百度小程序自动化测试
文章浏览阅读1k次。问题说明为了在同一个图片中生成8个小图,使用同一个mat文件,批量绘图以及命名。matlab代码%% 计算折合转速-3种% load('RESULT2.mat', 'RESULT2');%载入结果文件load('RESULT2.mat');% result=zeros(144,4);result=result2;%result=RESULT2;n=[12630 11367 10104];%三种原始..._matlab批量绘图
文章浏览阅读445次。绝代庄园妖娆一梦 英格兰旅游攻略http://www.sh51766.com/shows/show_2435.htmlhartwell-house 庄园酒店http://www.hartwell-house.com/英国旅游住宿推荐英国旅游住宿推荐 — 酒店价格:双人房的价格从50英镑到400英镑不等,含早餐。很多大型酒店在周末和淡季..._庄园式酒店
文章浏览阅读1.5k次。今天连接了一下oracle数据库,遇到了几个问题:1.出现如下的错误java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:111) at oracle.jdbc.driver.DatabaseError.throwSqlException
文章浏览阅读7.4k次。转自:http://blog.chinaunix.net/uid-29506893-id-5761774.htmlANR文件提取的有用片段如下:----- pid 13431 at 2016-09-14 11:46:10 -----Cmd line: com.android.settingsat java.lang.Object.wait(Native Me
文章浏览阅读693次。常见手机屏幕分辨率128x128、128x160、240x320(常见2.0、2.2、2.4、2.8寸屏手机使用)以前的功能机时代就是这么多320x240(常见2.4寸屏手机使用)240x400(常见3.0寸屏手机使用)320x480(主流屏常见3.2、3.5寸屏手机)360x640(诺基亚常见)480x800(常见4.0寸屏使用)480x854(常见3.7寸屏使用)960x540(..._屏幕分辨率: 360x800