我有一个表,(PROPERTY_ID, GPSTIME, STATION_ID, PROPERTY_TYPE, VALUE)
其中 PROPERTY_ID 是主键,STATION_ID 是外键。
该表记录状态变化;每行代表给定时间某个站点的属性值。但是,它的数据是从每个属性都是一列(如(STATION_ID, GPSTIME, PROPERTY1, PROPERTY2, PROPERTY3, ...)
)的旧表转换而来的。因为通常一次只有一个属性发生了变化,所以我有很多重复项。
我需要删除所有具有相同值的连续行。
例子。旧表包含的值如
time stn prop1 prop2
100 7 red large
101 7 red small
102 7 blue small
103 7 red small
转换后的表是
(order by time,type) (order by type,time)
time stn type value time stn type value
100 7 1 red 100 7 1 red
100 7 2 large 101 7 1 red
101 7 1 red 102 7 1 blue
101 7 2 small 103 7 1 red
102 7 1 blue 100 7 2 large
102 7 2 small 101 7 2 small
103 7 1 red 102 7 2 small
103 7 2 small 103 7 2 small
应该改为
time stn type value
100 7 1 red
100 7 2 large
101 7 2 small
102 7 1 blue
103 7 1 red
该表包含大约 2200 万行。
我目前的方法是使用过程来遍历表并删除重复项:
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id INT;
DECLARE psid,nsid INT DEFAULT null;
DECLARE ptype,ntype INT DEFAULT null;
DECLARE pvalue,nvalue VARCHAR(50) DEFAULT null;
DECLARE cur CURSOR FOR
SELECT station_property_id,station_id,property_type,value
FROM station_property
ORDER BY station_id,property_type,gpstime;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO id,nsid,ntype,nvalue;
IF done THEN
LEAVE read_loop;
END IF;
IF (psid = nsid and ptype = ntype and pvalue = nvalue) THEN
delete from station_property where station_property_id=id;
END IF;
SET psid = nsid;
SET ptype = ntype;
SET pvalue = nvalue;
END LOOP;
CLOSE cur;
END
但是,它太慢了。在具有 20000 行的测试表上,它会在 6 分钟内删除 10000 个重复项。有没有办法优化程序?
PS 我的旧表仍然完好无损,所以最好尝试在没有重复项的情况下对其进行转换,而不是在转换后处理重复项。
更新。
澄清我想允许哪些重复项,哪些不允许。
- 如果属性更改,然后更改回来,我希望保存所有 3 条记录,即使第一个和最后一个包含相同的 station_id、类型和值。
- 如果有多个具有相同 station_id、类型和值的连续(通过 GPSTIME)记录,我希望只保存第一个(表示对该值的更改)。
总之,a -> b -> b -> a -> a
应该优化到a -> b -> a
.
解决方案
正如@Kickstart 建议的那样,我创建了新表,填充了过滤数据。为了参考前几行,我使用了与此问题中使用的方法类似的方法。
rename table station_property to station_property_old;
create table station_property like station_property_old;
set @lastsid=-1;
set @lasttype=-1;
set @lastvalue='';
INSERT INTO station_property(station_id,gpstime,property_type,value)
select newsid as station_id,gpstime,newtype as type,newvalue as value from
-- this subquery adds columns with previous values
(select station_property_id,gpstime,@lastsid as lastsid,@lastsid:=station_id as newsid,
@lasttype as lasttype,@lasttype:=property_type as newtype,
@lastvalue as lastvalue,@lastvalue:=value as newvalue
from station_property_old
order by newsid,newtype,gpstime) sub
-- we filter the data, removing unnecessary duplicates
where lastvalue != newvalue or lastsid != newsid or lasttype != newtype;
drop table station_property_old;