2012-07-18 52 views
2

我創建一個使用遊標Mysql的過程,但它的運行速度太慢......它是由每秒40條60線之間得到。請參見:程序與光標的mysql太慢..爲什麼?

DELIMITER $$ 
CREATE PROCEDURE sp_create(IN v_idsorteio INT,OUT afetados INT) 
    BEGIN 
     DECLARE done INT default 0; 
     DECLARE vc_idsocio INT; 
     DECLARE z INT; 
     DECLARE cur1 CURSOR FOR select IdSocio from socios where Sorteio=1 and Finalizado='S' and CodClientes IS NOT NULL; 
     DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; 
     SET z=1; 
     OPEN cur1; 
     FETCH cur1 INTO vc_idsocio; 
     WHILE done=0 DO 
      -- SELECT register as t; 
      insert INTO socios_numeros_sorteio (IdSocio,IdSorteio,NumerodeSorteio) VALUES (vc_idsocio,v_idsorteio,z); 
      FETCH cur1 INTO vc_idsocio; 
      SET z = z+1; 
     END WHILE; 
     CLOSE cur1; 
     Select z-1 as total INTO afetados; 
    END$$ 
DELIMITER ; 

我怎樣才能改善呢?

回答

9

這很慢,因爲您循環遍歷一個結果集,逐行併爲返回的每一行執行單獨的插入語句。這就是爲什麼它會變慢。

讓我們簡要總結一下你在做什麼。首先,你正在運行一個查詢:

select IdSocio 
    from socios 
where Sorteio=1 
    and Finalizado='S' 
    and CodClientes IS NOT NULL; 

(顯然,這些行被返回的順序並不重要)

然後爲每一行從該查詢返回的,要插入一行到另一表。

insert INTO socios_numeros_sorteio 
(IdSocio 
,IdSorteio 
,NumerodeSorteio 
) VALUES 
(vc_idsocio 
,v_idsorteio 
,z); 

第一列的值來自查詢返回的值。 第二列的值將被分配一個作爲參數傳遞給過程的值。 第三列的值來自計數​​器,該計數器從1開始,每行增加1。

MySQL被優化來執行這樣的操作。但是它並沒有被優化,使用循環遍歷遊標的存儲過程來做到這一點。

如果您希望獲得一些合理的性能,您需要顯着減少您運行的單個INSERT語句的數量,而是考慮處理「集合」而不是單個行中的數據。一種方法是將行批量化爲「​​擴展插入」語句,這些語句一次可以插入多行。 (您可以在一條語句中插入的行數受到max_allowed_pa​​cket的有效限制。)

該方法將顯着提高性能,但它不會避免遊標的開銷,將每行讀取到過程變量中。

像這樣的東西(在你的過程中)很可能會更好,因爲它從你的select中取得結果集,並將所有行一次插入到目的表中,而不會打擾弄亂更新過程中變量的值。

BEGIN 

SET @idsorteio = v_idsorteio; 

INSERT INTO socios_numeros_sorteio 
(IdSocio 
, IdSorteio 
, NumerodeSorteio 
) 
SELECT s.IdSocio AS IdSocio 
    , @idsorteio AS IdSorteio 
    , @z := @z+1 AS NumerodeSorteio 
    FROM socios s 
    JOIN (SELECT @z := 0) z 
WHERE s.Sorteio=1 
    AND s.Finalizado='S' 
    AND s.CodClientes IS NOT NULL; 

SELECT ROW_NUMBER() INTO afetados; 

END$$ 
+0

我第二,'INSERT .. SELECT'是學習 – spacediver 2012-07-19 20:20:31

+0

優秀的答案:) – 2012-07-19 20:39:41

+1

的警告是必要對此例如一個偉大的工具。這些插入語句中涉及的表將在插入期間保持鎖定狀態,因此只要鎖就位,就可以防止表上的其他動作(例如,選擇,插入,更新,刪除)。 – 2013-12-26 17:03:05

0

另一種簡單的解決方案是隻通過運行下面的查詢表中的發動機改變的MyISAM,

ALTER TABLE `socios_numeros_sorteio` 
ENGINE=MyISAM; 

然後再次調用該過程。 注:MyISAM數據使插入過程非常快