现在的位置: 首页 > 综合 > 正文

SQL常犯错误—SQL分组查询中的单值规则和解决之道

2013年09月12日 ⁄ 综合 ⁄ 共 1446字 ⁄ 字号 评论关闭

SQL的分组查询(group by)是非常有用的,但是有时候它的特性也令人恼火。好好看看这个sql:

select employer_name, department, max(salary) from employer_salary group by department;

查询每个部门中工资最高人的姓名和工资数目,这个SQL有问题吗, 真有问题。

违反了SQL分组查询中的单值规则,这个规则通俗点就是说在分组查询中,select 关键字后面跟随的column必须在每个分组中都必须是相同的值,比如说department 因为分组就是以department分的,所以它必然是相同的,还有max(salary)也是单一的一个值,这就是所谓“单值规则”。

一般来说我们在做分组查询的时候会需要一些不能满足单值规则的column,比如employer_name。不知道这个规则之前,我们总是猜想分组查询应该会够智能,能够判断出我需要的employer_name就是最多薪水(max(salary)) 的那个人的名字, 其实这是一厢情愿。

1. 如果拿max(salary) 人不止一个,那employer_name应该显示哪个?

2. 如果不是max是avg, sum,count employer_name有应该放哪个?

所以不要为难SQL了,办法得自己想。

一般的处理方式基本上是这样的:

select a.* from employer_salary a JOIN (select employer_name, department, max(salary) as salary from employer_salary group by department) b on a.department = b,department and a.salery = b.salary;

麻烦吗,不麻烦,就是效率不高,另外在《SQL反模式》这本书中也还提供了几种解决方案,都不理想,其中一个方案却是巧妙,但是比较晦涩,不好理解,写出来供大家一起拜膜:

select a.* from 
(employer_salery a LEFT JOIN employer_salary b ON a.department = b.department and a.salary < b.salary)
where b.department is NULL;

有什么更好理解的解决方案吗, 不幸的是标准SQL没有, 幸运的是各大DB品牌有自己的解决方案, 最近在用postgresql 就说说它的解决方案吧。

Postgresql window function:

还是上面那个case使用window函数就会写成这样:

select a.employer_name, a.department, a.salary from 
(select employer_name, department, salary, max(salary) as max_salary OVER(DEPARTMENT BY department)) as a
where a.salary = a.max_salary

看起来好像也很复杂,但是很好理解。

请主要看第二行,window 函数和标准sql分组函数的区别在于它并不遵守单值规则,并没有将查询出的结果限制为一行而是依然保留查询进行前的所有结果集并将分组查询后的结果追加在后面。这样也便于将原始的column和聚合函数作用后的结果进行比较。

另外不遵守单值规则的sql一般来说都会受到DB报出的一个error除了mysql和SQLite, 它们会返回不确定的值而不报错,隐蔽性很高,需要注意。

 

抱歉!评论已关闭.