《一道褒贬不一的 SQL 考试题》 http://www.triaton.com.cn/cgi-bin/lb5k/topic.cgi?forum=4&topic=97&show=09.列印学生平均成绩及其名次 select count(distinct b.f) as 名次,a.学生ID,max(a.学生姓名),max(a.f) from (select distinct t.学生ID,t.学生姓名,(select avg(成绩) from t t1 where t1.学生id = t.学生id) as F from T ) as a, (select distinct t.学生ID,t.学生姓名,(select avg(成绩) from t t1 where t1.学生id = t.学生id) as F from T ) as b where a.f <= b.f group by a.学生ID order by count(b.f) 这里有很多值得一提的地方,先利用两个完全相同的自相关子查询生成两个派生表作 为基本表用于作小于或等于的连接,这样就可以通过表中小于或等于每个值的其他值 的 COUNT(distinct) 的计数聚集函数来体现名次了。 SELECT 1+(SELECT COUNT(distinct [平均成绩]) FROM (SELECT [学生ID],MAX([学生姓名]) AS 学生姓名 ,AVG([成绩]) AS [平均成绩] FROM T GROUP BY [学生ID] ) AS T1 WHERE [平均成绩] > T2.[平均成绩]) as 名次, [学生ID],[学生姓名],[平均成绩] FROM (SELECT [学生ID],max([学生姓名]) AS 学生姓名,AVG([成绩]) AS [平均成绩] FROM T GROUP BY [学生ID] ) AS T2 ORDER BY T2.[平均成绩] desc 方法二也使用了两个完全相同的自相关子查询生成两个派生表作为基本表,再利用它 们之间作大于的相关子查询取 COUNT(distinct) + 1 的计数聚集函数同样实现了名 次的显示。 这道题从应用角度来看,查询结果是相当合理的,并列情况的名次也都一样。但如果想 实现类似自动序列的行号,该解决方案的局限性突显,不能处理并列相等的情况了,所 以有必要强调:一定要选择不重复的连接条件,可以根据实际情况利用字段组合的不等 连接 (T1.f1 + ... + T1.fn <= T2.f1 + ... + T2.fn)。继续引申还可以通过判断 COUNT(distinct) % 2 是否为 0 的 HAVING 或 WHERE 子句实现只显示偶数或奇数行: HAVING count(distinct b.f) % 2 = 1 或: WHERE 1+(SELECT COUNT(distinct [平均成绩]) FROM (SELECT [学生ID],MAX([学生姓名]) AS 学生姓名 ,AVG([成绩]) AS [平均成绩] FROM T GROUP BY [学生ID] ) AS T1 WHERE [平均成绩] > T2.[平均成绩]) % 2 =1 再简单说一下 HAVING 和 WHERE 在含有 GROUP BY 分组的查询中的区别,HAVING 是 在数据分组后才筛选记录的,WHERE 是先进行筛选在分组的,而且 HAVING 一般应与聚 集函数合用才有真正含义。 两种方法再次体现了子查询与连接可以殊途同归之妙,第二种子查询方法值得推荐,因 为比较利于程序构造,便于为没有该功能的原有查询添加此项功能。本题仅仅是为了示 范一种比较新颖的解题思路,回避了效率的问题。
select * into #test from (select 1 as id,2 as a union all select 2,5 union all select 3,5 union all select 4,6 union all select 5,3) T select count(distinct T1.a),T1.id,min(T1.a) from (select * from #test ) as T1 , (select * from #test ) as T2 where T1.a <= T2.a group by T1.id order by count(*) SELECT 1+(SELECT COUNT(distinct a) FROM (select * from #test) AS T1 WHERE a > T2.a) as 名次 ,id,a FROM (select * from #test ) AS T2 ORDER BY T2.a desc
http://www.triaton.com.cn/cgi-bin/lb5k/topic.cgi?forum=4&topic=97&show=09.列印学生平均成绩及其名次 select count(distinct b.f) as 名次,a.学生ID,max(a.学生姓名),max(a.f)
from (select distinct t.学生ID,t.学生姓名,(select avg(成绩)
from t t1
where t1.学生id = t.学生id) as F
from T
) as a,
(select distinct t.学生ID,t.学生姓名,(select avg(成绩)
from t t1
where t1.学生id = t.学生id) as F
from T
) as b
where a.f <= b.f
group by a.学生ID
order by count(b.f) 这里有很多值得一提的地方,先利用两个完全相同的自相关子查询生成两个派生表作
为基本表用于作小于或等于的连接,这样就可以通过表中小于或等于每个值的其他值
的 COUNT(distinct) 的计数聚集函数来体现名次了。 SELECT 1+(SELECT COUNT(distinct [平均成绩])
FROM (SELECT [学生ID],MAX([学生姓名]) AS 学生姓名 ,AVG([成绩]) AS [平均成绩]
FROM T
GROUP BY [学生ID]
) AS T1
WHERE [平均成绩] > T2.[平均成绩]) as 名次,
[学生ID],[学生姓名],[平均成绩]
FROM (SELECT [学生ID],max([学生姓名]) AS 学生姓名,AVG([成绩]) AS [平均成绩]
FROM T
GROUP BY [学生ID]
) AS T2
ORDER BY T2.[平均成绩] desc 方法二也使用了两个完全相同的自相关子查询生成两个派生表作为基本表,再利用它
们之间作大于的相关子查询取 COUNT(distinct) + 1 的计数聚集函数同样实现了名
次的显示。 这道题从应用角度来看,查询结果是相当合理的,并列情况的名次也都一样。但如果想
实现类似自动序列的行号,该解决方案的局限性突显,不能处理并列相等的情况了,所
以有必要强调:一定要选择不重复的连接条件,可以根据实际情况利用字段组合的不等
连接 (T1.f1 + ... + T1.fn <= T2.f1 + ... + T2.fn)。继续引申还可以通过判断
COUNT(distinct) % 2 是否为 0 的 HAVING 或 WHERE 子句实现只显示偶数或奇数行: HAVING count(distinct b.f) % 2 = 1
或:
WHERE 1+(SELECT COUNT(distinct [平均成绩])
FROM (SELECT [学生ID],MAX([学生姓名]) AS 学生姓名 ,AVG([成绩]) AS [平均成绩]
FROM T
GROUP BY [学生ID]
) AS T1
WHERE [平均成绩] > T2.[平均成绩]) % 2 =1 再简单说一下 HAVING 和 WHERE 在含有 GROUP BY 分组的查询中的区别,HAVING 是
在数据分组后才筛选记录的,WHERE 是先进行筛选在分组的,而且 HAVING 一般应与聚
集函数合用才有真正含义。 两种方法再次体现了子查询与连接可以殊途同归之妙,第二种子查询方法值得推荐,因
为比较利于程序构造,便于为没有该功能的原有查询添加此项功能。本题仅仅是为了示
范一种比较新颖的解题思路,回避了效率的问题。
from
(select 1 as id,2 as a
union all
select 2,5
union all
select 3,5
union all
select 4,6
union all
select 5,3) T
select count(distinct T1.a),T1.id,min(T1.a)
from
(select *
from #test
) as T1
,
(select *
from #test
) as T2
where T1.a <= T2.a
group by T1.id
order by count(*) SELECT 1+(SELECT COUNT(distinct a)
FROM (select *
from #test) AS T1
WHERE a > T2.a) as 名次 ,id,a
FROM (select *
from #test
) AS T2
ORDER BY T2.a desc