2

我有 Light Athletic 比赛结果的表格。玩家获得前三名的积分。我需要列出他们的点的田径运动员名称。我有这个:

SELECT NAME, SUM(many) as sum FROM
(
(SELECT NAME, count(*) * (SELECT points from "points for place" where place = 1)  as many
FROM RESULTS R1 WHERE
(SELECT count(*) FROM RESULTS R2 WHERE
    R1.result < R2.result
    AND R1.DISCIPLINE = R2.DISCIPLINE
    AND R1.CITY = R2.CITY) = 0
GROUP BY NAME)
UNION
(SELECT NAME, count(*) * (SELECT points from "points for place" where place = 2)  as many
FROM RESULTS R1 WHERE
(SELECT count(*) FROM RESULTS R2 WHERE
    R1.result < R2.result
    AND R1.DISCIPLINE = R2.DISCIPLINE
    AND R1.CITY = R2.CITY) = 1
GROUP BY NAME)
UNION
(SELECT NAME, count(*) * (SELECT points from "points for place" where place = 3)  as many
FROM RESULTS R1 WHERE
(SELECT count(*) FROM RESULTS R2 WHERE
    R1.result < R2.result
    AND R1.DISCIPLINE = R2.DISCIPLINE
    AND R1.CITY = R2.CITY) = 2
GROUP BY NAME)
)
GROUP BY NAME ORDER BY SUM;

我有三倍几乎相同的冗余代码。如果这不取决于地方,我可以使用 View ...

4

2 回答 2

3

我认为您可以从根本上简化为:

SELECT name, sum(p.points) AS total_points
FROM  (
    SELECT r1.name
          ,sum((r1.result > r2.result)::int) + 1 AS place
    FROM   results r1
    LEFT   JOIN results r2 USING (discipline, city)
    GROUP  BY 1
    HAVING sum((r1.result > r2.result)::int) < 3
    ) r
JOIN   "points for place" p USING (place)
GROUP  BY r.name
ORDER  BY total_points;

这是用PostgreSQL测试的。
由于 OP 后来宣布 Oracle,这是 Oracle 的另一个版本

SELECT name, SUM(p.points) AS total_points
FROM  (
   SELECT r1.name
         ,SUM(CASE WHEN r1.result > r2.result THEN 1 ELSE 0 END) + 1 AS place
   FROM   results r1
   LEFT   JOIN results r2 USING (discipline, city)
   GROUP  BY r1.name
   HAVING SUM(CASE WHEN r1.result > r2.result THEN 1 ELSE 0 END) < 3
) r
JOIN   "points for place" p USING (place)
GROUP  BY r.name
ORDER  BY total_points;

应该提供与问题中的查询相同的结果。只有更快更简单。
-> sqlfiddle 并排显示新查询和原始查询。

现在我知道这个查询是关于什么的:dense_rank()显然是更好的解决方案。

要点

  • 对于 Postgres,计算低于sum((r1.result < r2.result)::int)的次数。转换为 TRUE 为 1,FALSE 为 0。 对于 Oracle,此表达式的作用相同:r1.resultr2.resultbooleaninteger

     SUM(CASE WHEN r1.result < r2.result THEN 1 ELSE 0 END)
    
  • 我添加1到该计数并命名它以从表place中获取它。points"points for place"

于 2013-02-07T23:38:44.883 回答
2

您可以通过以下方式简化此操作:

select r.name, 
       sum(p.points) total_points
  from (select city, discipline, name, 
               row_number() over (partition by city, discipline order by result) place
         from results r) r
       inner join "points for place" p
               on p.place = r.place
 where p.place <= 3 
 group by r.name
 order by total_points desc;

* 注意:如果有平局,并且您希望两者都算作那个地方,请使用dense_rank()而不是row_number()

考虑到您的测试数据,这也where p.place <= 3可能是多余的,因为您只有 3 个得分点……所以您可以省略它。我已经把它留在了。

但我感到困惑的是,在你原来的 SQL 和 Erwins 的回答中,你们都颠倒了这些地方。即跑得最长的人是第一名?!

即在你的小提琴中你有:

-- RESULTS --
INSERT INTO results VALUES
('9.87', 'Doha', '100m', 'Justin GATLIN');
...
INSERT INTO results VALUES
('10.28', 'Doha', '100m', 'Jimmy VICAUT');

现在我在 9.87 秒内读到贾斯汀赢了。但是你们都认为这是吉米的胜利。

如果是这样,那么分析应该是

partition by city, discipline order by result desc
于 2013-02-08T01:55:02.900 回答