0

次の列を持つテーブルがあります: match_id、player_slot、能力、レベル

試合ごとに 10 人のプレイヤー スロットがあり、各ヒーローは最大 25 のレベルと 5 つの能力のいずれかを持つことができます。API からデータをプルしていますが、プルを高速にする必要があるため、テーブルの設定方法にあまり柔軟性がありません。

ただし、データを表示するために、既存のテーブル形式を実行可能なものに変えるのに本当に苦労しています。試合ごとに、データを次のように配置したいと思います。

プレーヤー スロット 1、レベル 1 の能力、レベル 2 の能力、...、レベル 16 の能力

...

プレーヤー スロット 10、レベル 1 の能力、レベル 2 の能力、...、レベル 16 の能力

実際の例 (ここに例があります) を取得できますが、それは本当に醜いクエリです。行ごとに 15 個のネストされたサブクエリがあり、UNION を実行して 10 行のそれぞれを結合します。

クエリのスニペット:

SELECT player_slot, ability,
(SELECT ability FROM api_abilities WHERE match_id = 119009486 AND player_slot = 1 AND level = 2 ),
...
(SELECT ability FROM api_abilities WHERE match_id = 119009486 AND player_slot = 1 AND level = 16 )
FROM api_abilities
WHERE match_id = 119009486 AND player_slot = 1 AND level = 1

これを単純化するにはどうすればよいですか?このデータを別の方法で表示するビューまたは新しいテーブルを作成する必要がありますか? また、これを複雑にしているのは、各プレイヤーが異なるレベルでゲームを終了することです。私が今持っているものは機能しますが、クエリを実行するためのより効率的な方法が必要なようです。いくつかの異なることを試しましたが、他のクエリで行の統合を正しく取得できないようです。

4

3 に答える 3

1

このタイプの変換はピボットです。残念ながら、MySQL にはピボット関数がないため、複数の結合または集計関数を使用して複製する必要があります。

サブクエリの代わりに複数の結合を使用できます。

SELECT 
  l1.player_slot, 
  l1.ability Level1Ability,
  l2.ability Level2Ability,
  l16.ability Level16Ability
FROM api_abilities l1
LEFT JOIN api_abilities l2
  on l1.match_id = l2.match_id 
  and l1.player_slot = l2.player_slot
  and l2.level = 2
-- .... add more joins for each level
LEFT JOIN api_abilities l16
  on l1.match_id = l16.match_id 
  and l1.player_slot = l16.player_slot
  and l16.level = 16
WHERE l1.match_id = 119009486 
  AND l1.player_slot >= 1
  AND l1.player_slot <= 10
  AND l1.level = 1

CASEまたは、式で集計関数を使用できます。

select
  player_slot,
  max(case when level = 1 then ability end) Level1Ability,
  max(case when level = 2 then ability end) Level2Ability,
  -- ... add more case expressions for each level
FROM api_abilities 
where match_id = 119009486 
  and player_slot >= 1
  and player_slot <= 10
group by player_shot
于 2013-02-11T21:00:03.403 に答える
1

1 つのアプローチを次に示します。

SELECT a.player_slot
     , MAX(IF(a.level=1,a.ability,NULL)) AS ability_at_level_1
     , MAX(IF(a.level=2,a.ability,NULL)) AS ability_at_level_2
     , MAX(IF(a.level=3,a.ability,NULL)) AS ability_at_level_3
     , MAX(IF(a.level=4,a.ability,NULL)) AS ability_at_level_4
     , MAX(IF(a.level=5,a.ability,NULL)) AS ability_at_level_5
   ...   
     , MAX(IF(a.level=15,a.ability,NULL)) AS ability_at_level_15
     , MAX(IF(a.level=16,a.ability,NULL)) AS ability_at_level_16
  FROM api_abilities a
WHERE a.match_id = 119009486
  AND a.player_slot >= 1
  AND a.player_slot <= 10
GROUP
   BY a.player_slot

クエリが複数の を返すようにするmatch_id場合は、GROUP BY句に , のmatch_id前に列を含める必要がありますplayer_slot(また、SELECT リストに含めることもできます)。

GROUP
   BY a.match_id
    , a.player_slot

最高のパフォーマンスを得るには、適切なインデックスが必要です。カバリング インデックスを使用すると、(おそらく) 最高のクエリ パフォーマンスが得られます。

ON api_abilities (match_id, player_slot, level, ability)

これが適切なインデックスである理由は、列に等値述語があり、match_id列に範囲述語と GROUP BY があるためplayer_slotです。(このインデックスは、player_slot の範囲述語が削除された場合でも適切です。)levelおよびability列を含めることは厳密には必要ではありませんが、それらを含めるとインデックスが「カバリング インデックス」になります。これは、インデックスの基になるデータ ページにアクセスする必要なく、インデックスからクエリを完全に満たすことができるため (EXPLAIN の出力には「Using index」と表示されます)、「カバリング インデックス」と呼ばれます。

MAX 集計と IF 関数を使用することで、適切な行から a.ability を「選択」することができます。(「トリック」は、指定されたレベルにないすべての行に対して、IF 関数が NULL を返すことです。MAX で実際に行っていることは、それらの NULL 値を捨てることです。

が一意の場合(match_id, player_slot, level)、その MAX 集計関数は 1 つのa.ability値と一連の NULL で動作します。より一般的なケースでは、一意であることが保証されていない場合、MAX 集計が 2 つ (またはそれ以上) の非 NULLa.ability値で動作する可能性があります。a.abilityそのより一般的なケースでは、各レベルの (文字列として) 短いリストを返したい場合は、GROUP_CONCAT 集計関数が (MAX の代わりに) 役立つ場合があります。

于 2013-02-11T21:00:26.883 に答える
0

データベースの正規化について理解を深める必要があるように思えます。あなたの説明に基づいてここで何をしようとしているのかはまだ正確にはわかりませんが、このデータは複数のテーブル (試合用、プレーヤー用、能力用、関連プレーヤー用) で表す必要があることは明らかです。能力と能力レベル、プレイヤーを試合に関連付けるものなど)。

于 2013-02-11T20:59:25.917 に答える