2

Light Athletic の大会結果の表があります。プレイヤーは上位 3 位までポイントを獲得します。陸上競技のポイントの名前のリストが必要です。私はこれを持っています:

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;

私はほぼ同じ冗長コードを3回持っています。これが場所に依存していない場合は、ビューを使用できます...

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の回答で、両方の場所が逆になっていることです。つまり、一番長く走った男が1位!?

つまり、あなたのフィドルには次のものがありました:

-- 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 に答える