我有以下程序
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。 我需要在此更新中进行任何更改吗?
答案 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次单一操作中更新整个范围。根据涉及的数据量,可能需要一段时间;但它肯定比逐行尝试更快。