MySQL查询索引&绩效改进

时间:2016-01-07 14:25:21

标签: mysql sql performance

我已经为我和我的朋友创建了一个应用程序来跟踪英雄联盟的进度。为此,我每天多次将有关当前排名的信息收集到我的MySQL数据库中。要获取结果并在图表中显示结果,我使用以下查询/查询:

SELECT 
    lol_summoner.name as name, grid.series + ? as timestamp, 
    AVG(NULLIF(lol.points, 0)) as points
FROM 
    series_tmp grid
JOIN 
    lol ON lol.timestamp >= grid.series AND lol.timestamp < grid.series + ?
JOIN 
    lol_summoner ON lol.summoner = lol_summoner.id
GROUP BY
    lol_summoner.name, grid.series
ORDER BY
    name, timestamp ASC

SELECT 
    lol_summoner.name as name, grid.series + ? as timestamp, 
    AVG(NULLIF(lol.points, 0)) as points
FROM 
    series_tmp grid
JOIN 
    lol ON lol.timestamp >= grid.series AND lol.timestamp < grid.series + ?
JOIN 
    lol_summoner ON lol.summoner = lol_summoner.id
WHERE 
    lol_summoner.name IN (". str_repeat('?, ', count($names) - 1) ."?)
GROUP BY
    lol_summoner.name, grid.series
ORDER BY
    name, timestamp ASC

如果我想要检索保存在数据库中的所有玩家,则使用第一个查询。网格表是一个临时表,它在特定的时间间隔内生成时间戳,以便以此间隔的块的形式检索信息。此查询中的两个变量是间隔。如果我只想检索特定玩家的信息,则使用第二个查询。

网格表由以下存储过程产生,该过程使用三个参数调用(n_first - 第一个时间戳,n_last - 最后一个时间戳,n_increments - 两个时间戳之间的增量):

BEGIN
    -- Create tmp table
    DROP TEMPORARY TABLE IF EXISTS series_tmp;
    CREATE TEMPORARY TABLE series_tmp (
        series bigint
    ) engine = memory;

    WHILE n_first <= n_last DO
        -- Insert in tmp table
        INSERT INTO series_tmp (series) VALUES (n_first);

        -- Increment value by one
        SET n_first = n_first + n_increment; 
    END WHILE;
END

查询工作并在合理的时间内完成(约10秒),但我感谢任何通过重写或向数据库添加其他索引来改进查询的帮助。

/编辑:

在回顾了@Rick James的回答之后,我按如下方式修改了查询:

SELECT lol_summoner.name as name, (lol.timestamp div :range) * :range + :half_range as timestamp, AVG(NULLIF(lol.points, 0)) as points
  FROM lol
    JOIN lol_summoner ON lol.summoner = lol_summoner.id
  GROUP by lol_summoner.name, lol.timestamp div :range
  ORDER by name, timestamp ASC

SELECT lol_summoner.name as name, (lol.timestamp div :range) * :range + :half_range as timestamp, AVG(NULLIF(lol.points, 0)) as points
  FROM lol
    JOIN lol_summoner ON lol.summoner = lol_summoner.id
  WHERE lol_summoner.name IN (<NAMES>)
  GROUP by lol_summoner.name, lol.timestamp div " . $steps . "
  ORDER by name, timestamp ASC

这可以将查询执行时间提高一个很好的余量(在1s以下完成)。

1 个答案:

答案 0 :(得分:1)

问题1和解决方案

两个值之间需要一系列整数吗?他们相差1?还是通过一些更大的价值?

首先,创建一个永久表,其数字从0到足够大的值:

CREATE TABLE Num10 ( n INT );
INSERT INTO Num10 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE Nums ( n INT, PRIMARY KEY(n))
    SELECT a.n*1000 + b.n*100 + c.n*10 + d.n
        FROM Num10 AS a
        JOIN Num10 AS b  -- note "cross join"
        JOIN Num10 AS c
        JOIN Num10 AS d;

现在Nums有0..9999。 (如果你可能需要更多,请把它做大。)

从123到234获取一系列连续数字:

 SELECT 123 + n FROM Nums WHERE n < 234-123+1;

从12345到23456获得一系列连续数字,步长为15:

 SELECT 12345 + 15*n FROM Nums WHERE n < (23456-12345+1)/15;

JOIN改为SELECT,而不是series_tmp

除了其他问题,这应该会大大加快速度。

问题2

您是GROUPing BY seriesORDERingtimestamp。他们是相关的,所以你可能会得到正确的&#39;回答。但想一想。

问题3

你似乎正在建造#34;水桶&#34; (称为&#34;系列&#34;?)来自&#34;时间戳&#34;。它是否正确?如果是这样,让我们​​倒退 - 转一个&#34;时间戳&#34;进入&#34;桶&#34;号:

bucket_number = (timestamp - start) / bucket_size

通过这样做,您可以避免问题1&#39;并消除我的解决方案。也就是说,根据存储桶重新制定整个查询。

相关问题