まず第一に:あなたが望む機能は と と呼ばれsliding window
ますranking
。Oracle は、これらをOVER
-keyword と -function で実装しrank()
ます。MySQL はこれらの機能をサポートしていないため、これを回避する必要があります。
この回答を使用して、次のクエリを作成しました。+1
これがあなたに役立つなら、彼にも与えてください。
SELECT
`player_id`, `event`, `points`,
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) AS `rank`
FROM
`points` `l`
これは、のすべてのランクを出力player_id
しevent
ますpoints
。例: あると仮定すると、出力は次のようになります(player_id, event, points)
。(1,A,10), (1,A,5), (1,A,2), (1,A,2), (1,A,1), (2,A,0)
player_id event points rank
1 A 10 1
1 A 5 2
1 A 2 3
1 A 2 3
1 A 1 5
2 A 0 1
ランクは密集していないため、重複するタプルがある場合、同じランクの出力タプルとランク番号のギャップが生じます。
それぞれのトップN
* タプルを取得するには、ビューを作成するか、条件でサブクエリを使用できます。ビューは推奨される方法ですが、多くのサーバーでビューを作成する権限がありません。player_id
event
rank
as 列を含むビューを作成します。
CREATE VIEW `points_view`
AS SELECT
`player_id`, `event`, `points`,
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) as `rank`
FROM
`points` `l`
ビューから目的の上位 N 件の結果を取得します。
SELECT
`player_id`, `event`, `points`
FROM `points_view`
WHERE
`event` = 'A' AND `rank` <= 5
OR
`event` = 'B' AND `rank` <= 2
OR
`event` = 'C' AND `rank` <= 3
状態でのランクの使い方
SELECT
`player_id`, `event`, `points`
FROM
`points` `l`
WHERE
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) <= N
イベントに応じて異なる量のタプルをさらに取得するには、次のようにします。
SELECT
`player_id`, `event`, `points`
FROM
`points` `l`
WHERE
`event` = 'A' AND
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) <= 5
OR
`event` = 'B' AND
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) <= 2
OR
`event` = 'C' AND
(SELECT 1 + count(*)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` > `l`.`points`
) <= 3
N
MySQL はこのクエリを最適化せず、3 つの独立した従属サブクエリが発生するため、最大の 5 を使用し、他のイベント タイプの他のタプルを無視します。パフォーマンスが問題にならない場合、またはデータがあまりない場合は、そのままにしておいてください。
* 説明したように、rank
は密集していないため、 ですべてのタプルを取得するrank <= N
と、通常は 2 つ以上のN
タプルになります。追加のタプルは重複しています。
例の表からわかるように、単純に重複を削除するのは悪い考えです。と の上位 5 つの結果が必要な場合は、両方のタプルが必要になりplayer_id = 1
ます。どちらもランク 3 です。しかし、どちらかを削除すると、結果は上位 4 つだけになります, , .event = A
(1,A,2)
(1,A,10,1)
(1,A,5,2)
(1,A,2,3)
(1,A,1,5)
密なランクを取得するには、このサブクエリを使用できます
(SELECT count(DISTINCT `points`)
FROM `points`
WHERE `l`.`player_id` = `player_id`
AND `l`.`event` = `event`
AND `points` >= `l`.`points`
) as `dense_rank`
これでもランクが重複するので注意してください。
編集
すべてのイベントのポイントを 1 つのスコアに合計するには、次を使用します。GROUP BY
SELECT
`player_id`, SUM(`points`)
FROM `points_view`
WHERE
`event` = 'A' AND `rank` <= 5
OR
`event` = 'B' AND `rank` <= 2
OR
`event` = 'C' AND `rank` <= 3
GROUP BY `player_id`
ORDER BY SUM(`points`) DESC
パーティショニング ( GROUP BY
) の前に、結果には正しい量のトップスコアが含まれているため、すべてのポイントを単純に合計できます。
ここで直面している大きな問題は、ツールがandのそれぞれに対して正確に 5 つのタプルを取得することも、ツールに与えないことrank
です。例: 誰かがイベント A で 1 ポイントの 1000 倍を獲得した場合、すべてのポイントが と を獲得するため、最終的に 1000 ポイントになります。dense_rank
player_id
event
rank
dense_rank
1
ただし、ROWNUM
MySQL はこれをサポートしていないため、これをエミュレートする必要があります。問題ROWNUM
は、すべてのタプルに対して合成数値が生成されることです。player_id
しかし、 、のグループの合成数が必要ですevent
。私はまだこの解決策に取り組んでいます。
編集2
この回答を使用して、このソリューションが機能することがわかりました:
select
player_id, sum( points )
from
(
select
player_id,
event,
points,
/* increment current_pos and reset to 0 if player_id or event changes */
@current_pos := if (@current_player = player_id AND
@current_event = event, @current_pos, 0) + 1 as position,
@current_player := player_id,
@current_event := event
from
(select
/* global variable init */
@current_player := null,
@current_event := null,
@current_pos := 0) set_pos,
points
order by
player_id,
event,
points desc
) pos
WHERE
pos.event = 'A' AND pos.position <= 5
OR
pos.event = 'B' AND pos.position <= 2
OR
pos.event = 'C' AND pos.position <= 3
GROUP BY player_id
ORDER BY SUM( points ) DESC
内部クエリは (player_id, event, points)-タプルを選択し、それらを player_id と event でソートし、最後に各タプルに合成数を与えます。これは、player_id またはイベントが変更されるたびに 0 にリセットされます。順序のため、同じ player_id を持つすべてのタプルは連続します。外側のクエリは、以前に使用されたクエリがビューに対して行うのと同じことを行います。
Edit3 (コメントを参照)
OLAP ROLLUP 演算子を使用して、中間合計または異なる種類のパーティションを作成できます。クエリは、たとえば次のようになります。
select
player_id, event, sum( points )
from
(
select
player_id,
event,
points,
/* increment current_pos and reset to 0 if player_id or event changes */
@current_pos := if (@current_player = player_id AND
@current_event = event, @current_pos, 0) + 1 as position,
@current_player := player_id,
@current_event := event
from
(select
/* global variable init */
@current_player := null,
@current_event := null,
@current_pos := 0) set_pos,
points
order by
player_id,
event,
points desc
) pos
WHERE
pos.event = 'A' AND pos.position <= 5
OR
pos.event = 'B' AND pos.position <= 2
OR
pos.event = 'C' AND pos.position <= 3
GROUP BY player_id, event WITH ROLLUP
/* NO ORDER BY HERE. SEE DOCUMENTATION ON MYSQL's ROLLUP FOR REASON */
結果は、最初に でグループ化されplayer_id, event
、次に のみplayer_id
で、最後に null (すべての行を合計) でグループ化されます。
最初のグループは次のようになります。ここで、20 と 5 はと(player_id, event, sum(points)) = {(1, A, 20), (1,B,5)}
に関するポイントの合計です。2 番目のグループは次のようになります。25 は、 に関するすべてのポイントの合計です。それが役立つことを願っています。:-)player_id
event
(player_id, event, sum(points)) = {(1,NULL,25)}
player_id