送你以下java学习资料。文末有一个认识HAVING的方法。SQL中的HAVING相信大家都不陌生。它通常与GROUPBY结合使用,以指定聚合操作的条件。说到指定条件,我们首先想到的往往是WHERE子句,但是WHERE子句只能指定行的条件,不能指定组的条件,所以就有了HAVING子句,用来指定群的状况。让我们看一个具体的例子来说明。我们有一张学生班级表(tbl\_student\_class),数据如下:DROPTABLEIFEXISTStbl_student_class;CREATETABLEtbl_student_class(idint(8)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增主键',snovarchar(12)NOTNULLCOMMENT'学号',cnovarchar(5)NOTNULLCOMMENT'班级号',cnamevarchar(50)NOTNULLCOMMENT'班级名',PRIMARYKEY(id))ENGINE=InnoDBDEFAULTCHARSET=utf8COMMENT='学生班级表';------------------------------tbl_student_class的记录--------------------------INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190607001','0607','影视7班');INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190607002','0607','影视7班');INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190608003','0608','影视8班');INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190608004','0608','影视8班');INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190609005','0609','影视9班');插入tbl_student_class(sno,cno,cname)VALUES('20190609006','0609','影视9班');INSERTINTOtbl_student_class(sno,cno,cname)VALUES('20190609007','0609','影视9班');我们要查询班级有3个学生,需要用到HAVING,相信大家都会这样写SELECTcno,COUNT(*)numsFROMtbl_student_classGROUPBYcnoHAVINGCOUNT(*)=3;如果我们不使用HAVING,它会是什么样子?可以看到,除了数量等于3的类之前,其他的类也被检测到了。我们可以简单总结一下:WHERE先过滤行,然后GROUPBY对行进行分组,HAVING再过滤分组,过滤出我们需要的HAVING子句的组成部分。HAVING操作的对象是一个组,所以对可以使用的元素有一定的限制。可以使用三种类型的元素:常量、聚合函数和聚合键。聚合键是GROUPBY子句中指定的列名。HAVINGCOUNT(*)=3,COUNT(*)是一个聚合函数,3是一个常数,都在3个元素之中;如果有3个元素以外的条件,会发生什么?SELECTcno,COUNT(*)numsFROMtbl_student_classGROUPBYcnoHAVINGcname='Movie9class';执行上面的SQL会失败,提示:[Err]1054-Unknowncolumn'cname'in'havingclause'使用HAVING子句时,聚合GROUPBY作为HAVING的起点会更容易理解结果条款;例子中通过cno聚合后的结果如下:聚合后的结果没有cname列,当然通过该列进行条件处理会报错。小伙伴们应该已经发现,当包含GROUPBY子句时,HAVING子句的组成部分和SELECT子句的组成部分是一样的,只能包含常量、聚合函数和聚合键。HAVINGHAVING子句的魅力是SQL中一个非常重要的特性,是理解SQL面向集合本质的关键下面就让我们结合具体的案例来感受一下HAVING有无缺号的魅力吧。tbl\_student\_class表中记录的id是连续的(id初值不一定为1),我们去掉其中3个DELETEFROMtbl_student_classWHEREidIN(2,5,6);从tbl_student_class选择*;如何判断是否缺号?数据量小,我们一眼就能看出来,但是如果数据量是百万行,肉眼是看不出来的。别绕圈子了,我直接写,相信大家都能看懂(记得和自己思考比较)SELECT'有一个缺失的数字'ASgapFROMtbl_student_classHAVINGCOUNT(*)<>MAX(id)-MIN(id)+1;上面的SQL语句中没有GROUPBY子句,整个表都会聚合成一个组。这种情况下也可以使用HAVING子句(HAVING不一定要和GROUPBY一起使用)写的更严谨一些,如下(没有HAVING,不是主角,随便看看)——反正有返回结果SELECTCASEWHENCOUNT(*)=0THEN'Thetableisempty'WHENCOUNT(*)<>MAX(id)-MIN(id)+1THEN'Thereisamissingnumber'ELSE'Consecutive'ENDAS来自tbl_student_class的差距;那怎么找漏号,请在评论区留言求模式假设我们有一个表:tbl\_student\_salary,里面记录了毕业生第一份工作的年薪DROPTABLEIFEXISTStbl_student_salary;CREATETABLEtbl_student_salary(idint(8)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增主键',namevarchar(5)NOTNULLCOMMENT'姓名',salaryDECIMAL(15,2)NOTNULLCOMMENT'年薪,单位元',YPRIMARYKE(id))ENGINE=InnoDBDEFAULTCHARSET=utf8COMMENT='毕业生年薪大关';insertintotbl_student_salaryvalues(1,'李小龙',1000000);insertintotbl_student_salaryvalues(2,'李四',50000);insertintotbl_student_salaryvalues(3,'王五',50000);insertintotbl_student_salaryvalues(4,'赵六',50000);insertintotbl_student_salaryvalues(5,'张三',70000);insertintotbl_student_salaryvalues(6,'张三三',70000);insertintotbl_student_salaryvalues(7,'张二三',70000);insertintotbl_student_salaryvalues(8,'张三三',60000);insertintotbl_student_salaryvalues(9,'张三四',40000);insertintotbl_student_salaryvalues(10,'张三丰',30000);平均工资达到了14.9万元。乍一看,似乎大多数毕业生都能拿到很高的薪水。不过,这个数字背后却有些玄机,因为功夫大师李小龙在这批毕业生中,由于薪资突出,大家的平均薪资都被大幅度提高。单纯计算平均值有个缺点,就是容易受到离群值的影响。在这种情况下,就需要使用一种能够更准确地反映群体趋势的指标——众数就是其中之一。那么如何使用SQL语句查找模式,我们往下看——使用谓词ALL查找模式SELECTsalary,COUNT(*)AScntFROMtbl_student_salaryGROUPBYsalaryHAVINGCOUNT(*)>=ALL(SELECTCOUNT(*)FROMtbl_student_salary按工资分组);结果如下。ALL谓词在用于NULL或空集时会导致问题。取而代之的是价值函数;这里需要的是元素个数最多的集合,所以可以用MAX函数——用极值函数求多数SELECTsalary,COUNT(*)AScntFROMtbl_student_salaryGROUPBYsalaryHAVINGCOUNT(*)>=(SELECTMAX(cnt)FROM(SELECTCOUNT(*)AScntFROMtbl_student_salaryGROUPBYsalary)TMP);寻找中位数当均值不可靠时,另一个和众数一样经常被使用的指标就是中位数(median)。指集合中的元素按升序排列后正好位于中间的元素。如果集合中的元素个数为偶数,则取中间两个元素的平均值作为中位数。tbl\_student\_salary表中有10条记录,那么张三三60000和李四50000的平均值就是中位数如何用SQL求中位数?方法是把集合中的元素按照大小分成上部和下部两个子集,让这两个子集共享集合中间的元素。这样,公共部分的元素的平均值就是中位数。如下图,当需要根据大小关系生成子集时,就轮到非等价自连接出现了——求中位数的SQL语句:Usenon-equivalentself-joinjoinSELECTAVG(DISTINCTsalary)FROM(SELECTT1.salaryFROMtbl_student_salaryT1,tbl_student_salaryT2GROUPBYT1.salary--HAVING子句中的S1条件TalT1HAVINGsEN.SUM(CASE2=H.salaryTHEN1ELSE0END)>=COUNT(*)/2--S2条件ANDSUM(CASEWHENT2.salary<=T1.salaryTHEN1ELSE0END)>=COUNT(*)/2)TMP;这条SQL语句的关键点在于比较条件中的等号>=COUNT(*)/2。加上等号并不是为了明确的把子集S1和S2分开,而是让两个子集有共同的部分如果等号去掉,条件改为>COUNT(*)/2,那么当元素是偶数,S1和S2没有公共元素,求不到中位数;添加等号是为了编写一个更通用的不包含NULL集的SQL查询。假设我们有一个学生报告提交记录表:tbl\_student\_submit\_logDROPTABLEIFEXISTStbl_student_submit_log;CREATETABLEtbl_student_submit_log(idint(8)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增主键',snovarchar(12)NOTNULLCOMMENT'学号',deptvarchar(50)NOTNULLCOMMENT'学院',submit_dateDATECOMMENT'提交日期',PRIMARYKEY(id))ENGINE=InnoDBDEFAULTCHARSET=utf8COMMENT='学生报告提交记录表';insertintotbl_student_submit_logvalues(1,'20200607001','ScienceCollege','2020-12-12'),(2,'20200607002','ScienceCollege','2020-12-13'),(3,'20200608001','艺术学院',null),(4,'20200608002','艺术学院','2020-12-22'),(5,'20200608003','文学院','2020-12-22'),(6,'20200612001','工学院',null),(7,'20200617001','经济学院','2020-12-23');学生提交报告后,submit\_date栏会写上日期,但提交前为NULL。现在我们需要从这张表中找出哪些大学生都提交了报告。这个SQL应该怎么写?如果只是用WHEREsubmit\_dateISNOTNULL条件查询,那么艺术学院也会被包含在内,结果是不正确的。正确的做法应该是先按部门分组(GROUPBY),再按条件筛选分组。SQL如下SELECTdeptFROMtbl_student_submit_logGROUPBYdeptHAVINGCOUNT(*)=COUNT(submit_date);这里实际使用的是COUNT函数,COUNT(*)可以用于NULL,COUNT(列名)和其他聚合函数一样,必须先排除NULL当然使用CASE也可以实现同样的功能表达式,它更通用SELECTdeptFROMtbl_student_submit_logGROUPBYdeptHAVINGCOUNT(*)=SUM(CASEWHENsubmit_dateISNOTNULLTHEN1ELSE0END);以上场景适用于HAVING,还有很多其他场景也需要用到HAVING。有兴趣的可以阅读《SQL进阶教程》聚合关键条件的归属。看个有意思的东西,还是用表:tbl\_student\_classimage-20210712181154110我们发现聚合键对应的条件可以写在HAVING子句和WHERE子句中。虽然条件分别写在HAVING子句和WHERE子句中,但是条件的内容和返回的结果是完全一样的,所以很多朋友会认为这两种写法没有问题。单从结果来看,确实是没有问题,但是其中一个是偏离SQL规范的非正式用法。推荐的方法是:聚合key对应的条件写在WHERE子句中。原因是语义更清晰。WHERE子句和HAVING子句的作用不同;前面说过,HAVING子句是用来指定“group”的条件的,“row”对应的条件要写在WHERE子句中。这样,写出来的SQL语句不仅可以区分两者的功能,而且更容易理解,执行起来也更快。使用COUNT等函数对表中的数据进行聚合时,DBMS在内部进行排序处理,排序过程会大大增加机器的负载,从而降低处理速度;因此,尽可能减少排序的行数可以提高处理速度当通过WHERE子句指定条件时,由于数据在排序前进行了过滤,因此减少了聚合操作时需要排序的记录数;而HAVING子句对排序后的数据进行分组,相较于在WHERE子句中指定条件另外,索引是WHERE在速度上的另一个优势支持。在WHERE子句指定条件对应的列上建立索引,可以大大提高WHERE子句的处理速度。总结1.集合论集合论是SQL语言的基础。只有站在集合的角度去思考,才能理解SQL的强大。学习HAVING子句的用法,是帮助我们忘记面向过程语言的思维方式,理解SQL面向集合特性的最重要途径。有效方法2、HAVING子句的要素3个要素:常量、聚合函数、聚合键HAVING多数情况下是与GROUPBY结合使用,但不一定与GROUPBY结合使用3、SQL执行顺序WHERE子句是对应的条件指定行,HAVING子句为指定组对应的条件
