まず最初に、各タイプの最大ダメージを選択する select を構築する必要があります (すでに持っています):
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
さて、次の場合を除き、これは良いパフォーマンスを発揮しません。
- type is
INT
(またはENUM
その他の数値型)
type
またはにインデックスがありますtype, damage
MySQL は一致するpokemon_name
ことを保証しないため、選択できません(これは、この問題を既にカバーしている stackoverflow に関する優れた回答です)。pokemon_name
MaxD
これで、その一致するポケモンを選択できますpokemon_name
SELECT p1.pokemon_name, p1.type, p1.damage
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
GROUP BY (p1.type, p1.damage)
最後のGROUP BY
ステートメントは、同じ攻撃ダメージを持つ複数のポケモンを持っていても、1 つのtype,damage
ペアに対して複数のレコードが発生しないことを確認します。
繰り返しますが、 に置き換えることpokemon_name
で優れたパフォーマンスが得られpokemon_id
ます。[ウィキペディア]、[最初のチュートリアル]database normalization
でしばらくの間、Google で検索する必要があるかもしれません。また、この Q&Aもチェックしてみてください。「リレーション テーブル」の意味を簡単に説明しています。
これで正しいことがわかりpokemon_name
ました(プログラムのために、これを に置き換えてpokemon_id
ください)、すべてをまとめることができます:
SELECT p1.pokemon_name, p1.type, p1.damage, p.*
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
INNER JOIN Pokemon p
ON p.pokemon_name = p1.pokemon_name
GROUP BY (p1.type, p1.damage)
理想例
完璧な世界では、データベースは次のようになります。
-- Table with pokemons
CREATE TABLE `pokemons` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255),
-- More fields
PRIMARY KEY (`id`)
)
-- This contains pairs as (1,'Wather'), (2, 'Flame'), ...
CREATE TABLE `AttackTypes` (
`id`,
`name` VARCHAR(255)
)
-- Create records like (1, 2, 3, 152)
-- 1 = automatically generated keys
-- 2 = id of pokemon (let say it's Pikachu :P)
-- 3 = type of attack (this say it's Electric)
-- 152 = damage
-- This way each pokemon may have multiple attack types (Charizard flame + wind)
CREATE TABLE `Attacks` (
`id`,
`pokemonID` INT NOT NULL, -- Represents pokemons.id
`typeID` INT NOT NULL, -- Represents attack.id
`damage` INT
)
ID フィールドはALWAYS PRIMARY KEY
でNOT NULL
、AUTO_INCREMENT
この例では
そして、そこから選択して、最初に型を取得します。
SELECT MAX(attack.damage) AS mDmg, attack.typeID
FROM attack
GROUP BY attack.typeID
ポケモンIDを取得するより:
SELECT a.pokemonID, a.damage, a.typeID
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.typeID
FROM attack AS a
GROUP BY a.typeID
) AS maxA
ON a.typeID = maxA.typeID
AND a.damage = mDmg
GROUP BY (a.typeID)
そして、すべてをカバーしたら、実際にポケモンデータを選択できます
SELECT aMax.pokemonID as id,
aMax.damage,
p.name AS pokemonName,
aMax.typeID AS attackTypeID,
t.name AS attackType
FROM (
SELECT a.pokemonID, a.damage, a.type
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.type
FROM attack AS a
GROUP BY a.type
) AS maxA
ON a.type = maxA.type
AND a.damage = mDmg
GROUP BY (a.type)
) AS aMax
INNER JOIN pokemons AS p
ON p.id = aMax.pokemonID
INNER JOIN AttackTypes AS t
ON t.id = aMax.typeID
パフォーマンスのヒント:
MaxDamage
フィールドを追加してAttackTypes
(ストアドプロシージャによって計算されます)、ネストされたクエリの1レベルを節約できます
- すべての
ID
フィールドはPRIMARY KEY
sでなければなりません
- インデックスをオンに
Attacks.typeID
すると、そのタイプの攻撃が可能なすべてのポケモンをすばやく取得できます
- インデックスをオンに
Attack.damage
すると、最強の攻撃をすばやく見つけることができます
- (2 つのフィールド) のインデックスは
Attack.type, Attack.damage
、各攻撃の最大値を見つけるときに役立ちます
- インデックスをオンに
Attack.pokemonID
すると、検索がpokemon -> attack -> attack type name
高速になります