MySQLテーブルのランクを更新します
質問
テーブルプレーヤー用の次のテーブル構造があります
Table Player {
Long playerID;
Long points;
Long rank;
}
PlayerIDとポイントに有効な値があると仮定すると、1つのクエリのポイント数に基づいてすべてのプレーヤーのランクを更新できますか? 2人が同じ数のポイントを持っている場合、彼らはランクのために結びつけるべきです。
アップデート:
ネイティブクエリとして提案されたクエリを使用して、冬眠を使用しています。 Hibernateは、変数、特に「:」の使用が好きではありません。誰かが回避策を知っていますか?変数を使用しないか、HQLを使用してこの場合のHibernateの制限を回避することにより?
解決
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)
注:私が提案しているクエリをさらに簡素化できることを考慮してください。
他のヒント
ダニエル、あなたはとても素敵な解決策を持っています。 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;
編集:以前に提示された更新ステートメントは機能しませんでした。
これは正確にあなたが求めているものではありませんが、選択するときにその場でランクを生成できます。
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
編集:更新ステートメントをもう一度試してください。一時的なテーブルはどうですか:
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;
お役に立てれば。
によると 正規化ルール, 、ランクは選択時に評価する必要があります。