我该如何优化这个MySQL查询?

时间:2009-07-29 12:30:11

标签: php sql optimization mysql

我在包含超过300,000,000(是,3亿)行的数据库的PHP脚本中使用以下MySQL查询。我知道这是非常耗费资源的,运行这一个查询需要很长时间。有谁知道如何优化查询或以更快的方式获取信息?

我需要能够使用1到15之间的任何整数代替MID()中的14。我还需要能够匹配LIKE子句中相同范围内的长度字符串。

表格信息:

games | longint, unsigned, Primary Key
win   | bit(1)
loss  | bit(1)

示例查询:

SELECT MID(`game`,14,1) AS `move`,
       COUNT(*) AS `games`,
       SUM(`win`) AS `wins`,
       SUM(`loss`) AS `losses`
FROM `games`
WHERE `game` LIKE '1112223334%'
GROUP BY MID(`game`,1,14)

提前感谢您的帮助!

6 个答案:

答案 0 :(得分:5)

首先,在游戏领域有一个索引...... :)

查询看似简单明了,但它隐藏了可能需要更改数据库设计的事实。

在这种情况下,我总是喜欢维护一个包含聚合数据的字段,每天,每个用户或每个任意轴。这样,您就可以拥有聚合相关数据的日常任务,并将其保存在数据库中。

如果确实经常调用此查询,则应使用降低插入效率的原则来提高检索效率。

答案 1 :(得分:2)

看起来game列存储了此查询正在使用的两个(或可能更多)不同的内容:

  1. game开头过滤(前10个字符)
  2. 分组MID(游戏,1,14)(我假设其中一个MID表达式是拼写错误。
  3. 我将其拆分,以便您不必在game列上使用字符串操作,并且还将索引放在新列上,以便您可以对它们进行过滤和分组。

    这个查询正在进行大量的转换(长到字符串)和字符串操作,如果表被规范化(如每列中的一条信息而不是像现在这样的多条信息),则不需要。

    保留game列的方式,并根据它创建一个game_filter字符串列,以便在WHERE子句中使用。然后设置game_group列,并在插入时使用MID表达式填充它。将这两列设置为聚集索引,先是game_filter,然后是game_group

答案 2 :(得分:1)

查询很简单,除了确保有所有必要的索引(显然是“游戏”字段)之外,通过仅重写查询可能没有明显的方法使其更快。 可能需要对数据结构进行一些修改。

一种方法:预先计算总和。这些记录中的每一个都很可能具有create_date或自动增量的键字段。预先计算所有记录的总和,此字段≤X,将结果放入边表,然后您只需计算所有记录> X,然后用预先计算的结果总结这些部分结果。

答案 3 :(得分:1)

您可以预先计算MID(game,14,1)和MID(game,1,14),并将game的前十位数存储在单独的gameid列中这是索引。

调查是否可以只存储预先计算的值的聚合表,以便在插入时增加计数和赢或输列列,这也是一个想法。

答案 4 :(得分:0)

SELECT  MID(`game`,14,1) AS `move`,
        COUNT(*) AS `games`,
        SUM(`win`) AS `wins`,
        SUM(`loss`) AS `losses`
FROM    `games`
WHERE   `game` LIKE '1112223334%'

game上创建索引:

CREATE INDEX ix_games_game ON games (game)

并将此查询重写为:

SELECT  move,
        (
        SELECT  COUNT(*)
        FROM    games
        WHERE   game >= move
                AND game < CONCAT(SUBSTRING(move, 1, 13), CHR(ASCII(SUBSTRING(move, 14, 1)) + 1))
        ),
        (
        SELECT  SUM(win)
        FROM    games
        WHERE   game >= move
                AND game < CONCAT(SUBSTRING(move, 1, 13), CHR(ASCII(SUBSTRING(move, 14, 1)) + 1))
        ),
        (
        SELECT  SUM(lose)
        FROM    games
        WHERE   game >= move
                AND game < CONCAT(SUBSTRING(move, 1, 13), CHR(ASCII(SUBSTRING(move, 14, 1)) + 1))
        )
FROM    (
        SELECT  DISTINCT SUBSTRING(q.game, 1, 14) AS move
        FROM    games
        WHERE   game LIKE '1112223334%'
        ) q

这将有助于更有效地使用game上的索引。

答案 5 :(得分:0)

您可以使用Memcache或类似的东西缓存结果集吗?这将有助于重复点击。即使您只将结果集缓存几秒钟,也可以避免大量数据库读取。