分组比较是看起来比较简单,但是写起来比较麻烦的问题,一般就是先进行两个不同分组计数、求和、求均值,然后两个均值作比较,这样就涉及表连接和判断,写的代码量就比其他问题多很多。它与连续问题、排名问题和累加问题不同的是,后面三个问题是数据行之间纵向产生关系,而这里是横向产生关系。

这类问题也是有一定套路的,下面用实际案例数据还原真实取数场景,帮助你在实战中理解如何实现分组比较取数的过程,总结思路。建议在电脑大屏阅读效果会更好。

需求:老板要求写一个查询语句,求出在每月每个经销商的平均销售金额与公司的平均销售金额的比较结果 (高 / 低 / 相同),这样以便于后续制定激励经销商的政策措施。你该如何写呢?

背景:数据来源于微软示例数据库,一家销售自行车制造公司的销售数据,分为网络销售FactInternetSales和经销商销售FactResellerSales两张表,其中网络销售订单数据60398行,经销商销售数据有60855行。此处我们简化问题,重点在问题的解决上,只用到FactResellerSaless表,表中有包含价格orderdate、resellerkey和salesamount在内的共计27个字段,每个日期存在多个订单情况。

分析:先分析需求看我们要取的是哪些字段,每月、每个经销商、经销商平均销售金额,公司、公司平均销售金额,比较结果,这里面公司字段值相同不需要取,经销商平均销售金额、公司平均销售金额,比较结果三个字段是原表没有的,因此需要新增计算字段。求平均需要用到avg函数,而判断条件,自然需要if语句或者case when语句(两种在功能上是相同的)。这一步是很多初学者可能都还没有形成的思维,一看到需求还无法迅速定位到要取什么数、用到什么函数。

角度一:简单查询是一张表取数,这里虽然原数据在一张表中,但是显然比较两个分组会产生两张临时表,所以就需要复合查询来解决。

角度二:聚合函数+复合查询

解法一 复合查询

下面是复合查询的代码:

with t1 as (select date_format(orderdate,"%Y-%m") as paydate,resellerkey,avg(salesamount) as avd from FactResellerSalesgroup by date_format(orderdate,"%Y-%m"),resellerkey),
t2 as (select date_format(orderdate,"%Y-%m") as paydate,avg(salesamount) as ava from FactResellerSalesgroup by date_format(orderdate,"%Y-%m"))
select paydate,resellerkey,t2.ava as allsa,t1.avd as allsd,if(t1.avd<< span="">t2.ava,"lower",if(t1.avd=t2.ava,"same","higher")) as comparison
from t1 left join t2
using(paydate)
order by paydate,resellerkey;

解析上面的代码,t1表是产生每个月每个经销商评价销售额,t2表是每个月总平均销售额,在主SQL语句中用if语句来判断产生对应的结果。整体思路没什么难的,只是一些小问题需要解决,比如日期,实际工作中日期一般都是自然日期,不会增加一个字段专门记录每年每月,所以需要日期格式化;另外,表连接的时候on关键字还可以换成using关键字,on t1.paydate = t2.paydate 就可以直接用using(paydate)代替节省代码,但是这要在两张表的相同字段相同名称才可以。

解法二 子查询

上面用了公共临时表作为过渡表,这里我们也可以把它转成子查询。

select b.paydate,b.resellerkey, a.ava,b.avd,case when b.avd > a.ava then 'higher'when b.avd << span=""> a.ava then 'lower'else 'same' end as comparison
from (select DATE_FORMAT(orderdate,'%Y-%m') as paydate, avg(salesamount) as avafrom FactResellerSales group by paydate) as a
right JOIN (select resellerkey,DATE_FORMAT(orderdate,'%Y-%m') as paydate, avg(salesamount) as avdfrom FactResellerSales group by paydate,resellerkey) as b
on (a.paydate = b.paydate)
order by paydate,resellerkey;

两者的处理思路一样、执行效率也是差不多的,子查询的衍生表放在with as 结构中就是公共临时表,而公共临时表(CTE)的好处在于可读性很强,逻辑关系分得清楚,而且如果多次使用衍生表,则可以引用CTE就可以了,代码复用性强。这里只是把if语句换成了case when。

解法三 窗口函数

之前我们在文章“茴”字有几种写法?SQL排名问题之全局排名的四种解法说到,窗口函数实际是聚合函数加group by的功能封装,所以我们也可以使用聚合函数加over来实现。

select distinct date_format(orderdate,"%Y-%m") as paydate,resellerkey,casewhen avg(salesamount) over(partition by date_format(orderdate,"%Y-%m")) >avg(salesamount) over(partition by date_format(orderdate,"%Y-%m"),resellerkey) then 'higher'when avg(salesamount) over(partition by date_format(orderdate,"%Y-%m")) >avg(salesamount) over(partition by date_format(orderdate,"%Y-%m"),resellerkey) then 'lower'else 'same' end as comparison
from FactResellerSales
order by paydate,resellerkey;

上面这段代码结构与之前两种写法相比简单,没有使用表连接。一般写SQL语句能用单表查询尽量用单表,因为表连接是有代价的。在select后面字段里直接将分组聚合和条件判断放在一起,分组的功能是通过窗口over里的partition by来实现。这也启发了一种解题思路,如果有具备相似功能的工具,即使是用在不同问题上也可以创造条件来借用。举一反三的能力就是在这样情况下锻炼得到。case when then语句的使用很简单,when后面接第一个条件,满足就把结果给then后面,如果不满足就给else后面,最后用end关键字结尾,如果有多个条件就重复使用when…then句式,它比if语句更方便用在多个条件,因为if嵌套有很多括号,往往容易出错。

思路三:自定义变量。一般情况下窗口函数都可以使用自定义变量方法,但是这里使用自定义变量而不用avg函数也不用窗口函数,代码量就会非常大。

解法四 自定义变量

以下就是代码:

with t1 as (
select orderdate,resellerkey,max(sss)/max(sss1) as as1
from(select orderdate,resellerkey,salesamount,@cur:=salesamount,if(@dt = orderdate,if(@re=resellerkey,@sa:=@sa+@cur,@sa:=@cur),@sa:=@cur) sss,if(@dt = orderdate,if(@re=resellerkey,if(@r=salesamount,@sa1:=@sa1,@sa1:=@sa1+1),@sa1:=1),@sa1:=1) sss1,@dt:= orderdate,@re:=resellerkeyfrom FactResellerSales,(select @dt :=0,@re :=0,@sa:=null,@cur:=null,@sa1:=0) initorder by orderdate,resellerkey,salesamount)c
group by orderdate,resellerkey),t2 as (select orderdate,resellerkey,max(sss2)/max(sss3) as as2
from(select orderdate,resellerkey,salesamount,@cur2:=salesamount,if(@dt2 = orderdate,@sa2:=@sa2+@cur2,@sa2:=@cur2) sss2,if(@dt2 = orderdate,@sa3:=@sa3+1,@sa3:=1) sss3,@dt2:= orderdate,@re2:=resellerkeyfrom FactResellerSales,(select @dt2 :=0,@re2 :=0,@sa2:=null,@cur2:=null,@sa3:=0) initorder by orderdate,resellerkey,salesamount) f
group by orderdate)select t1.orderdate,t1.resellerkey,t1.as1,t2.as2,if(t1.as1<< span="">t2.as2,"lower",if(t1.as1=t2.as2,"same","higher")) as comparison
from t1 left join t2
using(orderdate);

可以看到这个代码比其他方法多了一倍。但是这也是练习理解自定义变量使用的很好的实践,假如你能写通这段代码,以后当你遇到无法使用窗口函数的时候,就可以得心应手的使用这种方法啦。这也是种装逼方式,你可以跟人家说你一条SQL语句写了一百多行,自己都看不懂哈哈。

代码整体逻辑很简单,t1表生成每个月每个经销商平均销售额,它表生成每个月公司总平均销售额,最后两张表连接,用if语句做比较得到结果。这里我没有将日期进行格式化为每月,因为表中原始数据实际上就是每个月只有月末数据,相当于格式化分组了,另外这种解法在实际工作中不建议使用,只有在没有办法后才使用,因为它变量太多,执行效率太低,代码可读性也很差,这里为了减少冗余就没处理。这里核心的部分就是求平均值的地方,你可以看到不用avg函数也是可以求均值。这里用到了累加的思路,先把每个组的销售额累加,再取最后一个值,同时也把每累加一次进行计数,取最后一个数,这样相当于算出了每组的总和和数据行数,两者相除就得到平均值。求解思路搞清楚之后,来看一下具体怎么写代码。这里是通过if语句条件判断来实现分组的效果,t1表中字段别名为sss的if语句,意思是如果变量@dt= orderdate,再判断是否@re=resellerkey,如果是同一个日期同一个经销商就用@sa(代表累加值)来迭代相加得到累计值每一行数据对应的累计值@sa:=@sa+@cur表示新的累加值都是前一个累加值加上当前值,而每进行一次迭代都会将当前值salesamount赋值给变量@cur,这样一行一行迭代,跟分组累加求解一样。但是光有总数没有每个组的个数,就没有办法求平均值。所以这里用同样的思维累加计数,得到每组个数。它的实现步骤比累加求和多了一步,不仅要判断是否同一日期同一经销商还要判断是否同一金额,达到对每个金额排名作为计数的效果(这里有个问题,可能同一日期同一经销同一金额有重复,实际上还要判断是否同一订单,为了简化我没有做,在这里你就能感受到做题题题会做,但是实际工作处处是细节问题,处处是坑啊),这两个if语句实现的效果如下:

再做一个子查询找到每组最大值,实现的效果如下:

最终t1表实现的效果如下:

可以看到两者的效果是一样的。但是写法和执行效率远不如avg函数简洁高效。t2表也是同样的方法得到。

总结一下自定义变量的套路就是:

第一步,先想好分组条件有几个,比如这里对同一日期同一经销同一金额计数,就需要三个变量,还要再加一个临时中间变量;每个变量的命名都以@开头,名字最好是对应原字段缩写,可以方便知道代表含义。

第二步,将这些变量写进select 语句中组成一张临时表,就是上面的(select @dt :=0,@re :=0,@sa:=null,@cur:=null,@sa1:=0) init,初始值都默认为0或者null,表别名可以自定义,但是建议统一命名为init,不需要再费脑筋想名字;将这张临时表跟在原数据表后面。

第三步,在主select语句中先将数据原字段取出,还要将所有临时变量都要写上,不能不写,不写或少写就会出错,顺序也不能错误,写错顺序也会得不到结果。书写顺序依次是:临时中间变量@cur,以@sa为最终结果的计算或判断语句,判断条件一@dt,判断条件二@re,这四个变量都是以“:="号形式分别将原数据表字段赋值给它们。下面是动画效果:

这张动图也能很好的说明MySQL执行的顺序,先from从表中一行一行从左到右取出数据,若要计算或者判断都是先从左往右判断执行。

以上就是教大家怎么理解用户自定义变量和具体怎么写,大家是否看懂了呢?如果没看懂请关注“二八Data”并私信我,我再详细为你解释。

这是SQL系列进阶文章第四篇,这篇用到了前面所讲的排名问题和累加问题的方法,如果还不理解请看相关文章。对于数据分析师来说,不求十八般武艺样样精通,但是至少要懂得最基础最常用的几种,这样才能让你有一战之力。写SQL语句不需要你必须完全懂”增删改“的知识,而只需要精通如何查询就好了,否则你就是数据库管理员了。

最后欢迎大家关注我,我是拾陆,搜索公众号“二八Data”,更多技术干货持续奉献。

销售额超过公司均值的优秀经销商?SQL比例问题之分组比较的四种解法相关推荐

  1. 强网杯 2019]随便注 【SQL注入】四种解法

    题目简介 题目名称:[强网杯 2019]随便注 1 题目平台:BUUCTF 题目类型:Web 考察知识点: SQL注入 解题步骤 方法一: 首先启动并访问靶机,有一个输入框,随便输入1' or 1 = ...

  2. SQL模糊查询【like】的四种匹配模式

      执行数据库查询时,有完整查询 和 模糊查询之分,一般模糊语句如下: SELECT 字段 FROM 表 WHERE 某字段 Like 条件 四种匹配模式 一.% 二. _ 三.[ ] 四.[^] 一 ...

  3. 像优秀的SQL程序员一样思考

    像优秀的SQL程序员一样思考 --<SQL编程风格> 本书详细信息请点击:http://www.china-pub.com/209168 数据库作为现代软件应用的核心之一,正在发挥越来越重 ...

  4. 如何帮助公司设计一个优秀的品牌标志?

    随着我们接触越来越多的数字媒体,一个品牌必须要在快速发展的市场中保持现代感和新鲜感.过去,判断一家公司的标志是看它在名片或信笺上的功能,而现在,这两者都不如品牌的社交媒体账户上的头像重要. 理所当然, ...

  5. 想要精通算法和SQL的成长之路 - 超过经理收入的员工(SQL)

    想要精通算法和SQL的成长之路 - 超过经理收入的员工(SQL) 前言 一. 超过经理收入的员工 1.1 自连接 1.2 子查询 1.3 join 语句 前言 想要精通算法和SQL的成长之路 - 系列 ...

  6. [强烈推荐]ORACLE SQL:经典查询练手第四篇(不懂装懂,永世饭桶!)

    [推荐]ORACLE SQL: 经典查询练手第四篇(不懂装懂,永世饭桶!) --通过知识共享树立个人品牌. 本文与大家共同讨论与分享ORACLE SQL的一些常用经典查询,欢迎大家补充,同时你认为有那 ...

  7. java笔试题_公司真题 | 用友2018秋招Java笔试题(四)

    公司真题 | 用友2018秋招Java笔试题(三)答案: 1.正确答案 A B 2.正确答案 A 3.正确答案 D 4.正确答案 B 5.正确答案 A E 6.正确答案 B 7.正确答案 C D 8. ...

  8. mysql数据库设计工具_四种优秀的数据库设计工具

    [51CTO.com快译]众所周知,良好的数据库设计能够大幅减少后期的运维工作,同时也能最大程度地减少软件项目出错的可能.由于我们所面临的真实项目需求往往五花八门,因此需要找到合适的设计工具,来实现事 ...

  9. //公司有四种职位 经理(Manager) 技术人员(Technician) 销售(SaleMan) 销售经理(SaleManager) //每增加一个人,工号加1 //薪资:经理 8000 技术人员

    //公司有四种职位 经理(Manager) 技术人员(Technician) 销售(SaleMan) 销售经理(SaleManager) //每增加一个人,工号加1 //薪资:经理 8000 技术人员 ...

最新文章

  1. 算法实践1_线性回归
  2. 点云配准(一 两两配准)
  3. F5提高Microsoft SharePoint 2010平台的灵捷 度、性能和安全性
  4. 信息掩码游戏地图掩码相关(msk)
  5. Spring基于注解的方式一
  6. 无连接可靠传输_FPC连接器的特点以及弹片微针模组的作用
  7. java+读取source资源_如何从JavaJAR文件中读取资源文件?
  8. docker构建dpdk运行环境镜像
  9. 常用几个UITableView,UICollectionView  UIScrollView关键点
  10. 在中标麒麟上基于源码安装第二个gcc编译器
  11. 2020的迷之骗局:从瑞幸退市到老干妈炒鹅 | 凌云时刻
  12. Android 消息机制之 MessageQueue 消息队列
  13. 更改itunes备份路径【windows备份iphone数据】
  14. 读《天才在左,疯子在右》
  15. Android 日历表事件表操作
  16. MDS中的caps和Locker
  17. 江苏计算机一级证书考试试题,2016年江苏省计算机一级考试试题
  18. 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例69
  19. 计算机毕业设计SSM电影售票管理系统【附源码数据库】
  20. 计算机学霸电视剧,好看的青春校园电视剧 十部好看的校园剧推荐

热门文章

  1. 关于stringWithFormat:
  2. 榆熙教育:拼多多店铺权重计算方式是什么
  3. 计算机组成原理笔记——中央处理器CPU
  4. python随机数列表变成字符串_DAY8 for Python--字符串与随机数
  5. 问卷星/问卷管理系统的设计与实现,附源码+开发环境【优质毕设】
  6. 计算机组成原理期末复习90分以上选择填空大题总考点
  7. pd.concat数据拼接
  8. cfiledialog对话框大小_CFileDialog文件对话框用法
  9. linux命令stat,Linux stat命令参数及使用方法详解
  10. 传承——用双手打工创造的未来依旧可行