使用SUM,日期范围和分组依据优化MySQL查询

时间:2012-02-17 14:42:53

标签: mysql

我有以下表格:

CREATE TABLE IF NOT EXISTS stats (
    date date NOT NULL DEFAULT '0000-00-00',
    cid int(8) NOT NULL DEFAULT '0',
    v bigint(15) NOT NULL DEFAULT '0',
    c bigint(15) NOT NULL DEFAULT '0',
    a bigint(15) NOT NULL DEFAULT '0',
PRIMARY KEY (date,cid),
KEY date (date),
KEY cid (cid),
KEY date_cid_vca (date,cid,v,c,a)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

此表格 30,842,712

CREATE TABLE IF NOT EXISTS camp (
id int(8) NOT NULL AUTO_INCREMENT,
name varchar(80) NOT NULL DEFAULT '',
PRIMARY KEY (id,name)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

此表格 1985

我有以下问题:

SELECT
    c.id,
    c.name,
    SUM(s.v) AS sumv,
    SUM(s.c) AS sumc,
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
    SUM(s.a) AS suma,
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio
FROM
    stats s, camp c
WHERE
    s.date >= '2012-02-01' AND
    s.date <= '2012-02-29' AND
    c.id=s.cid
GROUP BY s.cid;

EXPLAIN显示:

+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+
| id | select_type | table | type  | possible_keys                 | key          | key_len | ref                 | rows    | Extra                                                     |
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+
|  1 | SIMPLE      | s     | range | PRIMARY,date,cid,date_cid_vca | date_cid_vca | 3       | NULL                | 1010265 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | c     | ref   | PRIMARY                       | PRIMARY      | 4       | db.s.cid            |       1 | Using index                                               |
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+

问题是即使使用索引,查询也需要大约50秒才能完成。有没有其他方法来优化查询?

谢谢!

3 个答案:

答案 0 :(得分:4)

您在使用索引优化此查询方面做得很好。我想你在那个日期范围内stats确实有超过100万行。不幸的是,加入(然后分组)100万行,即使有覆盖索引,也要从数据库中提出很多要求。为了获得更好的性能,您需要加强硬件,开始非规范化(将camp置于stats内以避免加入),或者保持每个阵营的总计运行而不是动态计算。

修改

由于删除100万个连接似乎产生了很大的影响,你可以尝试这样的事情:

SELECT c.*, a.* FROM
(SELECT
    SUM(s.v) AS sumv,
    SUM(s.c) AS sumc,
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
    SUM(s.a) AS suma,
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio,
    s.cid
FROM
    stats s
WHERE
    s.date >= '2012-02-01'
   AND s.date <= '2012-02-29'
GROUP BY s.cid) a
JOIN
  camp c
  ON c.id = a.cid

此查询在较小的结果集上进行连接。

答案 1 :(得分:1)

以下查询应该允许它更有效地使用索引

SELECT
    c.id,
    c.name,
    SUM(s.v) AS sumv,
    SUM(s.c) AS sumc,
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
    SUM(s.a) AS suma,
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio
FROM
    camp c
INNER JOIN
    stats s
ON
    s.cid = c.id
    AND s.date BETWEEN '2012-02-01' AND '2012-02-29'

GROUP BY c.id;

另外我会考虑删除date_cid_vca键,因为它只包含整个表,因此不是特别有用。上面的查询应该使用PK来匹配基于日期和cid的统计数据到阵营的行,所以虽然很难100%确定无法访问您的数据库,但我相信上述内容会改善您的响应时间< / p>

答案 2 :(得分:0)

您可以使用C表创建内部联接,并且在联接中使用日期条件,它应该减少查询的时间。

你可以做更多的优化,但那是我能看到的第一个。