为了最简单地说明问题,我特意设计了一张这样的表。数据库
规则1:单值规则,跟在SELECT后面的列表,对于每一个分组来讲,必须返回且仅仅返回一个值。函数
典型的表现就是跟在SELECT后面的列,若是没有使用聚合函数,必须出如今GROUP BY子句后面。性能
以下面这个查询报错:设计
由于对于按照部门分组以后,技术部分组有3个编号,销售部分组有2个编号,你让数据库显示哪一个呢?3d
若是假设你使用聚合函数COUNT(编号)以后,对于每一个部门分组,就只有一个值 - 该部门下的人数:code
下面来实战下,咱们但愿查询出每一个部门,最高工资的那我的的姓名,部门,工资。blog
Shit,出师不利。第一次实战就错误了,咱们来分析下。it
很明显,上面的姓名列是不符合单值规则的。咱们的一厢情愿想法是,MAX(工资)以后,SQL Server就能自动帮咱们返回不符合单值规则的'姓名'。可是很遗憾,SQL Server并无这么作。理由以下:class
综上所述,数据库是不可能可以根据咱们输入的一个聚合函数,就帮助咱们判断并显示出不符合单值规则的列的。变量
对于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
输出以下:
WHERE与HAVING的区别:
错误使用WHERE的示例:
正确使用WHERE与HAVING的示例: