8

テーブルプレーヤーの次のテーブル構造があります

Table Player {  
Long playerID;  
Long points;  
Long rank;  
}

playerID とポイントが有効な値を持っていると仮定すると、1 回のクエリでポイント数に基づいてすべてのプレイヤーのランクを更新できますか? 2 人が同じポイント数を持っている場合、その順位は同点でなければなりません。

アップデート:

ネイティブクエリとして提案されたクエリを使用して休止状態を使用しています。Hibernate は変数、特に ':' の使用を好みません。誰かが回避策を知っていますか? この場合、変数を使用しないか、HQL を使用して hibernate の制限を回避しますか?

4

4 に答える 4

17

1 つのオプションは、次のようなランキング変数を使用することです。

UPDATE   player
JOIN     (SELECT    p.playerID,
                    @curRank := @curRank + 1 AS rank
          FROM      player p
          JOIN      (SELECT @curRank := 0) r
          ORDER BY  p.points DESC
         ) ranks ON (ranks.playerID = player.playerID)
SET      player.rank = ranks.rank;

このパーツでは、別のコマンドJOIN (SELECT @curRank := 0)を必要とせずに変数を初期化できます。SET

このトピックに関する詳細情報:


テストケース:

CREATE TABLE player (
   playerID int,
   points int,
   rank int
);

INSERT INTO player VALUES (1, 150, NULL);
INSERT INTO player VALUES (2, 100, NULL);
INSERT INTO player VALUES (3, 250, NULL);
INSERT INTO player VALUES (4, 200, NULL);
INSERT INTO player VALUES (5, 175, NULL);

UPDATE   player
JOIN     (SELECT    p.playerID,
                    @curRank := @curRank + 1 AS rank
          FROM      player p
          JOIN      (SELECT @curRank := 0) r
          ORDER BY  p.points DESC
         ) ranks ON (ranks.playerID = player.playerID)
SET      player.rank = ranks.rank;

結果:

SELECT * FROM player ORDER BY rank;

+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
|        3 |    250 |    1 |
|        4 |    200 |    2 |
|        5 |    175 |    3 |
|        1 |    150 |    4 |
|        2 |    100 |    5 |
+----------+--------+------+
5 rows in set (0.00 sec)

更新:同じランクを共有するには同点が必要であることに気付きました。これは少しトリッキーですが、さらに多くの変数を使用して解決できます。

UPDATE   player
JOIN     (SELECT    p.playerID,
                    IF(@lastPoint <> p.points, 
                       @curRank := @curRank + 1, 
                       @curRank)  AS rank,
                    @lastPoint := p.points
          FROM      player p
          JOIN      (SELECT @curRank := 0, @lastPoint := 0) r
          ORDER BY  p.points DESC
         ) ranks ON (ranks.playerID = player.playerID)
SET      player.rank = ranks.rank;

テスト ケースとして、175 ポイントの別のプレーヤーを追加してみましょう。

INSERT INTO player VALUES (6, 175, NULL);

結果:

SELECT * FROM player ORDER BY rank;

+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
|        3 |    250 |    1 |
|        4 |    200 |    2 |
|        5 |    175 |    3 |
|        6 |    175 |    3 |
|        1 |    150 |    4 |
|        2 |    100 |    5 |
+----------+--------+------+
6 rows in set (0.00 sec)

また、同点の場合にランクをスキップする必要がある場合は、別のIF条件を追加できます。

UPDATE   player
JOIN     (SELECT    p.playerID,
                    IF(@lastPoint <> p.points, 
                       @curRank := @curRank + 1, 
                       @curRank)  AS rank,
                    IF(@lastPoint = p.points, 
                       @curRank := @curRank + 1, 
                       @curRank),
                    @lastPoint := p.points
          FROM      player p
          JOIN      (SELECT @curRank := 0, @lastPoint := 0) r
          ORDER BY  p.points DESC
         ) ranks ON (ranks.playerID = player.playerID)
SET      player.rank = ranks.rank;

結果:

SELECT * FROM player ORDER BY rank;

+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
|        3 |    250 |    1 |
|        4 |    200 |    2 |
|        5 |    175 |    3 |
|        6 |    175 |    3 |
|        1 |    150 |    5 |
|        2 |    100 |    6 |
+----------+--------+------+
6 rows in set (0.00 sec)

注: 私が提案しているクエリはさらに単純化できることを考慮してください。

于 2010-04-28T06:25:15.557 に答える
6

ダニエル、あなたにはとても素晴らしい解決策があります。1点を除いて-ネクタイケース。3人のプレイヤー間で同点が発生した場合、このアップデートは正しく機能しません。私はあなたの解決策を次のように変更しました:

UPDATE player  
    JOIN (SELECT p.playerID,  
                 IF(@lastPoint <> p.points,  
                    @curRank := @curRank + @nextrank,  
                    @curRank)  AS rank,  
                 IF(@lastPoint = p.points,  
                    @nextrank := @nextrank + 1,  
                    @nextrank := 1),  
                 @lastPoint := p.points  
            FROM player p  
            JOIN (SELECT @curRank := 0, @lastPoint := 0, @nextrank := 1) r  
           ORDER BY  p.points DESC  
          ) ranks ON (ranks.playerID = player.playerID)  
SET player.rank = ranks.rank;
于 2011-10-22T14:42:23.537 に答える
3

編集: 以前に提示された更新ステートメントは機能しませんでした。

これはまさにあなたが求めているものではありませんが: 選択するとその場でランクを生成できます:

select p1.playerID, p1.points, (1 + (
    select count(playerID) 
      from Player p2 
     where p2.points > p1.points
    )) as rank
from Player p1
order by points desc

編集: UPDATE ステートメントをもう一度試します。一時テーブルはどうですか:

create temporary table PlayerRank
    as select p1.playerID, (1 + (select count(playerID) 
                                   from Player p2 
                                  where p2.points > p1.points
              )) as rank
         from Player p1;

update Player p set rank = (select rank from PlayerRank r 
                             where r.playerID = p.playerID);

drop table PlayerRank;

お役に立てれば。

于 2010-04-28T06:06:32.187 に答える
0

正規化ルールに従って、ランクは SELECT 時に評価する必要があります。

于 2010-04-28T06:08:38.240 に答える