2012-02-17 62 views
5

我有如下表:優化與SUM,日期範圍和集團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 

此表有行

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; 

此表有行

我有以下查詢:

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秒才能完成,即使它使用索引。有沒有其他的方式來優化查詢?

謝謝!

回答

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 

此查詢不上較小的結果集加入。

+0

我認爲硬件不是問題。服務器有4個CPU,8核(32核,64線程)和64GB RAM。它具有RAID 10中的6個SSD磁盤。my.cnf是最大優化(基於tuning-primer.sh)。你能否提供有關反規範化的更多信息?謝謝! – Paxxil 2012-02-17 15:19:32

+0

我試過刪除連接,然後從主要查詢的數據循環內的單獨查詢拉營地的數據。我想這是要走的路,因爲現在總執行時間大大減少了。大約需要10秒鐘才能完成。 – Paxxil 2012-02-17 15:47:53

+0

聽起來像不錯,健壯,硬件。 :) – 2012-02-17 16:16:55

0

你可以用你的C表創建一個Inner join,並且在join中使用日期條件,它應該減少查詢的時間。

你也許可以做更多的優化,但那是我能看到的第一個。

+0

你能否給我舉個例子?謝謝你 – Paxxil 2012-02-17 14:55:51

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%肯定沒有數據庫訪問,但我相當確信上述數據會提高您的響應時間

+0

我試過你的建議,但不幸的是,執行你的查詢版本需要約5秒的時間。沒有索引date_cid_vca它需要兩倍的時間。 – Paxxil 2012-02-17 15:09:37