mysql中的过程更新查询优化

时间:2014-03-22 12:59:50

标签: mysql sql stored-procedures query-optimization

我有以下程序

CREATE PROCEDURE `task_51_proce`()
BEGIN

  DECLARE v_finished INTEGER DEFAULT 0;
  DECLARE v_pkid INTEGER;
  DECLARE v_mine INTEGER;
  DECLARE v_date DATETIME;
  DECLARE v_lat INTEGER;
  DECLARE v_lng INTEGER;

DECLARE weather_cursor CURSOR FOR select pkid, mine_id, accident_dt, lat, lng
                              from mytable;


-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER 
    FOR NOT FOUND SET v_finished = 1;
OPEN weather_cursor;

get_update: LOOP

    FETCH weather_cursor INTO v_pkid, v_mine, v_date, v_lat, v_lng;

    IF v_finished = 1 THEN 
        LEAVE get_update;
    END IF;

    UPDATE mytable mt
    INNER JOIN myvalues mv
    ON STR_TO_DATE(mv.Date, '%m/%d/%Y') = mt.accident_dt
    SET mt.tmax = mv.tmax, mt.tmin = mv.tmin, mt.dayl= mv.dayl ,
    mt.prcp = mv.prcp , mt.srad = mv.srad, mt.swe=mv.swe, mt.vp=mv.vp
    WHERE mv.Longitude_MAX >= v_lng and 
                      mv.Longitude_MIN <= v_lng and 
                      mv.Latitude_MAX >=  v_lat and  
                      mv.Latitude_MIN <= v_lat  and  
                      STR_TO_DATE(mv.Date, '%m/%d/%Y') = v_date
                      -- limit 1
                      and mt.pkid = v_pkid;
END LOOP get_update;

CLOSE weather_cursor;
commit;  

END;

在两个表中,都有主键,在“mytable”187000记录中,而在“myvalues”中有1000000条记录。任务是合并“mytable”中的数据。

在大约3个小时内,它只更新了3000条记录。

如何优化此更新以便程序快速运行?

在参考gordon回答时,我有以下更新声明:

UPDATE mytable mt INNER JOIN
   myvalues mv
   ON mt.accident_dt = mv.date and
      mt.pkid = v_pkid 
SET mt.tmax = mv.tmax, mt.tmin = mv.tmin, mt.dayl= mv.dayl ,
    mt.prcp = mv.prcp , mt.srad = mv.srad, mt.swe=mv.swe, mt.vp=mv.vp
WHERE mv.Longitude_MAX >= v_lng and 
      mv.Longitude_MIN <= v_lng and 
      mv.Latitude_MAX >=  v_lat and  
      mv.Latitude_MIN <= v_lat;

(i)我已将mv.date的数据类型修改为日期。 (ii)在mv.date和mt.accident_dt列上创建索引

现在每分钟的更新记录从15增加到52。 我需要在此更新中进行任何更改吗?

2 个答案:

答案 0 :(得分:1)

虽然您正在循环更新,但这可能不会对性能产生很大影响。可能关键问题是:

ON STR_TO_DATE(mv.Date, '%m/%d/%Y') = mt.accident_dt

这需要扫描每个值的整个mv表。我建议您修改表,以便Date列实际存储为日期/时间值。然后你可以在它上建立一个索引。

另一种方法是使用变量v_date。如果我假设mv.Date没有时间组件,那么我可以这样编写查询:

UPDATE mytable mt INNER JOIN
       myvalues mv
       ON mt.accident_dt = v_date and
          mt.pkid = v_pkid and
          mv.Date = DATE_FORMAT(v_date, '%m/%d/%Y')
    SET mt.tmax = mv.tmax, mt.tmin = mv.tmin, mt.dayl= mv.dayl ,
        mt.prcp = mv.prcp , mt.srad = mv.srad, mt.swe=mv.swe, mt.vp=mv.vp
    WHERE mv.Longitude_MAX >= v_lng and 
          mv.Longitude_MIN <= v_lng and 
          mv.Latitude_MAX >=  v_lat and  
          mv.Latitude_MIN <= v_lat;

使用此结构,myvalues(date, Longitude_Max)上的索引应该会有所帮助。我不确定为什么在mt上需要两个条件,如果其中一个是主键。如果pkid不是主键,那么你应该在mytable(accident_dt,pkid)`上有一个索引。

EDIOT:

我建议重命名旧列,添加另一个具有相同名称的列,然后更新值:

alter table myvalues change column date oldDate varchar(255);

alter table myvalues add column date date;

update myvalues
    set date = ON STR_TO_DATE(oldDate, '%m/%d/%Y');

答案 1 :(得分:0)

在比赛的后期,虽然我同意@Gordon Linoff提出的建议,但我确实对你为什么需要在你的存储过程中使用光标感到困惑?

光标中使用的mytable在UPDATE中是否与mytable相同?

如果是这样,你应该完全摆脱光标并减少&#39;存储过程:

CREATE PROCEDURE `task_51_proce`()
BEGIN


UPDATE mytable mt 
INNER JOIN myvalues mv
   ON mt.accident_dt = mv.date   
  SET mt.tmax = mv.tmax, mt.tmin = mv.tmin, mt.dayl= mv.dayl ,
      mt.prcp = mv.prcp , mt.srad = mv.srad, mt.swe=mv.swe, mt.vp=mv.vp
WHERE mv.Longitude_MAX >= mt.lng and 
      mv.Longitude_MIN <= mt.lng and 
      mv.Latitude_MAX >= mt.lat and  
      mv.Latitude_MIN <= mt.lat;

commit;  

END;

据我所知,这将在1次单一操作中更新整个范围。根据涉及的数据量,可能需要一段时间;但它肯定比逐行尝试更快。