Oracle ORDER BY使用rownum或HAVING> = ALL

时间:2013-04-28 15:28:16

标签: sql select average having rownum

我的数据库老师要我写(在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)吗? 感谢。

3 个答案:

答案 0 :(得分:3)

您的导师告诉您这有两个原因。

  1. 数据模型。关系DBMS处理集合,而不是列表。如果您正在学习SQL,那么您最好使用无序的元组集来思考,而不是在顺序列表中进行思考。您将更好地了解如何查询DBMS。我认为你的解决方案是一个黑客攻击:部分工作,因为 - 正如Perun_x所指出的那样 - 如果多个元组与结果匹配,它就不起作用。这与SQL的数据模型和精神相反。

  2. 可移植性。这是真正的杀手。您的代码将适用于Oracle,但不支持其他不支持row_number属性的DBMS(每种DBMS都有自己的方法)。

  3. - 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行或所有最佳行。