2

次の列を含むテーブル:

Player_id (primary key), Event_type(A,B,C), Points.

event_type ごとに 1 人のプレイヤーが何度も登場する可能性があります

いくつかの条件を入れながら、すべてのイベントタイプから DESC SUM(Points) GROUP BY player_id で総合ランキングを表示したいと思います。

  • イベント タイプ A の player_id ごとに最高の 5 つの結果のみ
  • イベント タイプ B の player_id ごとに最高の 2 つの結果のみ
  • イベント タイプ C の player_id ごとにベスト 3 の結果のみ

私は無駄に試しました:

SUM(points) WHERE event_type ="X" 
GROUP BY Player_id ORDER BY SUM(points) LIMIT N

サブクエリ、UNION、または一時テーブルを含めることになるとかなり混乱して、1週間この頭痛と戦ってきました。全てのパーツを揃える方法がわかりません...

私の夢は、クリックするとプレーヤーごとの詳細なポイントの内訳にアクセスできるようにして、この全体的なランキングを実行することです....

これに関するあらゆる種類のヘルプを受け入れます...ありがとう!

ソーステーブルの例:

player_id------event_type-------スコア-----

---1 ----------------------A---------------- 5 ---------- -

---1 ----------------------A ------- 10 ----------

---1 ----------------------A---------------- 5 ----------

---1-------A----------------5---------------------

---1-------A----------------2---------------------

---1 ----------------------A---------------- 15 ---------

---1 ----------------------A---------------- 10 ---------

---1-------C----------------20---------------------

---1 ----------------------B---------------- 5 ---------

---1---------------------B----------------5---------------------

---1 ----------------------B---------------- 20 ---------

---2-------------------A---------------- 50 ---------

---2 ----------------------B---------------- 55 ----------

この例に従った望ましい出力:

ランク---player_id-------overall_score-----

----1-------------2----------- 105 ポイント[A から 50 (ベスト 5) + B から 55 (ベスト 2)]---- -----

----2----------1----------- 90 ポイント[A から 45 (ベスト 5) + C から 20 (ベスト 3) + B から 25 (ベスト) 2)]---------

4

2 に答える 2

3

まず第一に:あなたが望む機能は と と呼ばれ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_ideventます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_idevent

rankas 列を含むビューを作成します。

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

NMySQL はこのクエリを最適化せず、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_rankplayer_ideventrankdense_rank 1

ただし、ROWNUMMySQL はこれをサポートしていないため、これをエミュレートする必要があります。問題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_idevent(player_id, event, sum(points)) = {(1,NULL,25)}player_id

于 2012-04-13T01:50:42.593 に答える
0

おそらく合計(ポイント)に名前を付ける必要があります。

そうする:

select player,sum(points) as points from table where event_type = "x" group by player order by points desc limit 5;

(これをドロップインできるものとして記述するには、正確なテーブルスキーマを確認する必要がありますが、これが要点です)

于 2012-04-13T01:25:04.150 に答える