058、HAVING与条件组上一章我们把GROUP BY拆了个底朝天有兄弟回头就踩了个坑统计每个销售订单的行项目数用GROUP BY搞定了但想只显示行项目数大于5的订单直接把条件丢进WHERE里系统报错“字段不是按分组字段”。这问题我这周还帮人看过报错信息挺有迷惑性实际就是没弄明白WHERE和HAVING的分工。今天就从这坑开刀把HAVING那点事聊透。先说那个错。你写SELECT vbeln, COUNT(posnr) FROM vbap WHERE posnr 5 GROUP BY vbeln.数据库根本不认识你这个posnr 5因为分组之后单个posnr已经被聚合掉了数据库看到的是一堆订单号加一个计数结果哪还有原始的posnr这个posnr压根不在分组键里也不是聚合函数的结果所以WHERE想过滤它等于在一个已经碎成渣的表格里找原装零件找不着。正确的姿势是等分组和聚合都干完了再拿结果集来筛。这时候该上场的就是HAVINGSELECT vbeln, COUNT( posnr ) AS cnt FROM vbap GROUP BY vbeln HAVING COUNT( posnr ) 5.注意HAVING不是在SQL语句最后随便补个尾巴它跟GROUP BY是成对出现的逻辑上跟在聚合之后。你可以在脑子里把它想成先GROUP BY把数据揉成几坨然后对每一坨做COUNT、SUM这些动作得出一个中间结果集最后HAVING再从这个结果集里挑出符合条件的组。这里有个常见误解以为HAVING能直接写字段别名。比如上面我写了AS cnt然后你写HAVING cnt 5某些数据库支持但ABAP的Open SQL在标准行为下是不认这个别名的别指望它。老老实实把聚合函数再写一遍。这地方我踩过当时从SQL Server转过来习惯了HAVING cnt 5搬到ABAP直接被拒说“未知列名”。后来干脆每次都在HAVING里重复写聚合表达式虽然看着啰嗦但稳。有人问那如果我就是想在分组前把某些行剔掉怎么办用WHERE啊WHERE在分组之前干活HAVING在分组之后干活。这俩不冲突能一起用。比如我们要查每个订单号下物料主数据里删除标记为空的明细数量并且只保留数量超过10的订单。先WHERE把不想要的物料类型滤掉再GROUP BY再HAVING筛数量SELECT vbeln, COUNT( posnr ) FROM vbap WHERE mtart Z001 GROUP BY vbeln HAVING COUNT( posnr ) 10.这个顺序别搞反。你不能用HAVING去干WHERE的活也别用WHERE去干HAVING的活。什么活是WHERE的针对单行原始数据的条件比如vbeln 123、werks IN (...)、mtart Z001这些必须在分组前就定下来。什么活是HAVING的针对聚合结果的条件比如COUNT(*) 5、SUM( netwr ) 1000、MAX( posnr ) 50这些必须在分组后才有意义。再往深一点说“条件组”这个词其实指的就是HAVING后面跟的多个条件组合。ABAP里HAVING可以跟AND、OR连用跟WHERE里的逻辑一样。比如要找出订单里行项目数大于5且净价值总和大于10000的订单SELECT vbeln, COUNT( posnr ) AS cnt, SUM( netwr ) AS total FROM vbap GROUP BY vbeln HAVING COUNT( posnr ) 5 AND SUM( netwr ) 10000.注意这里SUM(netwr)和COUNT(posnr)分别作用于同一组的不同列只要它们都属于同一个分组就可以放心组合。但别把不同分组键的条件混在一个HAVING里比如HAVING COUNT( posnr ) 5 AND vbeln 123这个vbeln 123其实应该扔进WHERE。虽然有时候语法能过但逻辑上很拧巴而且可能会影响执行效率。分组前就能筛掉的单行记录你非要拖到分组后再筛等于先把所有数据揉成组再一个一个组拆开看白白让数据库多干活。我们写ABAP的本来就该体谅数据库能早筛就早筛。还有一个细节容易忽略HAVING里其实也能用不含聚合的字段但前提是这个字段必须出现在GROUP BY里。比如SELECT vbeln, bstnk, COUNT( posnr ) FROM vbap GROUP BY vbeln, bstnk HAVING bstnk PO-2024-001这语法对因为bstnk在分组键里。但你要是有这需求干嘛不放WHEREWHEREbstnk PO-2024-001效果一样还更快。所以HAVING里写非聚合字段虽然合法但基本属于「能跑但是有异味」的代码。我看到别人这么写一般会提醒一句把条件往下挪挪。再说一个性能上的坑。HAVING的数字比较可能让你误以为能走索引实际上聚合操作基本把索引优势抵消了。尤其是对全表做GROUP BY再HAVING数据量一大就卡。我见过一个报表查三年销售数据跑了好几分钟最后发现人家把一堆条件全塞HAVING里WHERE里啥都没写。后来把能提前的条件全挪到WHERE执行时间直接降到十几秒。这例子我记得很清楚因为那哥们还跟我说“明明HAVING也能用”我说“能用和好用是两码事”。程序里写ABAPHAVING通常配合FOR GROUP BY这种循环结构来用但那是后面章节的事。今天你只需要掌握这个最小模型HAVING是用来筛组的不是用来筛行的。每次写HAVING之前先问自己我这个条件是不是必须等分组聚合完才能判断如果是就放HAVING如果不是麻溜放WHERE。最后给个个人习惯我在写HAVING的时候喜欢把聚合函数里的字段跟GROUP BY的字段统一一下风格。比如GROUP BY里写了vbeln那HAVING里就写COUNT( posnr )别一个全大写一个全小写虽然系统不在乎但人看代码时会愣一下。另外如果HAVING里条件超过两个我习惯每个条件占一行用AND开头对齐这样回头调试时一眼就能看清是哪组条件筛掉了数据。调试的时候还有个土办法先把SQL注释掉HAVING跑一遍看看总共多少组再带上HAVING跑一遍对比一下组数差异心里就有底了。别笑这种土办法在关键时候比什么调试器都直接。HAVING这东西理解了它和WHERE的边界就不会再犯开头的错。下次再看到报错里带“GROUP BY”关键字你就知道是数据分组前你动了不该动的字段。把条件放对地方数据库不闹脾气你加班也少双赢。