我有一个包含以下列的表:match_id,player_slot,ability,level
每场比赛有10个玩家老虎机,每个英雄最多可以有25个等级和5个异能中的一个。我从API中提取数据并需要快速提升,因此我对表的设置方式不具备很大的灵活性。
然而,为了显示数据,我真的很难将现有的表格格式变成可行的东西。对于每场比赛,我希望将数据布局如下:
玩家slot1,等级1的能力,等级2的能力,......等级16的能力
...
玩家slot10,1级能力,2级能力,...... 16级能力
我可以得到一个有效的例子(Here's an example),但这是一个非常丑陋的查询。对于每一行,我有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
我如何简化这个?我应该以不同的方式提供这些数据的视图或新表吗?同样复杂的是,每个玩家将在不同级别结束游戏。我现在所做的工作,但似乎必须有一种更有效的方式来运行查询。我尝试了一些不同的东西,但似乎无法在我的其他查询中纠正行合并。
答案 0 :(得分:1)
此类转换是 pivot 。不幸的是,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
答案 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
列上有一个等式谓词,player_slot
列上有一个范围谓词和GROUP BY。 (即使删除了player_slot上的范围谓词,此索引仍然适用。)包含level
和ability
列并非绝对必要,但包含它们会使索引成为“覆盖索引” 。它被称为“覆盖索引”,因为查询可以完全从索引中获得(EXPLAIN输出将显示“使用索引”),而无需访问索引下的数据页。
MAX聚合和IF函数的使用使我们能够从适当的行中“挑选”出来。 (“技巧”是对于任何不在指定级别的行,IF函数返回NULL。我们对MAX的实际做法是抛出那些NULL值。
如果(match_id, player_slot, level)
是唯一的,那么MAX聚合函数将在一个a.ability
值和一堆NULL上运行。在更一般的情况下,如果我们没有保证唯一,那么MAX聚合可能会在两个(或更多)非NULL a.ability
值上运行。在更一般的情况下,如果您想在每个级别返回a.ability
(作为字符串)的短列表,则GROUP_CONCAT聚合函数可能很有用(代替MAX)。
答案 2 :(得分:0)
听起来我需要更好地理解数据库规范化。根据你的描述,我仍然不确定你要做什么,但我似乎很清楚,这些数据应该在多个表格中表示(一个用于比赛,一个用于球员,一个用于能力,一个用于球员)能力和能力水平,一个将球员与比赛等相关联。)。