MySQL SQL优化——分片搜索

DELIMITER $$

DROP PROCEDURE IF EXISTS `test_release`.`Sp_JP_A_NotifyBegin`$$

CREATE DEFINER=`encysys48`@`%` PROCEDURE `Sp_JP_A_NotifyBegin`(
    OUT v_Result          varchar(10),  
    OUT v_OrderID         varchar(500), 
    OUT v_HFserialid      varchar(500), 
    OUT v_ChargeSerialID  varchar(500), 
    OUT v_ErrorCode       varchar(100), 
    OUT v_FullMoney       varchar(200), 
    OUT v_PlatId          varchar(200), 
    OUT v_NotifyCount     varchar(4),   
    OUT v_PHONERESMONEY   varchar(200), 
    OUT v_AGENTID         varchar(500)
)
    MODIFIES SQL DATA
    SQL SECURITY INVOKER
BEGIN
    label2: BEGIN
        DECLARE l_hforderid       varchar(30);
        DECLARE l_hfserialid      varchar(30);
        DECLARE l_errorcode       varchar(10);
        DECLARE l_charserialid    varchar(50);
        DECLARE l_fullmoney       decimal(10, 2);
        DECLARE l_platid          int(10);
        DECLARE l_notofynum       int(4);
        DECLARE l_status          int(4);
        DECLARE l_increce         int(10);
        DECLARE l_nowtime         timestamp;
        DECLARE l_phoneresmoney    varchar(30);
        DECLARE l_agentid         varchar(30);
        DECLARE l_CheckCode       varchar(100);
        DECLARE l_PlatCheckResult varchar(10);
        DECLARE l_stop_flag       int(2);
        DECLARE l_flag            int(2);  
        DECLARE l_cursorEmpty     int(2) DEFAULT 0; #游标是否为空标记位,0-为空  ;1-不为空
        DECLARE l_cursorOpen      int(2) DEFAULT 0; #游标是否打开标记位,0-没打开;1-已打开
        DECLARE l_cursorClose     int(2) DEFAULT 0; #游标是否关闭标记位,0-未关闭;1-已关闭
      
        #定义游标
        DECLARE cur_notify CURSOR FOR
        SELECT a.hf_serialid
          FROM (SELECT hf_serialid
              FROM jp_fullnote s
             WHERE charge_status IN (0, 3) 
               AND (notify_flag = 1 OR (notify_flag = 2 AND DATE_ADD(end_time, INTERVAL 3 MINUTE) < l_nowtime))
               AND DATE_ADD(begin_time, INTERVAL 48 HOUR) > l_nowtime
               AND DATE_ADD(begin_time, INTERVAL 3 HOUR) < l_nowtime
               ORDER BY DATE_ADD(end_time, INTERVAL notify_count MINUTE) ASC
            ) a
        LIMIT 0, 10;
      
        #其他异常  
        DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND
            BEGIN
            #先判断是否需要关闭游标:如果游标“已打开且未关闭”,则需要关闭
            IF l_cursorOpen = 1 AND l_cursorClose = 0 THEN
                CLOSE cur_notify;
            END IF;
            
            IF l_cursorEmpty = 0 THEN
                SET v_OrderID = '-1';
                SET v_HFserialid = '-1';
                SET v_ErrorCode = '-1';
                SET v_FullMoney = '-1';
                SET v_PlatId = '-1';
                SET v_result = '11111';
                SET v_PHONERESMONEY = '-1';
                SET v_AGENTID = '-1';
                ROLLBACK;
            ELSE
                SET v_result = '0';
                SET v_notifycount = l_increce;
                COMMIT;
            END IF;
            END;
      
        #赋初始值
        SET v_result = '82001';
        SET v_OrderID = '';
        SET v_HFserialid = '';
        SET v_ChargeSerialID = '';
        SET v_ErrorCode = '';
        SET v_FullMoney = '';
        SET v_PlatId = '';
        SET l_notofynum = 0;
        SET l_increce = 0;
        SET v_PHONERESMONEY = '';
        SET v_AGENTID = '';
        SET l_nowtime = now();
        SET l_stop_flag = 0;
        SET l_flag = 0;
      
        #自动流程监控
        SELECT CONCAT('ChPlat', t.sys_id, '_A8002_NotifyHF') INTO l_CheckCode
          FROM jp_sysplat t;
        CALL Sp_MO_PlatAutoCheck(l_CheckCode, '3B', l_PlatCheckResult);
      
        #使用游标
        BEGIN
        DECLARE EXIT HANDLER FOR SQLSTATE '02000'
            BEGIN
              SET l_stop_flag = 1;
            END;
        
        #打开游标
        OPEN cur_notify;
        SET l_cursorOpen = 1; #打开游标后,把游标打开的标记位置为1,表示已打开
        
        FETCH cur_notify INTO l_hfserialid;
        #进入游标循环
        label1: WHILE l_stop_flag < 1 DO
            #每次循环计数
            SET l_cursorEmpty = l_cursorEmpty + 1;
        
            SELECT charge_status,ERROR_CODE,hf_orderid,hf_serialid,finish_money,
               f.senderobj,charge_serialid,PHONE_RESMONEY,AGENT_ID 
              INTO l_status, l_errorcode, l_hforderid, l_hfserialid, l_fullmoney,
               l_platid, l_charserialid, l_phoneresmoney, l_agentid
              FROM jp_fullnote f
             WHERE hf_serialid = l_hfserialid;
        
            
            IF l_status = 3 THEN
            SET l_fullmoney = 0;
            END IF;
        
            
            BEGIN
            DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND 
                SET l_flag = 1;
      
            SELECT notify_return_code 
              INTO l_errorcode
              FROM jp_return_code r
             WHERE r.error_code = l_errorcode;
            END;
        
            IF l_flag = 1 THEN
            FETCH cur_notify INTO l_hfserialid;
            ITERATE label1;
            END IF;
        
            
            SET v_OrderID = CONCAT(v_OrderID, l_hforderid, ',');
            SET v_HFserialid = CONCAT(v_HFserialid, l_hfserialid, ',');
            SET v_ChargeSerialID = CONCAT(v_ChargeSerialID, l_charserialid, ',');
            SET v_ErrorCode = CONCAT(v_ErrorCode, l_errorcode, ',');
            SET v_FullMoney = CONCAT(v_FullMoney, l_fullmoney, ',');
            SET v_PlatId = CONCAT(v_PlatId, l_platid, ',');
        
            IF l_phoneresmoney > 10000 THEN
            SET v_PHONERESMONEY = CONCAT(v_PHONERESMONEY, '', ',');
            ELSE
            SET v_PHONERESMONEY = CONCAT(v_PHONERESMONEY, l_phoneresmoney, ',');
            END IF;
        
            SET v_AGENTID = CONCAT(v_AGENTID, l_agentid, ',');
        
            UPDATE jp_fullnote
               SET notify_flag = 2,
               notify_count = notify_count + 1,
               end_time = l_nowtime
            WHERE hf_serialid = l_hfserialid;
        
            SET l_increce = l_increce + 1;
            COMMIT;
        
             
            FETCH cur_notify INTO l_hfserialid;
        END WHILE label1;
        END;
      
        #关闭游标
        CLOSE cur_notify;
        SET l_cursorClose = 1; #关闭游标后,把游标关闭的标记位置为1,表示已关闭
      
        #判断游标是否为空
        IF l_cursorEmpty = 0 THEN
        SET v_OrderID = '-1';
        SET v_HFserialid = '-1';
        SET v_ChargeSerialID = '-1';
        SET v_ErrorCode = '-1';
        SET v_FullMoney = '-1';
        SET v_PlatId = '-1';
        SET v_PHONERESMONEY = '-1';
        SET v_AGENTID = '-1';
        LEAVE label2;
        END IF;
      
        SET v_NotifyCount = l_increce;
        SET v_result = '0';
        COMMIT;
    END;
    END$$

DELIMITER ;
优化前
DELIMITER $$

DROP PROCEDURE IF EXISTS `encysys48`.`sp_jp_a_notifyBegin_acc`$$

CREATE DEFINER=`encysys48`@`%` PROCEDURE `sp_jp_a_notifyBegin_acc`(
    IN    v_num           VARCHAR(2),   #流水号最后一位除10,所得余数
    OUT v_Result          varchar(10),  #结果码
    OUT v_OrderID         varchar(500), #订单号
    OUT v_HFserialid      varchar(500), #话费流水号
    OUT v_ChargeSerialID  varchar(500), #对端流水
    OUT v_ErrorCode       varchar(100), #结果码
    OUT v_FullMoney       varchar(200), #充值金额
    OUT v_PlatId          varchar(200), #中心平台号
    OUT v_NotifyCount     varchar(4),   #数量    
    OUT v_PHONERESMONEY   varchar(200), #充值后手机余额  
    OUT v_AGENTID         varchar(500)
)
    MODIFIES SQL DATA
    SQL SECURITY INVOKER
BEGIN
label2: BEGIN
        DECLARE l_num                   INT(2); #流水号最后一位除10,所得余数
        DECLARE l_hforderid       varchar(30);
        DECLARE l_hfserialid      varchar(30);
        DECLARE l_errorcode       varchar(10);
        DECLARE l_charserialid    varchar(50);
        DECLARE l_fullmoney       decimal(10, 2);
        DECLARE l_platid          int(10);
        DECLARE l_notofynum       int(4);
        DECLARE l_status          int(4);
        DECLARE l_increce         int(10);
        DECLARE l_nowtime         timestamp;
        DECLARE l_phoneresmoney   varchar(20);
        DECLARE l_agentid         varchar(30);
        DECLARE l_CheckCode       varchar(100);
        DECLARE l_PlatCheckResult varchar(10);
        DECLARE l_stop_flag       int(2);
        DECLARE l_flag            int(2);  
        DECLARE l_str             VARCHAR(50);
        DECLARE l_cursorEmpty     int(2) DEFAULT 0; #游标是否为空标记位,0-为空  ;1-不为空
        DECLARE l_cursorOpen      int(2) DEFAULT 0; #游标是否打开标记位,0-没打开;1-已打开
        DECLARE l_cursorClose     int(2) DEFAULT 0; #游标是否关闭标记位,0-未关闭;1-已关闭
        declare l_search_times      INT(2) DEFAULT 0; #循环执行游标的次数
        DECLARE l_start_time        TIMESTAMP;    #游标搜索的起始时间
        DECLARE l_end_time          TIMESTAMP;    #游标搜索的结束时间
        #定义游标
        DECLARE cur_notify CURSOR FOR
        SELECT a.hf_serialid
          FROM (SELECT hf_serialid
              FROM jp_fullnote s
             WHERE charge_status IN (0, 3) 
               AND (notify_flag = 1 OR (notify_flag = 2 AND DATE_ADD(end_time, INTERVAL 3 MINUTE) < l_nowtime))
            AND MOD(SUBSTRING(hf_serialid, CHAR_LENGTH(hf_serialid), 1), 10) = l_num
               AND begin_time >= l_start_time and begin_time < l_end_time
               ORDER BY DATE_ADD(end_time, INTERVAL notify_count MINUTE) ASC
            ) a
        LIMIT 0, 10;
      
        #其他异常  
        DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND
            BEGIN
            #先判断是否需要关闭游标:如果游标“已打开且未关闭”,则需要关闭
            IF l_cursorOpen = 1 AND l_cursorClose = 0 THEN
                CLOSE cur_notify;
            END IF;
            
            IF l_cursorEmpty = 0 THEN
                SET v_OrderID = '-1';
                SET v_HFserialid = '-1';
                SET v_ErrorCode = '-1';
                SET v_FullMoney = '-1';
                SET v_PlatId = '-1';
                SET v_result = '11111';
                SET v_PHONERESMONEY = '-1';
                SET v_AGENTID = '-1';
                ROLLBACK;
            ELSE
                SET v_result = '0';
                SET v_notifycount = l_increce;
                COMMIT;
            END IF;
            CALL sp_SN_dbwarning(1, 'Sp_JP_A_NotifyBegin_Acc', '1000', "", NULL, NULL);
            END;
            
        #赋初始值
        SET v_result = '82001';
        SET v_OrderID = '';
        SET v_HFserialid = '';
        SET v_ChargeSerialID = '';
        SET v_ErrorCode = '';
        SET v_FullMoney = '';
        SET v_PlatId = '';
        SET l_notofynum = 0;
        SET l_increce = 0;
        SET v_PHONERESMONEY = '';
        SET v_AGENTID = '';
        SET l_nowtime = now();
        SET l_stop_flag = 0;
        SET l_flag = 0;
        SET l_num = v_Num;
        #自动流程监控
     BEGIN
        DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND SET l_CheckCode = 'unknown_A80??_Charge_General?';
        SELECT
            CASE v_Num
              WHEN 0 THEN '_A8040_Charge_General0'
              WHEN 1 THEN '_A8041_Charge_General1'
              WHEN 2 THEN '_A8042_Charge_General2'
              WHEN 3 THEN '_A8043_Charge_General3'
              WHEN 4 THEN '_A8044_Charge_General4'
              WHEN 5 THEN '_A8045_Charge_General5'
              WHEN 6 THEN '_A8046_Charge_General6'
              WHEN 7 THEN '_A8047_Charge_General7'
              WHEN 8 THEN '_A8048_Charge_General8'
              WHEN 9 THEN '_A8049_Charge_General9' ELSE '_A80??_Charge_General?' END INTO l_str
        FROM DUAL;
        SELECT CONCAT('ChPlat', t.sys_id, l_str) INTO l_CheckCode
          FROM jp_sysplat t;
        CALL Sp_MO_PlatAutoCheck(l_CheckCode, '3B', l_PlatCheckResult);
     END;
        set l_start_time = now();
        #使用游标
     looplabel: while ((l_increce < 10) and (l_search_times < 18)) do
        set l_end_time = l_start_time;
        set l_start_time = DATE_ADD(l_end_time, INTERVAL -10 MINUTE);
        set l_stop_flag = 0;
        BEGIN
        DECLARE EXIT HANDLER FOR SQLSTATE '02000'
            BEGIN
              SET l_stop_flag = 1;
            END;
        #打开游标
        OPEN cur_notify;
        SET l_cursorOpen = 1; #打开游标后,把游标打开的标记位置为1,表示已打开
        
        FETCH cur_notify INTO l_hfserialid;
        INSERT INTO w_help(char_content) VALUES(l_increce);
        #进入游标循环
        label1: WHILE l_stop_flag < 1 and l_increce < 10 DO
            #每次循环计数
            SET l_cursorEmpty = l_cursorEmpty + 1;
           
            SELECT charge_status,ERROR_CODE,hf_orderid,hf_serialid,finish_money,
               senderobj,charge_serialid,PHONE_RESMONEY,AGENT_ID 
              INTO l_status, l_errorcode, l_hforderid, l_hfserialid, l_fullmoney,
               l_platid, l_charserialid, l_phoneresmoney, l_agentid 
              FROM jp_fullnote f
             WHERE hf_serialid = l_hfserialid;
           
            #判断订单是否是失败    
            IF l_status = 3 THEN
            SET l_fullmoney = 0;
            END IF;
        
            #获取错误码
            BEGIN
            DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND 
                SET l_flag = 1;
      
            SELECT notify_return_code 
              INTO l_errorcode
              FROM jp_return_code r
             WHERE r.error_code = l_errorcode;
            END;
        
            IF l_flag = 1 THEN
            FETCH cur_notify INTO l_hfserialid;
            ITERATE label1;
            END IF;
        
            #拼接参数
            SET v_OrderID = CONCAT(v_OrderID, l_hforderid, ',');
            SET v_HFserialid = CONCAT(v_HFserialid, l_hfserialid, ',');
            SET v_ChargeSerialID = CONCAT(v_ChargeSerialID, l_charserialid, ',');
            SET v_ErrorCode = CONCAT(v_ErrorCode, l_errorcode, ',');
            SET v_FullMoney = CONCAT(v_FullMoney, l_fullmoney, ',');
            SET v_PlatId = CONCAT(v_PlatId, l_platid, ',');
        
            IF l_phoneresmoney > 10000 THEN
            SET v_PHONERESMONEY = CONCAT(v_PHONERESMONEY, '', ',');
            ELSE
            SET v_PHONERESMONEY = CONCAT(v_PHONERESMONEY, l_phoneresmoney, ',');
            END IF;
        
            SET v_AGENTID = CONCAT(v_AGENTID, l_agentid, ',');
        
            UPDATE jp_fullnote
               SET notify_flag = 2,
               notify_count = notify_count + 1,
               end_time = l_nowtime
            WHERE hf_serialid = l_hfserialid;
            
            SET l_increce = l_increce + 1;
            INSERT INTO w_help(char_content) VALUES(l_increce);
            COMMIT;
        
            #进入下一次循环 
            FETCH cur_notify INTO l_hfserialid;
        END WHILE label1;
        END;
        #关闭游标
        CLOSE cur_notify;
        SET l_cursorClose = 1; #关闭游标后,把游标关闭的标记位置为1,表示已关闭
        set l_search_times = l_search_times + 1;
        INSERT INTO w_help(char_content) VALUES(l_search_times);
     END WHILE looplabel;   
        #判断游标是否为空
        IF l_cursorEmpty = 0 THEN
        SET v_OrderID = '-1';
        SET v_HFserialid = '-1';
        SET v_ChargeSerialID = '-1';
        SET v_ErrorCode = '-1';
        SET v_FullMoney = '-1';
        SET v_PlatId = '-1';
        SET v_PHONERESMONEY = '-1';
        SET v_AGENTID = '-1';
        LEAVE label2;
        END IF;
      
        SET v_NotifyCount = l_increce;
        insert into w_help(char_content) values(l_increce);
        SET v_result = '0';
        COMMIT;
    END;
    END$$

DELIMITER ;
优化后

优化的核心是:

  优化前,搜索三小时以内的满足条件的订单,十条;

  优化后,搜索十分钟以内的,满足条件的,搜索结果数目大于10,返回10条搜素结果;

      搜索结果数目小于10,继续搜索10~20分钟之间的,总共之和大于10,返回结果;

      总共结果小于10,继续搜索20~30分钟之间的。

这样,即使搜索三小时以内的,速度也明显的快。

原文地址:https://www.cnblogs.com/tengpan-cn/p/4997099.html