2017-08-02 96 views
1

我有這樣的MySQL查詢:MySQL的優化與分和限制查詢在觸發

SELECT MIN(v.ifr) FROM (SELECT v.ifr FROM tbl_valuation v WHERE v.stock_id = 1 ORDER BY v.created_at DESC LIMIT 19) as v; 

的查詢說明:

+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+---------------------------------------+ 
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref | rows | filtered | Extra         | 
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+---------------------------------------+ 
| 1 | PRIMARY  | <derived2> | NULL  | ALL | NULL   | NULL  | NULL | NULL | 19 | 100.00 | NULL         | 
| 2 | DERIVED  | v   | NULL  | ref | idx_stock  | idx_stock | 9  | const | 2873 | 100.00 | Using index condition; Using filesort | 
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+---------------------------------------+ 

的問題是,這個查詢以及其他類似於此的在觸發器中,因此,在每次插入之前,這些查詢都會運行並更新一些值。 觸發器每分鐘激活約千次。

經過1m記錄後,速度變慢,可能是因爲mysql經歷了大約54587行。

有一種方法可以優化此查詢嗎?

這是我的觸發器:

CREATE TRIGGER BUY_WARNING_TRIG BEFORE INSERT 
ON tbl_valuation 
FOR EACH ROW 
BEGIN 
    DECLARE warn_counter INT DEFAULT 0; 
    DECLARE min_ifr DECIMAL(17,12); 
    DECLARE min_lgui DECIMAL(17,12); 
    DECLARE stock VARCHAR(100);  

    IF New.ls >= New.macd THEN 
     SELECT MIN(v.ifr) 
      FROM (SELECT v.ifr FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 9) as v INTO min_ifr; 

     IF New.ifr <= min_ifr THEN 
      SELECT MIN(v.lgui) 
       FROM (SELECT v.lgui FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 9) as v INTO min_lgui; 

      IF New.lgui <= min_lgui THEN 
       SET warn_counter = warn_counter + 1; 
      END IF; 
     END IF; 

     SELECT MIN(v.ifr) FROM (SELECT v.ifr FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 4) as v INTO min_ifr; 

     IF New.ifr <= min_ifr THEN 
      SELECT MIN(v.lgui) 
        FROM (SELECT v.lgui FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 4) as v INTO min_lgui; 

      IF New.lgui <= min_lgui THEN 
       SET warn_counter = warn_counter + 1; 
      END IF; 
     END IF; 

     SELECT MIN(v.ifr) FROM (SELECT v.ifr FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 19) as v INTO min_ifr; 

     IF New.ifr <= min_ifr THEN 
      SELECT MIN(v.lgui) 
        FROM (SELECT v.lgui FROM tbl_valuation v WHERE v.stock_id = New.stock_id ORDER BY v.created_at DESC LIMIT 19) as v INTO min_lgui; 

      IF New.lgui <= min_lgui THEN 
       SET warn_counter = warn_counter + 1; 
      END IF; 
     END IF; 
    END IF; 

    IF warn_counter > 0 THEN 
     SELECT t.stock FROM tbl_stock t WHERE t.id = New.stock_id INTO stock; 
     CASE warn_counter 
      WHEN 1 THEN INSERT INTO tbl_warning (created_at, stock, level, rate, `type`) VALUES (NOW(), stock, 'LOW', New.rate, 'BUY'); 
      WHEN 2 THEN INSERT INTO tbl_warning (created_at, stock, level, rate, `type`) VALUES (NOW(), stock, 'MED', New.rate, 'BUY'); 
      WHEN 3 THEN INSERT INTO tbl_warning (created_at, stock, level, rate, `type`) VALUES (NOW(), stock, 'HIGH', New.rate, 'BUY'); 
     END CASE; 
    END IF; 
END$$ 

回答

2

您的查詢確實需要一個索引tbl_valuation (stock_id, created_at)(按順序),同時支持where -condition和order by。這將擺脫Using filesort

爲了使其更快一些,您應該在查詢中包含另外兩列,即tbl_valuation (stock_id, created_at, ifr, lgui),以使其成爲覆蓋索引。這將節省在表格中查找這些值的時間(並且將顯示爲using index)。

既然你基本上執行了一次非常類似的查詢6次,你的觸發器代碼本身也可以通過重組或使用不同的方法進行優化,儘管我不打算這樣做。一個快速的優化,但:您可以通過查詢相結合的lguiifr削減了一半的查詢數量:

SELECT min(v.ifr), min(v.lgui) into min_ifr, min_lgui 
FROM (SELECT v.ifr, v.lgui 
     FROM tbl_valuation v WHERE v.stock_id = new.stock_id 
     ORDER BY v.created_at DESC LIMIT 9) as v 

它不會花費你任何時候做到這一點,所以如果你不需要min_lgui在接下來的步驟中,沒有傷害,但如果你需要它,你可以保存一個select

+0

謝謝你,這個索引增加了我的響應時間減少了一半。 – c0nf1ck