我的数据库老师要我写(在Oracle服务器上)一个查询:选择2010年平均得分最高的groupid
我写道:
SELECT * FROM (
SELECT groupid, AVG(score) average FROM points
WHERE yr = 2010
AND score IS NOT NULL
GROUP BY groupid
ORDER BY average DESC
) WHERE rownum = 1;
我的老师告诉我这个要求“更好”:
SELECT groupid, AVG(score) average FROM points
WHERE yr = 2010
GROUP BY groupid
HAVING AVG(score) >= ALL (
SELECT AVG(score) FROM points
WHERE yr = 2010
GROUP BY groupid
);
哪一个最快/更好?还有更好的解决方案(仅限Oracle)吗? 感谢。
答案 0 :(得分:3)
您的导师告诉您这有两个原因。
数据模型。关系DBMS处理集合,而不是列表。如果您正在学习SQL,那么您最好使用无序的元组集来思考,而不是在顺序列表中进行思考。您将更好地了解如何查询DBMS。我认为你的解决方案是一个黑客攻击:部分工作,因为 - 正如Perun_x所指出的那样 - 如果多个元组与结果匹配,它就不起作用。这与SQL的数据模型和精神相反。
可移植性。这是真正的杀手。您的代码将适用于Oracle,但不支持其他不支持row_number属性的DBMS(每种DBMS都有自己的方法)。
- DMG
答案 1 :(得分:2)
查询不等同。第一个查询总是选择1行。第二个选择平均值最高的所有行(理论上可以有更多这样的行)。
答案 2 :(得分:2)
我碰巧更喜欢你的版本,假设一行足以满足你的需要。我对教师版本的问题主要是可读性。我发现很难解析。
您的版本基本上是说“按照平均值对群组进行排序,然后选择平均值最高的群组。”教师版本基本上是这样说:“找出大于或等于任何平均组的平均值”。这可能是主观的,但我发现前者比后者更容易理解。
哪个更快。您需要进行聚合和排序以获得最佳价值。第二个版本需要进行两次聚合和连接。我认为聚合/排序方法会更快,但真正了解的唯一方法是检查特定系统配置和数据集的性能。
在性能方面应与您的大致相当的另一种表述是:
select groupid, avgscore
from (select groupid, avg(score) as avgscore,
row_number() over (order by avg(score) desc) as seqnum
from points
where yr = 2010
group by groupid
) t
where seqnum = 1
此处的优点是您可以将row_number()
更改为dense_rank()
,以获得最佳行中的1行或所有最佳行。