需要一些严肃的SQL查询优化(MySQL)

时间:2010-10-11 11:55:16

标签: mysql refactoring

这是我当前的查询:

SELECT IFNULL(sum(open_for), 0) total, count(IF(open_for > 0, 1, null)) wins, count(IF(open_for < 0, 1, null)) losses FROM `sport_points` WHERE (sportable_id = 1 and sportable_type = 'Team' and game_time > '2010-07-13 11:39:58 UTC'

它基本上返回了这个聚合数据:

TEAM A

  • open_for
  • 总计:2000
  • 胜利:20
  • 损失:12

现在,假设表中还有大约6个其他列需要执行单独的查询以获取一个团队的所有列特定聚合数据。例如:

SELECT IFNULL(sum(FINAL_FOR), 0) total, count(IF(open_for > 0, 1, null)) wins, count(IF(open_for < 0, 1, null)) losses FROM `sport_points` WHERE (sportable_id = 1 and sportable_type = 'Team' and game_time > '2010-07-13 11:39:58 UTC'

TEAM A

  • final_for
  • 总计:4000
  • 胜利:40
  • 损失:18

这种方法的问题在于我必须对200多个团队中的所有列运行大约6个单独的查询。这是一个严重的负载问题。

理想情况下,查询将在一个查询中返回一个团队的所有特定于列的聚合数据。在结果中看起来像这样:

TEAM A

  • open_for_total
  • open_for_wins
  • open_for_losses
  • final_for_total
  • final_for_wins
  • final_for_losses

...等...

2 个答案:

答案 0 :(得分:0)

只是为了那些想要帮助的人的利益:

查询1:

SELECT
    IFNULL(sum(open_for), 0) total
    ,COUNT(IF(open_for > 0, 1, null)) wins
    ,COUNT(IF(open_for < 0, 1, null)) losses
FROM
    `sport_points`
WHERE
    sportable_id = 1
    AND sportable_type = 'Team'
    AND game_time > '2010-07-13 11:39:58 UTC'

查询2:

SELECT
    IFNULL(SUM(FINAL_FOR), 0) total
    ,COUNT(IF(open_for > 0, 1, null)) wins
    ,COUNT(IF(open_for < 0, 1, null)) losses
FROM
    `sport_points`
WHERE 
    sportable_id = 1
    AND sportable_type = 'Team'
    AND game_time > '2010-07-13 11:39:58 UTC'

所需的输出列:teamname,typeofquery,value

typeofquery是以下之一:

  • open_for_total
  • open_for_wins
  • open_for_losses
  • final_for_total
  • final_for_wins
  • final_for_losses

从两列open_forfinal_for以及winslosses列中派生而来。

最初考虑问题我猜测中间表可能有助于使用GROUP BY子句进行处理。

e.g。

INSERT INTO
  temptable
SELECT
  teamname
  ,'open_for' type
  ,IFNULL(SUM(open_for), 0) total
  ,COUNT(IF(open_for > 0, 1, null)) wins
  ,COUNT(IF(open_for < 0, 1, null)) losses
FROM
  `sport_points`
WHERE
  sportable_id = 1
  AND sportable_type = 'Team'
  AND game_time > '...'
GROUP BY
  teamname

然后运行相同的查询,但总结final_for。现在你的临时表包含如下行:

teamname, type, total, wins, losses
TEAM A, open_for, 100, 37, 63
TEAM A, final_for, 30, 10, 20
TEAM B, open_for, 12, 8, 4
TEAM B, final_for, 50, 49, 1

您的最终查询可以根据需要连接列。

答案 1 :(得分:0)

我会将此作为单个查询执行,为每个统计信息返回单独的。然后,如果有必要,我会在我的应用程序代码中重构结果。查询类似于:

select teamname,
 sum(open_for) as open_total,
 count(if(open_for > 0, 1, null)) as open_wins,
 count(if(open_for < 0, 1, null)) as open_losses,
 sum(final_for) as final_total,
 count(if(final_for > 0, 1, null)) as final_wins,
 count(if(final_for < 0, 1, null)) as final_losses,
from sport_points
where sportable_id = 1
 and sportable_type = 'Team'
 and game_time > '...'
group by teamname

我建议,这是一种更正统的关系方法,因此可以在SQL中轻松表达。如果它不是您在应用程序中所需要的,那么进行调整的地方就是代码,这比SQL灵活得多。