3

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

4

3 回答 3

3

你的导师告诉你有两个原因。

  1. 数据模型。关系 DBMS 处理集合,而不是列表。如果您正在学习 SQL,那么您最好考虑无序的元组集合,而不是顺序列表。您将更好地了解如何查询 DBMS。我认为您的解决方案是一种 hack:一个部分有效的解决方案,因为 - 正如 Perun_x 指出的那样 - 如果多个元组匹配结果,它就不起作用。与 SQL 的数据模型和精神背道而驰)。

  2. 可移植性。这才是真正的杀手。您的代码可以在 Oracle 上运行,但不能在不支持 row_number 属性的其他 DBMS 上运行(每个都有自己的方法)。

--dmg

于 2013-04-28T17:42:53.940 回答
2

查询不等价。第一个查询总是选择 1 行。第二个选择平均值最高的所有行(理论上可以有更多这样的行)。

于 2013-04-28T16:34:14.967 回答
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()以获得最佳行之一或所有最佳行。

于 2013-04-28T17:11:51.643 回答