问题:查找某个列中值的模式(数学中的模式概念就是对于给定的数据集出现最频繁的元素)。例如,查找DEPTNO 20中工资的模式。例如下列工资:
select sal
from emp
where deptno = 20
order by sal
SAL
----------
800
1100
2975
3000
3000
the mode is 3000.
解决方案
DB2和SQL Server
使用窗口函数DENSE_RANK,把工资重复出现次数分等级,以便提取模式:
1 select sal
2 from (
3 select sal,
4 dense_rank()over(order by cnt desc) as rnk
5 from (
6 select sal, count(*) as cnt
8 from emp
9 where deptno = 20
10 group by sal
11 ) x
12 ) y
13 where rnk = 1
Oracle
在Oracle8i Database中,可以使用DB2给出的解决方案。对于Oracle9i及更高版本,可以用聚集函数MAX的KEEP扩展,以得到SAL模式。特别要注意的是,如果存在绑带,也即多个行都是模式,则采用KEEP方案仅能得到一个,即其中工资最高的那个。如果想要看所有模式(如果存在多个模式),则必须修改该方案,或者简单地使用前面介绍的DB2解决方案。在这个例子中,由于3000是DEPTNO 20中SAL的模式,而且它也是最高的SAL,因此以下方案就可以了:
1 select max(sal)
2 keep(dense_rank first order by cnt desc) sal
3 from (
4 select sal, count(*) cnt
5 from emp
6 where deptno=20
7 group by sal
8 )
MySQL和PostgreSQL
使用子查询查找模式:
1 select sal
2 from emp
3 where deptno = 20
4 group by sal
5 having count(*) >= all ( select count(*)
6 from emp
7 where deptno = 20
8 group by sal )
讨论
DB2和SQL Server
内联视图X将返回每个SAL及它出现的次数。内联视图Y使用窗口函数DENSE_RANK(它允许绑带)给结果排序。结果按每个SAL出现的次数分等级,如下所示:
1 select sal,
2 dense_rank()over(order by cnt desc) as rnk
3 from (
4 select sal,count(*) as cnt
5 from emp
6 where deptno = 20
7 group by sal
8 ) x
SAL RNK
----- ----------
3000 1
800 2
1100 2
2975 2
最外层的查询只简单地保留RNK为1的行。
Oracle
内联视图将返回所有SAL及其出现的次数,如下所示:
select sal, count(*) cnt
from emp
where deptno=20
group by sal
SAL CNT
----- ----------
800 1
1100 1
2975 1
3000 2
下一步,使用聚集函数MAX的KEEP扩展查找模式。如果仔细分析下面给出的KEEP子句,会发现它又有三个子句,即DENSE_RANK、FIRST和ORDER BY CNT DESC:
keep(dense_rank first order by cnt desc)
这种做法对求模式极其方便。KEEP子句根据内联视图返回的CNT值来确定MAX返回SAL的哪个值。按从右向左的方向将CNT递减排序,然后保留下按DENSE_RANK次序返回的所有CNT值的第一个值。查看一下内联视图的结果集,就会看到3000具有最高的CNT值 —— 2。MAX(SAL) 返回的是拥有最高CNT值的最大SAL,在本例中是3000。
有关Oracle中集合函数的KEEP扩展的深入讨论,请参阅第11章第11.11节。有关Oracle中集合函数的KEEP扩展的深入讨论,请参阅第11章第11.11节。
MySQL和PostgreSQL
子查询将返回每个SAL出现的次数。外层查询将返回其的出现次数大于等于子查询所返回所有计数值的SAL(换句话说,外层查询会返回DEPTNO 20中出现最多的工资)。