为了最简单地说明问题,我特地设计了一张这样的表。

  

一、GROUP BY单值规则

  规则1:单值规则,跟在SELECT后面的列表,对于每个分组来说,必须返回且仅仅返回一个值。

  典型的表现就是跟在SELECT后面的列,如果没有使用聚合函数,必须出现在GROUP BY子句后面。

  如下面这个查询报错:

  

  因为对于按照部门分组之后,技术部分组有3个编号,销售部分组有2个编号,你让数据库显示哪个呢?

  如果假设你使用聚合函数COUNT(编号)之后,对于每个部门分组,就只有一个值 - 该部门下的人数:

  

  下面来实战下,我们希望查询出每个部门,最高工资的那个人的姓名,部门,工资。

  

  Shit,出师不利。第一次实战就错误了,我们来分析下。

  很明显,上面的姓名列是不符合单值规则的。我们的一厢情愿想法是,MAX(工资)之后,SQL Server就能自动帮我们返回不符合单值规则的'姓名'。但是很遗憾,SQL Server并没有这么做。理由如下:

  1.   如果两个人的工资相同,那么应该将哪个人的姓名返回?
  2.   如果我们使用的不是MAX()聚合函数,而是SUM、AVG等聚合函数(没有与之匹配的工资),那么姓名返回哪个?
  3.   如果在查询语句中使用了两个聚合函数,如MAX(),MIN()。那么应该返回的是MAX工资的姓名,还是MIN工资的姓名呢?

  综上所述,数据库是不可能能够根据我们输入的一个聚合函数,就帮助我们判断并显示出不符合单值规则的列的。

  对于MYSQL来说,当有这种不符合单值规则的列时,默认是返回这一组结果的第一条记录。而SQLite是返回最后一条。

  因此,对于以上查询,我们要另寻解决方案。

  解决方案1:关联子查询

SELECT 姓名,部门,工资 FROM 工资表 AS T1
WHERE NOT EXISTS (SELECT NULL FROM 工资表 AS T2 WHERE T1.部门 = T2.部门 AND T2.工资 > T1.工资)

  输出如下:

  

  完全符合要求。对于上面的关联子查询,可以理解为:

  遍历工资表的所有记录,查找不存在比当前记录部门相同且工资还大的记录。

  虽然,关联子查询的语法非常简单,但是性能并不好。因为对于每一条记录,都要执行一次子查询。

  解决方案2:衍生表

   使用衍生表的思路是,先执行一个子查询,得到一个临时结果集,然后用临时结果集和原表进行INNER JOIN操作。就能得到最高工资的人的信息。

  

  刚写出这个SQL语句时,觉得非常妙,理解了之后觉得非常妙。

SELECT 姓名,T1.部门,工资 FROM 工资表 AS T1 INNER JOIN
(
SELECT 部门,MAX(工资) AS 最高 FROM 工资表 --执行查询,先记录两个字段 部门-最高工资
GROUP BY 部门
) AS T2 --衍生表T2
ON T1.部门 = T2.部门 AND 工资 = 最高

  衍生表的方式性能优于关联子查询,因为衍生表的方式只执行了一次子查询。但是它需要一张临时表来存储临时记录。因此,这个方案也并不是最佳的解决方案。

  解决方案3:使用JOIN + IS NULL

  这是一个更妙的解决方案,当我们用一个外联结去匹配记录时,当匹配的记录不存在,就会用NULL来代替相应的列。

  我们先来看一条非常简答的SQL语句:

  

  从中你看到了什么?当T2表中,不存在比T1表中工资高的记录时就返回NULL。

  那么,那么,那么一个IS NULL是不是就解决问题了呢?

  

  好妙,好妙的方法,让人拍案叫绝的使用了OUTER JOIN。

  JOIN解决方案适用于针对大量数据查询并且可伸缩比较时。它总是能比基于子查询的解决方案更好地适应数据量的变量。

  解决方案4:对额外的列使用聚合函数

  我们知道,GROUP BY时,SELECT列表必须返回的是单值,那么我们可不可以通过使用聚合函数,让这个列返回单值呢?答案是可以的。

  

  其实,返回的数据是有问题的,当工资相同时,它就返回按姓名从大到小排列的第一个姓名。也就是说,当工资相同时,它只能够返回一条记录。

  我们将聚合函数换成MIN看看。

  

  解决方案5:Row_Number() + OVER

  WITH B AS
  (
  SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) AS part ,Score, Name, CreateTime
  FROM xxx
  )
  SELECT * FROM B WHERE Part = 1

  输出如下:

  

二、HAVING的理解

  WHERE与HAVING的区别:

  •   WHERE(分组前过滤):WHERE不能对聚合函数列进行过滤,因为执行WHERE的时候,分组尚未执行,聚合函数也未执行。
  •   HAVING(分组后过滤):主要用于对聚合函数列进行过滤,因为HAVING实在分组之后执行的。HAVING子句只能配合GROUP BY子句使用。没有GROUP BY子句时不能使用HAVING。

  错误使用WHERE的示例:

  

  正确使用WHERE与HAVING的示例:

  

查询反模式 - GroupBy和HAVING的理解的更多相关文章

  1. 查询反模式 - GroupBy、HAVING的理解

    为了最简单地说明问题,我特地设计了一张这样的表. 一.GROUP BY单值规则 规则1:单值规则,跟在SELECT后面的列表,对于每个分组来说,必须返回且仅仅返回一个值. 典型的表现就是跟在SELEC ...

  2. 查询反模式 - 正视NULL值

    一.提出问题 不可避免地,我们都数据库总有一些字段是没有值的.不管是插入一个不完整的行,还是有些列可以合法地拥有一些无效值.SQL 支持一个特殊的空值,就是NULL. 在很多时候,NULL值导致我们的 ...

  3. SQL反模式学习笔记1 开篇

    什么是“反模式” 反模式是一种试图解决问题的方法,但通常会同时引发别的问题. 反模式分类 (1)逻辑数据库设计反模式 在开始编码之前,需要决定数据库中存储什么信息以及最佳的数据组织方式和内在关联方式. ...

  4. FWORK-数据存储篇 -- 范式与反模式 (学习和理解)

    理解 1.第二范式的侧重点是非主键列是否完全依赖于主键,还是依赖于主键的一部分.第三范式的侧重点是非主键列是直接依赖于主键,还是直接依赖于非主键列.  2. 反模式 范式可以避免数据冗余,减少数据库的 ...

  5. SQL反模式学习笔记18 减少SQL查询数据,避免使用一条SQL语句解决复杂问题

    目标:减少SQL查询数据,避免使用一条SQL语句解决复杂问题 反模式:视图使用一步操作,单个SQL语句解决复杂问题 使用一个查询来获得所有结果的最常见后果就是产生了一个笛卡尔积.导致查询性能降低. 如 ...

  6. 第二天,导出文件sql,查询,视图view,聚合函数,反模式,字符串处理函数

    //把数据库导出到脚本文件mysqldump -uroot -p1234 --databases abc > d:/a/abc.sql CREATE TABLE stud( id INT PRI ...

  7. SQL反模式学习笔记2 乱穿马路

    程序员通常使用逗号分隔的列表来避免在多对多的关系中创建交叉表, 将这种设计方式定义为一种反模式,称为“乱穿马路”. 目标:  存储多属性值,即多对一 反模式:将多个值以格式化的逗号分隔存储在一个字段中 ...

  8. SQL反模式学习笔记4 建立主键规范【需要ID】

    目标:建立主键规范 反模式:每个数据库中的表都需要一个伪主键Id 在表中,需要引入一个对于表的域模型无意义的新列来存储一个伪值,这一列被用作这张表的主键, 从而通过它来确定表中的一条记录,即便其他的列 ...

  9. SQL反模式学习笔记15 分组

    目标:查询得到每组的max(或者min等其他聚合函数)值,并且得到这个行的其他字段 反模式:引用非分组列 单值规则:跟在Select之后的选择列表中的每一列,对于每个分组来说都必须返回且仅返回一直值. ...

随机推荐

  1. Linux内核参数之arp_ignore和arp_announce

    一.arp_ignore和arp_announce介绍 arp_ignore和arp_announce参数都和ARP协议相关,主要用于控制系统返回arp响应和发送arp请求时的动作.这两个参数很重要, ...

  2. Openldap基于digest-md5方式的SASL认证配置

    1. openldap编译 如果需要openldap支持SASL认证,需要在编译时加上–enable-spasswd选项安装完cyrus-sasl,openssl(可选),BDB包后执行: 1 2 $ ...

  3. 2016湘潭邀请赛—Heartstone

    http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1246 题意: 有n只怪,每只怪有指定的HP.现在1和2两种攻击方式,前者扣 ...

  4. S4 继承

    S3 系统是宽泛且灵活的,同类的 S3 对象也可能有不同的成员.但是,对于 S4 系统,就不会发生,也就是说,当我们创建一个属于某类的 S4 对象实例时,不能任意添加不在类表示中的字段.举个例子,在创 ...

  5. Java语言的垃圾回收机制

    java语言从诞生开始,一个吸引人眼球的功能就是垃圾回收,想一想C++中时不时的内存泄漏,当时感觉写java代码直是一种享受呀.     和.NET的引用计数不同,java的垃圾回收机制采取的是有向图 ...

  6. Java实现时钟小程序【代码】

    哎,好久没上博客园发东西了,上一次还是两个月前的五一写的一篇计算器博客,不过意外的是那个程序成了这学期的Java大作业,所以后来稍微改了一下那个程序就交了上去,这还是美滋滋.然后五月中旬的时候写了一个 ...

  7. linux进程概论

    1操作系统几大模块 进程管理,进程调度,进程间通讯机制,内存管理,中断异常处理,文件系统,I/O系统,网网络部分. 2操作系统的目的 管理硬件设备,为上层应用程序提供良好的执行环境. 3linux系统 ...

  8. gdb调试libtool封装的可执行文件

    http://www.gnu.org/software/libtool/manual/html_node/Debugging-executables.html 3.4 Debugging execut ...

  9. SGU 139. Help Needed! 逆序数,奇偶性,分析 难度:0

    139. Help Needed! time limit per test: 0.25 sec. memory limit per test: 4096 KB Little Johnny likes ...

  10. 关于CMD/DOS中的短文件名规则

    最近在制作一个批处理的过程中发现一个很郁闷的问题,就是有些时候搜索到的结果不是我们想要的