HAVING 在 SQL 里可重要啦,它常常跟 GROUP BY 子句一块用,能给聚合函数的结果做过滤。
在 SQL 查询里,GROUP BY 子句能把数据按特定的列分好组。HAVING 子句就在分组完了以后起作用,它能根据聚合函数的结果,把满足特定条件的组挑出来。比如说,在一个卖东西的数据表里头,咱们能靠 GROUP BY 按销售员来分组,然后用 HAVING 子句把总销售额超过一定数的销售员选出来。
HAVING 子 句的作用主要在这么几个地方:头一个,它能对聚合函数算出来的结果输出做限制。好比说,咱们可能就只想看销售总额超过 1500 美元的商店信息,这时候就得用 HAVING 子句。语法格式一般是这样:SELECT "column_name1", SUM ("column_name2") FROM "table_name" GROUP BY "column_name1" HAVING (arithematic function condition)。
HAVING 子句被限制在 SELECT 语句里已经定义好的列和聚合表达式上头。通常,咱们得在 HAVING 子句里再重复一遍聚合函数表达式来引用聚合的值,就跟在 SELECT 语句里做的一样。比如说:SELECT A COUNT (B) FROM TABLE GROUP BY A HAVING COUNT (B)>2。
反正,HAVING 在 SQL 里给咱们提供了一个厉害的工具,能对分组以后的聚合结果做精准的筛选,这样就能满足各种麻烦的查询需要啦。
二、HAVING 与 WHERE 的区别
(一)作用的阶段不一样
WHERE 子句是在数据分组前给原始数据做过滤,就是说它在查询刚开始的时候起作用,用来限制进入分组和聚合操作的数据。比如说,在一个卖东西的数据表里,如果咱们想找销售额大于 1000 的记录,在分组前就能用 WHERE 子句来筛选,像 SELECT product_id, SUM(quantity) AS total_quantity FROM sales WHERE sales_amount > 1000 GROUP BY product_id;,这个查询就只给销售额大于 1000 的记录做分组求和。
可 HAVING 子句是在数据分组以后给分组结果做过滤,就是说它在 GROUP BY 子句后面起作用,用来限制最终结果里的分组。比如 SELECT product_id, SUM(quantity) AS total_quantity FROM sales GROUP BY product_id HAVING SUM(quantity)>100;,这个查询会对所有产品按 product_id 分组求和,然后只给出总数量大于 100 的分组。
(二)使用的限制不一样
WHERE 子句能对单个的记录做条件判断,比如说对列值的具体数字做过滤,但是不能对聚合函数做判断。比如说,咱们不能写 SELECT product_id,SUM(quantity) AS total_quantity FROM sales WHERE SUM(quantity)>100 GROUP BY product_id;,这样的查询会报错,因为 WHERE 不能用聚合函数。
HAVING 子句通常是跟聚合函数(像 SUM (), AVG (), COUNT () 这些)一块用,对分组后的结果做条件判断。比如 SELECT customer_id, SUM(sales_amount) AS total_sales FROM sales GROUP BY customer_id HAVING SUM(sales_amount)>10000;,这个查询会把所有销售总额超过 10000 的客户返回。
反正,WHERE 和 HAVING 在 SQL 查询里都挺重要的,不过因为它们作用的阶段和使用的限制不一样,咱们得按照具体的查询需要来选合适的子句。
三、HAVING 的常见用法
(一)跟聚合函数一块用
HAVING 经常跟聚合函数一块用,发挥出很强的筛选作用。比如说,在一个卖东西的数据表里,咱们想查平均销售额超过 5000 的客户信息。能这么写 SQL 语句:SELECT customer_id, AVG(sales_amount) AS avg_sales FROM sales_data GROUP BY customer_id HAVING AVG(sales_amount)>5000;。在这,靠 AVG 聚合函数算出每个客户的平均销售额,然后用 HAVING 子句把平均销售额大于 5000 的客户挑出来。根据一些实际例子的数据统计,要是销售数据表有 1000 个客户的记录,当中满足平均销售额超过 5000 的客户可能就几十位,HAVING 子句就能准确地找出这些特定的客户信息。
(二)跟 GROUP BY 子句一块用
HAVING 跟 GROUP BY 子句一块用的时候,能对分组后的结果做有针对性的筛选。好比在一个学生成绩表里头,按照班级来分组,然后找出平均分超过 80 分的班级信息。SQL 语句是这样:SELECT_id, AVG(score) AS avg_score FROM student_scores GROUP BY_id HAVING AVG(score)>80;。靠 GROUP BY 子句把学生成绩按班级分好组,再用 HAVING 子句把平均分高于 80 分的班级选出来。在实际的学校成绩管理系统里,这样的查询能帮老师很快搞清楚哪些班级整体成绩比较好,方便做有针对性的教学调整。
(三)跟其他子句联合用
HAVING 能跟 WHERE 子句这些联合起来用,满足更复杂的查询需要。比如说在一个订单表里头,先靠 WHERE 子句把特定时间段里的订单记录选出来,然后再用 GROUP BY 子句按客户分组,最后用 HAVING 子句把总订单金额超过一定数的客户信息选出来。SQL 语句像这样:SELECT customer_id, SUM(order_amount) AS total_amount FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31' GROUP BY customer_id HAVING SUM(order_amount)>10000;。在这,WHERE 子句先把 2023 年的订单记录选出来,然后 GROUP BY 子句按客户分组,最后 HAVING 子句把总订单金额超过 10000 的客户选出来。这种联合使用的办法在实际的业务数据分析里特别常见,能帮咱们从复杂的数据里找出有价值的信息。
四、HAVING 的性能优化
(一)少点数据量
在用 HAVING 子句之前,尽量靠 WHERE 子句把没必要的数据给过滤掉,让处理的数据量变少点。比如说,在一个很大的销售数据表里头,咱们能先使 WHERE sales_date > '2023-01-01' 把特定时间段里的销售记录给筛出来,这么着在做分组和用 HAVING 子句筛选的时候,处理的数据量就能少好多。按照实际的数据统计,要是销售数据表有 100 万条记录,通过合理的 WHERE 子句过滤一下,可能就剩下 10 万条记录用来后面的分组和 HAVING 筛选,这样就能明显把查询性能提高。
(二)用上索引
保证在用来分组的列上弄好索引,把查询性能提上去。索引能让数据检索的速度变快,特别是处理大量数据的时候。好比在一个有客户信息和订单信息的数据库里,如果老是按客户 ID 分组还用上 HAVING 子句筛选,那在客户 ID 这列上弄个索引就能大大提高查询的效率。假设没索引的时候,查询得花 10 秒钟,弄上索引可能 2 秒钟就能把一样的查询做完。
(三)别弄复杂计算
在 HAVING 子句里别用复杂的计算和函数,这些计算可能会对性能有不好的影响。比如说,别在 HAVING 子句里用好几层嵌套的函数调用或者复杂的算术运算。要是非得做复杂计算,可以想想把计算结果存到临时表里,然后在 HAVING 子句里用临时表的列来筛选。这样能把 HAVING 子句执行时候的计算量变少,把查询性能提高。
(四)分析查询的计划
用数据库给的查询计划分析工具,瞅瞅查询的执行计划,找找可能的性能瓶颈。通过分析查询计划,咱们能知道数据库是咋执行查询的,包括用了啥索引、做了啥排序操作这些。比如说,在一个复杂的查询里,咱们可能发现数据库在执行 HAVING 子句的时候做了好多全表扫描,这可能就是性能瓶颈的地方。通过调整查询语句或者优化数据库的结构,能把查询性能变好。假设分析完查询计划,咱们发现能通过加合适的索引或者调整 WHERE 子句的条件来少做点全表扫描,这样就能把查询速度提上去。
五、HAVING 的实际应用案例
(一)找高销售额的客户
在销售数据的分析里,咱们经常得把高销售额的客户找出来。比如说在一个电商平台的销售数据库里,咱们能使下面这个 SQL 语句来找销售总额超过 100000 的客户:SELECT customer_id, SUM(sales_amount) AS total_sales FROM sales_table GROUP BY customer_id HAVING SUM(sales_amount)>100000;。假设这个电商平台有 10000 个客户,靠这个查询,咱们可能能找出几十或者几百个高销售额的客户。这些客户对平台来说是重要的客户群,能针对他们搞个性化的营销活动,把客户的满意度和忠诚度提上去。
(二)找高库存的产品
对企业的库存管理来说,清楚哪些产品库存太高或者太低特别重要。靠 HAVING 子句能很快把库存数量超过特定值的产品筛出来。好比在一个仓库管理系统里,使下面这个 SQL 语句:SELECT product_id, SUM(quantity) AS total_quantity FROM inventory_table GROUP BY product_id HAVING SUM(quantity)>500;。要是仓库里有 1000 种产品,这个查询能帮咱们把那些库存数量超过 500 的产品找出来。对这些高库存产品,企业能考虑搞促销活动或者调整生产计划,把库存成本降下来。
(三)找高绩效的员工
在人力资源管理里,评估员工的绩效是个重要的活儿。用 HAVING 子句能把绩效评分均值超过特定值的员工找出来。比如说在一个员工绩效评估系统里,使下面这个 SQL 语句:SELECT employee_id, AVG(performance_score) AS avg_score FROM performance_table GROUP BY employee_id HAVING AVG(performance_score)>4.5;。假设公司有 1000 名员工,靠这个查询能把那些绩效表现好的员工找出来。对这些高绩效员工,能给奖励或者晋升的机会,激励别的员工把绩效提上去。
(四)找高流量的网页
在网站分析里,清楚哪些网页的访问量高对优化网站的内容和布局特别重要。用 HAVING 子句能把访问次数超过特定值的网页找出来。好比在一个网站流量分析系统里,使下面这个 SQL 语句:SELECT page_url, COUNT(visitor_id) AS total_visits FROM web_traffic_table GROUP BY page_url HAVING COUNT(visitor_id)>1000;。要是网站有 10000 个页面,这个查询能帮咱们把那些访问量高的页面找出来。对这些高流量网页,能进一步分析用户的行为,优化页面的内容和布局,把用户体验提上去。
六、HAVING 的注意事项
(一)跟 GROUP BY 子句的关系
HAVING 子句一定得跟 GROUP BY 子句一块用,不能自己单着。这是因为 HAVING 是用来给分组后的结果做过滤的,而分组这个操作是 GROUP BY 子句干的。比如说,要是咱们只使 HAVING 子句,没有 GROUP BY 子句,数据库就会报错,因为没有分组的结果能让 HAVING 子句来筛选。
(二)支持的东西
HAVING 子句能使聚合函数,像 SUM、COUNT、AVG 这些。这就让咱们能按照分组后的聚合结果来做过滤。比如说,咱们能使 SELECT product_id, SUM(quantity) AS total_quantity FROM inventory_table GROUP BY product_id HAVING SUM(quantity)>500; 来找库存总量超过 500 的产品。这儿的 SUM(quantity) 就是聚合函数,HAVING 子句就照着这个聚合结果来做过滤。 HAVING 子句是用来过滤组的,不是单个的记录。它是在数据分组完了以后才用的,跟 WHERE 子句不一样,WHERE 子句是在数据分组前过滤行的。
(三)对性能的影响
因为 HAVING 子句是在数据分组以后才用的,所以它可能会对查询的性能有影响。特别是处理大量数据的时候,HAVING 子句可能会带来额外的计算和数据处理,让查询的速度变慢。
为了把 HAVING 子句的性能优化一下,咱们能做点事儿。比如说,在用 HAVING 子句之前,尽量靠 WHERE 子句把没必要的数据给过滤掉,让处理的数据量变少。同时,保证在用来分组的列上弄好索引,把查询性能提上去。别在 HAVING 子句里用复杂的计算和函数,也能少点性能上的开销。
(四)跟 WHERE 子句的差别
HAVING 子句跟 WHERE 子句的用处和功能不一样。WHERE 子句是在数据分组前过滤行的,不能用聚合函数。
可 HAVING 子句是在数据分组后过滤组的,能使聚合函数。 在选是用 WHERE 子句还是 HAVING 子句的时候,得按照具体的情况来判断。要是对单个记录做条件判断,或者在分组前过滤,就该用 WHERE 子句。要是对分组后的结果做条件判断,或者得用聚合函数来过滤,就该用 HAVING 子句。
七、HAVING 的高级用法
(一)用多个条件
SQL 的 HAVING 子句能把多个条件合起来,查满足特定要求的分组结果。比如说,下面这个 SQL 语句能找销售额大于 1000 并且订单数大于 10 的销售人员:SELECT salesman_id, SUM(sales_amount) AS total_sales, COUNT(order_id) AS total_orders FROM sales_table GROUP BY salesman_id HAVING SUM(sales_amount)>1000 AND COUNT(order_id)>10;。在这用了 SUM 和 COUNT 两个聚合函数,靠 AND 把两个条件连起来了,给分组后的销售人员做筛选。在实际用的时候,多个条件组合起来能更准地满足复杂的查询需要。假设在一个有 1000 条销售记录的数据库里,可能就只有很少的销售人员能同时满足销售额大于 1000 而且订单数大于 10 这个条件,HAVING 子句的多个条件能帮咱们很快找到这些特定的销售人员。
(二)用子查询
子查询在 HAVING 子句里能起大作用,帮咱们筛出特定的分组结果。比如说,咱们想找平均销售额比所有销售人员平均销售额高的销售人员,就能用子查询来实现。SQL 语句像这样:SELECT salesman_id, AVG(sales_amount) AS avg_sales FROM sales_table GROUP BY salesman_id HAVING AVG(sales_amount)>(SELECT AVG(sales_amount) FROM sales_table);。这儿的子查询(SELECT AVG(sales_amount) FROM sales_table)先算出所有销售人员的平均销售额,然后主查询里的 HAVING 子句把每个销售人员的平均销售额跟这个值比一比,把比平均值高的销售人员筛出来。在实际的数据分析里,子查询能根据不同的需要灵活地定筛选条件,让 HAVING 子句的功能更强。假设在一个有 50 个销售人员的数据库里,靠这个查询能找出那些销售业绩特别好的销售人员,给企业的销售管理提供有价值的信息。
八、HAVING 的使用场景
(一)显示特定地区的信息
在分析数据的时候,经常得只显示面积超过特定值的地区的总人口数和总面积。比如说,SELECT region, SUM(population), SUM(area) FROM test GROUP BY region HAVING SUM(area)>1000000;,这个 SQL 语句从叫 test 的表里,按照地区分组,然后把总面积超过 1000000 的地区挑出来,显示这些地区的名字、总人口数和总面积。假设在一个地理数据库里有 50 个不一样的地区,靠这个查询咱们能很快找出那些面积大的地区,好做进一步的分析和决定。
(二)筛选联合查询的结果
在 union 联合查询里,HAVING 能用来挑出不重复的时间段。比如说,SELECT date FROM (SELECT DISTINCT(date) FROM abc.test1 UNION ALL SELECT DISTINCT(date) FROM abc.test2 ) a GROUP BY date HAVING count(*)<=1;,这个查询从两个不同的表 abc.test1 和 abc.test2 里查字段 date,分组合并以后,把不重复的时间段筛出来。假设这两个表总共有 1000 个不同的日期记录,靠这个查询能找出那些唯一的时间段,给数据分析提供更准的数据基础。 (三)筛选学生成绩 能筛出每个学生成绩在特定值以上的有多少门课。比如说,select sno,count(*) from sc where grade>=90 group by sno;,这个查询从学生成绩表 sc 里,找出成绩大于等于 90 分的课程数量,
按学生编号分组。假设学生成绩表有 1000 个学生的成绩记录,靠这个查询能知道每个学生的优秀课程数量。 能筛出至少两门成绩在特定值以上的学生。比如 select sno,count(*) from sc where grade>=90 group by sno having count(*)>2;,这个查询在上面那个的基础上,再把成绩大于等于 90 分的课程数量至少两门的学生筛出来。
能找出满足特定条件有资格的学生。比如说,select sno,avg(grade) from sc where SNO IN(SELECT SNO FROM SC WHERE GRADE>=95 AND PNO='YW') group by sno having avg(grade)>90;,这个查询先从学生成绩表中找出语文课成绩大于等于 95 分的学生编号,然后再按这些学生编号算平均成绩,把平均成绩大于 90 分的学生筛出来。
(四)比较查询
能查比特定学生平均成绩高的学生学号和平均分数。比如说,select sno,avg(grade) from sc group by sno having avg(grade)>(select avg(grade) from sc where sno=3);,这个查询从学生成绩表中,按学生编号分组算平均成绩,然后把平均成绩比学号为 3 的学生平均成绩高的学生学号和平均分数筛出来。假设学生成绩表有 500 个学生的成绩记录,靠这个查询能找出那些成绩相对好的学生,给教学评估做参考。
(五)获取维度的 topN
用 HAVING in 拿前 N 条维度项。比如说,select area,category,sales sum_index_sales_0 from(select area,category,sales from localdoc_topN t) a group by area,category,sales having area in(select areafrom(select area,sum(sales) sum_index_sales_0 from localdoc_topN group by area order by sum_index_sales_0 desc limit 2) b) limit 5000;,这个查询从叫 localdoc_topN 的表里,先按地区、类别和销售额分组,然后把地区在特定范围里的前 5000 条记录筛出来。假设这个表有 10000 条记录,靠这个查询能很快拿到特定维度的前几条数据。
用 row_number 函数拿每个维度的前 N 条数据。比如说,select area,category,index_sales_0 from(select area,category,sum(sales) index_sales_0,row_number() over(partition by area order by sum(sales) desc) rank_num from(select area,category,sales from localdoc_topN) t group by area,category) a where a.rank_num<=2 limit 5000;,这个查询通过窗口函数 row_number()给每个地区的销售额排名,然后把排名在前两位的记录筛出来,限制返回结果是 5000 条。在实际数据分析里,能按照不同的需要灵活调整排名和限制条件,拿到想要的维度数据。