MySQL第六讲·where和having的异同?

news2024/11/25 3:00:54

在这里插入图片描述

你好,我是安然无虞。

文章目录

  • 面试常考:where与having有什么不同?
    • 一个实际查询需求
    • where
    • having
    • 怎么正确的使用where和having?

面试常考:where与having有什么不同?

我们在进行查询的时候,经常需要按照条件对查询结果进行筛选,这就要用到条件语句where和having了。

where是直接对表中的字段进行限定来筛选结果,having则需要跟分组关键字group by一起使用,通过对分组字段或分组计算函数进行限定来筛选结果。虽然它们都是对查询进行限定,却有着各自的特点和适用场景。很多时候,我们会遇到2个都可以用的情况。一旦用错,就容易出现执行效率低下、查询结果错误,甚至是查询无法运行的情况。

一个实际查询需求

超市经营者提出,要查单笔销售金额超过50元的商品。

我们来分析一下这个需求:需要查询出一个商品记录集,限定条件是单笔销售超过50元。这个时候,我们就需要用到where 和 having了。

这个问题的条件很明确,查询的结果也只有“商品”一个字段,看起来挺容易实现的。

假设我们有一个商品信息表demo.goodsmaster,里面有2种商品:书和笔。

mysql> select *
    -> from demo.goodsmaster;
+------------+---------+-----------+---------------+------+------------+
| itemnumber | barcode | goodsname | specification | unit | salesprice |
+------------+---------+-----------+---------------+------+------------+
|          1 | 0001    ||               ||      89.00 |
|          2 | 0002    ||               ||       5.00 |
+------------+---------+-----------+---------------+------+------------+
2 rows in set (0.00 sec)

同时我们还有一个商品销售明细表demo.transactiondetails,里面有4条销售记录:

mysql> select *
    -> from demo.transactiondetails;
+---------------+------------+----------+-------+------------+
| transactionid | itemnumber | quantity | price | salesvalue |
+---------------+------------+----------+-------+------------+
|             1 |          1 |    1.000 | 89.00 |      89.00 |
|             1 |          2 |    2.000 |  5.00 |      10.00 |
|             2 |          1 |    2.000 | 89.00 |     178.00 |
|             3 |          2 |   10.000 |  5.00 |      50.00 |
+---------------+------------+----------+-------+------------+
4 rows in set (0.01 sec)

接下来我们分别用where和having尽心查询,看看它们各自是如何查询的,是否能够得到正确的结果。

首先用where关键字进行查询:

mysql> select distinct b.goodsname
-> from demo.transactiondetails as a
-> join demo.goodsmaster as b
-> on (a.itemnumber=b.itemnumber)
-> where a.salesvalue > 50;
+-----------+
| goodsname |
+-----------+
||
+-----------+
1 row in set (0.00 sec)

接着,使用having 关键字进行查询:

mysql> select b.goodsname
-> from demo.transactiondetails as a
-> join demo.goodsmaster as b
-> on (a.itemnumber = b.itemnumber)
-> group by b.goodsname
-> having max(a.salesvalue) > 50;
+-----------+
| goodsname |
+-----------+
||
+-----------+
1 row in set (0.00 sec)

可以看到两次查询的结果是一样的,那么这两种查询到底有什么区别,那个更好呢?要明白这个问题,首先我们需要先学习where和having的执行过程。

where

我们先来分析一下刚才使用 where 条件的查询语句,来看看 MySQL 是如何执行这个查询的。

首先,MySQL 从数据表 demo.transactiondetails 中抽取满足条件“a.salesvalue>50”的记录:

mysql> select *
    -> from demo.transactiondetails as a
    -> where a.salesvalue > 50;
+---------------+------------+----------+-------+------------+
| transactionid | itemnumber | quantity | price | salesvalue |
+---------------+------------+----------+-------+------------+
|             1 |          1 |    1.000 | 89.00 |      89.00 |
|             2 |          1 |    2.000 | 89.00 |     178.00 |
+---------------+------------+----------+-------+------------+
2 rows in set (0.00 sec)

为了获取到销售信息所对应的商品名称,我们需要通过公共字段“itemnumber”与数据表 demo.goodsmaster 进行关联,从 demo.goodsmaster 中获取商品名称:

mysql> select
    ->     a.*, b.goodsname
    -> from
    ->     demo.transactiondetails a
    -> join
    ->     demo.goodsmaster b 
    -> on (a.itemnumber = b.itemnumber)
    -> where
    ->     a.salesvalue > 50;
+---------------+------------+----------+-------+------------+-----------+
| transactionid | itemnumber | quantity | price | salesvalue | goodsname |
+---------------+------------+----------+-------+------------+-----------+
|             1 |          1 |    1.000 | 89.00 |      89.00 ||
|             2 |          1 |    2.000 | 89.00 |     178.00 ||
+---------------+------------+----------+-------+------------+-----------+
2 rows in set (0.00 sec)

这个时候,如果查询商品名称,就会出现两个重复的记录:

mysql> select
    ->     b.goodsname
    -> from
    ->     demo.transactiondetails as a
    -> join
    ->     demo.goodsmaster as b on (a.itemnumber = b.itemnumber)
    -> where
    ->     a.salesvalue > 50;
+-----------+
| goodsname |
+-----------+
||
||
+-----------+
2 rows in set (0.00 sec)

需要注意的是,为了消除重复的语句,这里我们需要用到一个关键字:DISTINCT,它的作用是返回唯一不同的值。比如,DISTINCT 字段 1,就表示返回所有字段 1 的不同的值。

下面我们尝试一下加上 DISTINCT 关键字的查询:

mysql> select
    ->     distinct(b.goodsname)  -- 返回唯一不同的值
    -> from
    ->     demo.transactiondetails as a
    ->         join
    ->     demo.goodsmaster as b on (a.itemnumber = b.itemnumber)
    -> where
    ->     a.salesvalue > 50;
+-----------+
| goodsname |
+-----------+
||
+-----------+
1 row in set (0.00 sec)

这样,我们就得到了需要的结果:单笔销售金额超过 50 元的商品就是“书”。

总之,WHERE 关键字的特点是,直接用表的字段对数据集进行筛选。如果需要通过关联查询从其他的表获取需要的信息,那么执行的时候,也是先通过 WHERE 条件进行筛选,用筛选后的比较小的数据集进行连接。这样一来,连接过程中占用的资源比较少,执行效率也比较高。

having

讲完了where,我们再说说having 是如何执行的。不过,在这之前,我们先来了解一下group by,因为having不能单独使用,必须要跟group by一起使用。

我们可以把group by 理解成对数据进行分组,方便我们对组内的数据进行统计计算。

举个小例子,来具体讲讲group by 如何使用,以及如何在分组里面进行统计计算。

假设现在有一组销售数据,我们需要从里面查询每天、每个收银员的销售数量和销售金额。我们通过以下的代码,来查看一下数据的内容:

mysql> select *
    -> from demo.transactionhead;
+---------------+------------------+------------+---------------------+
| transactionid | transactionno    | operatorid | transdate           |
+---------------+------------------+------------+---------------------+
|             1 | 0120201201000001 |          1 | 2020-12-10 00:00:00 |
|             2 | 0120201202000001 |          2 | 2020-12-11 00:00:00 |
|             3 | 0120201202000002 |          2 | 2020-12-12 00:00:00 |
+---------------+------------------+------------+---------------------+
3 rows in set (0.00 sec)

mysql> select *
    -> from demo.transactiondetails;
+---------------+------------+----------+-------+------------+
| transactionid | itemnumber | quantity | price | salesvalue |
+---------------+------------+----------+-------+------------+
|             1 |          1 |    1.000 | 89.00 |      89.00 |
|             1 |          2 |    2.000 |  5.00 |      10.00 |
|             2 |          1 |    2.000 | 89.00 |     178.00 |
|             3 |          2 |   10.000 |  5.00 |      50.00 |
+---------------+------------+----------+-------+------------+
4 rows in set (0.01 sec)

mysql> select *
    -> from demo.operator;
+------------+----------+--------+--------------+-------------+---------+--------------------+--------+
| operatorid | branchid | workno | operatorname | phone       | address | pid                | duty   |
+------------+----------+--------+--------------+-------------+---------+--------------------+--------+
|          1 |        1 | 001    | 张静         | 18612345678 | 北京    | 110392197501012332 | 店长   |
|          2 |        1 | 002    | 李强         | 13312345678 | 北京    | 110222199501012332 | 收银员 |
+------------+----------+--------+--------------+-------------+---------+--------------------+--------+
2 rows in set (0.01 sec)

mysql> select
    -> a.transdate,   -- 交易时间
    -> c.operatorname,-- 操作员
    -> d.goodsname,   -- 商品名称
    -> b.quantity,    -- 销售数量
    -> b.price,       -- 价格
    -> b.salesvalue   -- 销售金额
    -> from
    ->   demo.transactionhead as a
    -> join
    ->   demo.transactiondetails as b on (a.transactionid = b.transactionid)
    -> join
    ->  demo.operator as c on (a.operatorid = c.operatorid)
    -> join
    ->  demo.goodsmaster as d on (b.itemnumber = d.itemnumber);
+---------------------+--------------+-----------+----------+-------+------------+
| transdate           | operatorname | goodsname | quantity | price | salesvalue |
+---------------------+--------------+-----------+----------+-------+------------+
| 2020-12-10 00:00:00 | 张静         ||    1.000 | 89.00 |      89.00 |
| 2020-12-10 00:00:00 | 张静         ||    2.000 |  5.00 |      10.00 |
| 2020-12-11 00:00:00 | 李强         ||    2.000 | 89.00 |     178.00 |
| 2020-12-12 00:00:00 | 李强         ||   10.000 |  5.00 |      50.00 |
+---------------------+--------------+-----------+----------+-------+------------+
4 rows in set (0.00 sec)

如果我想看看每天的销售数量和销售金额,可以按照一个字段transdate对数据进行分组和统计。

mysql> select
    -> a.transdate,
    -> sum(b.quantity), -- 统计分组的总计销售数量
    -> sum(b.salesvalue) -- 统计分组的总计销售金额
    -> from
    ->   demo.transactionhead as a
    -> join
    -> demo.transactiondetails as b on (a.transactionid = b.transactionid)
    -> group by a.transdate;
+---------------------+-----------------+-------------------+
| transdate           | SUM(b.quantity) | SUM(b.salesvalue) |
+---------------------+-----------------+-------------------+
| 2020-12-10 00:00:00 |           3.000 |             99.00 |
| 2020-12-11 00:00:00 |           2.000 |            178.00 |
| 2020-12-12 00:00:00 |          10.000 |             50.00 |
+---------------------+-----------------+-------------------+
3 rows in set (0.00 sec)

如果想看看每天、每个收银员的销售数量和销售金额,可以按照2个字段进行分组和统计,分别是transdate和operatorname:

mysql>  select
    ->     a.transdate,
    ->     c.operatorname,
    ->     sum(b.quantity), -- 数量求和
    ->     sum(b.salesvalue)-- 金额求和
    -> from
    ->     demo.transactionhead as a
    -> join
    ->     demo.transactiondetails as b on (a.transactionid = b.transactionid)
    -> join
    ->     demo.operator as C on (a.operatorid = c.operatorid)
    -> group by a.transdate , c.operatorname; -- 按照交易日期和操作员分组
+---------------------+--------------+-----------------+-------------------+
| transdate           | operatorname | SUM(b.quantity) | SUM(b.salesvalue) |
+---------------------+--------------+-----------------+-------------------+
| 2020-12-10 00:00:00 | 张静         |           3.000 |             99.00 |
| 2020-12-11 00:00:00 | 李强         |           2.000 |            178.00 |
| 2020-12-12 00:00:00 | 李强         |          10.000 |             50.00 |
+---------------------+--------------+-----------------+-------------------+
3 rows in set (0.00 sec)

可以看到,通过对销售数据按照交易日期和收银员进行分组,再对组内数据进行求和统计,就实现了对每天、每个收银员的销售数量和销售金额的查询。

好了,知道了group by 的使用方法,我们就来学习having。

回到开头的超市经营者的需求:查询单笔销售金额超过 50 元的商品。现在我们来使用 having 来实现,代码如下:

mysql> select b.goodsname
    -> from demo.transactiondetails as a
    -> join demo.goodsmaster as b
    -> on (a.itemnumber=b.itemnumber)
    -> group by b.goodsname
    -> having max(a.salesvalue) > 50;
+-----------+
| goodsname |
+-----------+
||
+-----------+
1 row in set (0.00 sec)

这种查询方式在MySQL里面分四步实现。

第一步,把流水明细表和商品信息表通过公共字段itemnumber连接起来,从两个表中获取数据:

mysql> select
    ->     a.*, b.*
    -> from
    ->     demo.transactiondetails a
    ->         join
    ->     demo.goodsmaster b on (a.itemnumber = b.itemnumber);
+---------------+------------+----------+-------+------------+------------+---------+-----------+---------------+------+------------+
| transactionid | itemnumber | quantity | price | salesvalue | itemnumber | barcode | goodsname | specification | unit | salesprice |
+---------------+------------+----------+-------+------------+------------+---------+-----------+---------------+------+------------+
|             1 |          1 |    1.000 | 89.00 |      89.00 |          1 | 0001    || NULL          ||      89.00 |
|             1 |          2 |    2.000 |  5.00 |      10.00 |          2 | 0002    || NULL          ||       5.00 |
|             2 |          1 |    2.000 | 89.00 |     178.00 |          1 | 0001    || NULL          ||      89.00 |
|             3 |          2 |   10.000 |  5.00 |      50.00 |          2 | 0002    || NULL          ||       5.00 |
+---------------+------------+----------+-------+------------+------------+---------+-----------+---------------+------+------------+
4 rows in set (0.00 sec)

查询的结果有点复杂,为了方便理解,对结果进行了分类,并加了注释,如下图所示:

img

第二步,把结果集按照商品名称进行分组,分组的示意图如下所示:

组一:

img

组二:

img

第三步,对分组后的数据集进行筛选,把组中字段salesvalue的最大值 >50 的组筛选出来,结果如下:

img

第四步,返回商品名称。这是我们就得到了结果:单笔销售金额超过50元的商品。

总结使用having的查询过程:

首先我们需要把所有的信息都准备好,包括从关联表中获取的信息,对数据集进行分组,形成一个包含所有需要的信息的数据集合。接着,再通过having 条件筛选,得到需要的数据。

怎么正确的使用where和having?

首先我们需要知道它们的2个典型区别:

第一个区别是,如果需要通过连接从关联表中获取需要的数据,where是先筛选后连接,而having是先连接后筛选。

这一点就决定了在关联查询中,where比having更高效。因为where可以先筛选,用一个筛选后的较小的数据集和关联表进行连接,这样占用的资源比较少,执行效率也就比较高。having则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也比较低。

第二个区别是,where可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;having必须要与group by配合使用,可以把分组计算的函数和分组字段作为筛选条件。

这也就决定了,在需要对数据进行分组统计的时候,having可以完成where不能完成的任务。这是因为,在查询语法结构中,where在group by 之前,所以无法对分组结果进行筛选。having在group by之后,可以使用分组字段和分组中的计算函数对分组的结果集进行筛选,这个功能是where无法完成的。

举个例子,假如超市经营者提出,要查询是哪个收银员、在哪天卖了2单商品。这种必须先分组才能筛选的查询,用where语句实现就比较难,我们可能要分好几步,通过把中间结果存储起来,才能搞定,但是用having,就很轻松,如下:

mysql> select
    ->   a.transdate, c.operatorname
    -> from
    ->   demo.transactionhead as a
    -> join
    ->   demo.transactiondetails as b on (a.transactionid = b.transactionid)
    -> join
    ->   demo.operator as c on (a.operatorid = c.operatorid)
    -> group by a.transdate, c.operatorname
    -> having count(*)=2;  -- 销售了2单
+---------------------+--------------+
| transdate           | operatorname |
+---------------------+--------------+
| 2020-12-10 00:00:00 | 张静         |
+---------------------+--------------+
1 row in set (0.01 sec)

where和having的优缺点:

img

当然了,where和having也可以一起配合使用,包含分组统计函数的条件用having,普通条件用where。这样,我们就既利用了where条件的高效快速,又发挥了having可以使用包含分组统计函数的查询条件的优点,当数据量特别大的时候,运行效率会有很大的差别。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言运行代码示例

这是一个基本的 C 爬虫程序&#xff0c;使用了 C11 版本。这个程序使用了 C11 的标准库&#xff0c;包括了网络编程库&#xff08;<net/http>&#xff09;&#xff0c;字符串处理库&#xff08;<string>&#xff09;和文件操作库&#xff08;<fstream>&#…

Centos7下通过docker安装Rancher2.7搭建Kubernetes

Rancher官方网站&#xff08;中文&#xff09; Rancher单节点 Rancher2.7与Kubernetes部署在同一台设备上 关闭防火墙与selinux #关闭防火墙 systemctl stop firewalld && systemctl disable firewalld && iptables -F #关闭selinux sed -i s/enforcing/di…

聚观早报 |小鹏P7i 550版上市;零一万物发布大模型

【聚观365】11月7日消息 小鹏P7i 550版上市 零一万物发布大模型 vivo X100现身Geekbench 小马智行与丰田联合发布Robotaxi 王云鹏出任百度IDG负责人 小鹏P7i 550版上市 小鹏P7i 550版正式上市&#xff0c;新车共推出550 Pro、550 Max 两款新版型&#xff0c;售价分别为22…

Photoshop Web版本用了哪些CSS技术

本文翻译自 CSS Findings From Photoshop Web Version &#xff0c;作者&#xff1a;Ahmad&#xff0c; 略有删改。 几周前&#xff0c;Adobe发布了一个Web版的Photoshop&#xff0c;它是用WebAssembly、Web组件、P3颜色等网络技术构建的。 Photoshop是我14岁时学会的第一个专…

【I2C】熟悉I2C的传输时序。根据I2C的时序图,标出每段时序对应的含义

参考&#xff1a;I2C -- I2C总线详解_i2c频率-CSDN博客 首先看下I2C协议中数据传输的时序&#xff1a; I2C空闲&#xff1a;SCL和SDA同时处于“高电平”。 I2C起始&#xff1a;SCL维持高电平&#xff0c;SDA由“高电平跳变为低电平”&#xff0c;下降沿。 I2C终止&#xff1a…

机组 硬件

典型的冯诺伊曼计算机是以运算器为中心 现代的计算机已转化为以存储器为中心 运算器&#xff1a;完成算术运算和逻辑运算&#xff0c;并将运算的中间结果暂存在运算器内存储器&#xff1a;存放数据和程序控制器&#xff1a;控制、指挥程序和数据的输入、运行以及处理运算结果输…

【LeetCode】每日一题 2023_11_7 统计范围内的元音字符串数

文章目录 刷题前唠嗑题目&#xff1a;最大单词长度乘积题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;最大单词长度乘积 题目链接&#xff1a;2586. 统计范围内的元音字符串数 题目描述 代码与…

上海亚商投顾:创业板指放量涨3.26% 两市近4500股飘红

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日高开后展开震荡&#xff0c;深成指涨超2%&#xff0c;创业板指大涨超3%&#xff0c;宁德时代涨超5%。…

世界土壤数据库(HWSD)土壤数据集

简介&#xff1a; HWSD&#xff08;Harmonized World Soil Database&#xff09;是联合国粮食及农业组织&#xff08;FAO&#xff09;和国际土壤参考与信息中心&#xff08;ISRIC&#xff09;共同开发的世界土壤数据集。该数据集是一份高分辨率&#xff08;1 km&#xff09;&a…

Gin框架如何使用并搭建一个后台管理系统(四)

到这个时候&#xff0c;我们应该安装上Mysql 开始工作。首先下载安装包mysql-installer-community-8.0.20.0.msi ps&#xff1a;看到有的人window环境下安装不成功可以下载一下依赖的包 NDP452-KB2901907-x86-x64-AllOS-ENU.exe 一、安装mysql 环境和可视化工具 二、导入…

linux修改rocketmq的日志文件位置

文章目录 &#x1f50a;修改rocketmq的日志文件位置&#x1f4d5;原来的文件&#x1f4cc;修改后文件&#x1f4c7;rocketmq中的Rocketmq_client.log文件在配置文件中改不了 需要在代码logback文件中进行修改&#x1f58a;️最后总结 &#x1f50a;修改rocketmq的日志文件位置 …

面试官:Spring 用到了哪些设计模式?

文章目录 前言一、策略模式二、工厂方法模式三、Builder模式总结 前言 关于设计模式&#xff0c;如果使用得当&#xff0c;将会使我们的代码更加简洁&#xff0c;并且更具扩展性。本文主要讲解Spring中如何使用策略模式&#xff0c;工厂方法模式以及Builder模式。 一、策略模式…

数字滤波器之高通滤波器设计

文章来源地址&#xff1a;https://www.yii666.com/blog/393376.html 通过在Z平面放置零极点的来设计数字滤波器 要求&#xff1a;设计一款高通滤波器&#xff0c;用在音频信号处理过程中&#xff0c;滤掉100Hz以下的信号。 实现方法&#xff1a;通过在Z平面放置零极点的来设…

多测师肖sir_高级金牌讲师__git讲解

git 一、git的介绍 &#xff08;一&#xff09;git的理论介绍&#xff08;版本控制工具&#xff09; git &#xff0c;目前世界上最先级的分布式版本控制系统&#xff0c;可以有效&#xff0c;高速的处理从很小到非常大的项目版本管理。 git是linus torvalds 为了帮助管理linu…

什么是柴油发电机组负载测试

柴油发电机组负载测试是对柴油发电机组在不同负载条件下进行性能和稳定性测试的过程&#xff0c;负载测试旨在评估发电机组在实际运行中的工作能力和性能表现&#xff0c;以确保其能够在负载变化时稳定可靠地提供电力。在负载测试中&#xff0c;需要确定测试负载的大小和类型。…

idea使用gradle教程 (idea gradle springboot)2024

这里白眉大叔&#xff0c;写一下我工作时候idea怎么使用gradle的实战步骤吧 ----windows 环境----------- 1-本机安装gradle 环境 &#xff08;1&#xff09;下载gradle Gradle需要JDK的支持&#xff0c;安装Gradle之前需要提前安装JDK8及以上版本 https://downloads.gra…

MES生产执行系统源码

MES生产执行系统源码 MES系统介绍 MES系统着重解决生产过程管控、防错防呆、产质量追溯、设备运行等相关管理目标&#xff0c;具体如下&#xff1a; &#xff08;1&#xff09;全面集成。承上启下&#xff0c;完成公司所有与MES系统链接的信息化系统 &#xff08;如ERP、PLM…

BSP-STM32移植FreeRTOS

在stm32裸机工程中的Middlewares目录添加freeRtos源码 在裸机工程中的main中调用freertos接口

最新版Office2024安装教程

一. 介绍&#xff1a;Office版本都是每三年发布一个版本&#xff0c;从Office 2007、2010、2013、2016、2019&#xff0c;2021到现在的2024。 二. 下载&#xff1a; http://dt1.8tupian.net/2/29913a54b1000.pg3三. 安装教程&#xff1a; 1.用到的软件是开源的脚本&#xff0c…