DROP PROCEDURE IF EXISTS revision_archive; DELIMITER // CREATE PROCEDURE revision_archive( IN data_source VARCHAR(256), IN archive_target VARCHAR(256), IN identify_columns TEXT, IN track_columns TEXT, OUT mesg MEDIUMTEXT ) BEGIN /* 存储过程说明 对数据源表data_source做追踪数据的归档,以identify_columns的列为惟一标记追踪数据行,对track_columns的列有改变则保存新版本 参数说明 IN data_source VARCHAR(256), 数据源,表名或数据库名.表名,允许带反引号,与SQL中习惯一致。 IN archive_target VARCHAR(256), 归档目标表,第一次执行时会自动创建;规则同上。 IN identify_columns TEXT, 惟一标识数据的字段,支持多个字段,以英文逗号分隔,逗号前后不要带空格。 IN track_columns TEXT, 追踪数据的字段,只有(且只要)这些字段中数据有改变,即自动留存新版本,否则不会;规则同上。 OUT mesg MEDIUMTEXT 调试消息,执行log 关于归档表 1. 归档表中字段有 _id, _revision, _del 三个内部字段,及 identify_columns, track_columns, 指定的字段 2. 自动建表时,字段类型与源表一致,但默认值都不设置。(兼容MySQL版本间差异、默认值的特殊符号等太烦琐;而且归档表不需要根据默认值插入数据) 3. 源表中数据,identify_columns 字段必须是惟一值,但并不要求惟一索引;如果有非惟一,则归档会异常。<或许,以后这点可以在存储过程中自动检测> 4. _del=1的行,是数据源中删除的行。其它字段是该行删除前的一次归档的内容,而非NULL值或默认值。<按理说NULL值更合理,但所有列都要允许NULL,浪费有点大> 5. 如果数据源表字段增减或改类型,归档表不会自动同步修改;此时要手工同步修改;但不建议在已经不过追踪数据手再修改源表结构。 6. 三个内部字段如果与数据源字段名冲突,可修改存储过程内的定义;正常情况下不会。 7. 归档时,会按identify_columns把检出各自的(包含删除标记删除的行)最新版本,也说是如果非常多删除记录,这个最新快照表会很大,此时效果会受影响。 8. 版本号 _revision 是日期时间秒,可据此检出指定时间的归档版本,即追踪指定历史快照。 */ # 声明配置参数,用于存储数据源变量及归档目标变量(通过传入参数 data_source, archive_target 计算) DECLARE source_db VARCHAR(512); DECLARE source_table VARCHAR(64); DECLARE target_db VARCHAR(512); DECLARE target_table VARCHAR(64); DECLARE current_db VARCHAR(512) DEFAULT ''; # 声明配置参数,归档表字段名称:主键,版本号,删除标识。 可在后面赋值处修改,推荐保持默认。 DECLARE revision_column VARCHAR(64); DECLARE del_flag_column VARCHAR(64); DECLARE archive_table_pk VARCHAR(64); # 声明配置参数,表名,最新的快照表及临时表,从归档中导出一份最新快照表到 snap_table # 创建在当前数据库中,名称为 CONCAT(source_table,'_snap') DECLARE snap_table VARCHAR(64) DEFAULT ''; DECLARE snap_checking_table VARCHAR(64) DEFAULT ''; # 定义两个 用于拆分字符串的起止位置 的整形变量 DECLARE p1 int DEFAULT 0; DECLARE p2 int DEFAULT 0; # sql 部件们 ## 关联列 JOIN 条件, track 列的过滤条件 DECLARE sp_identify_join TEXT DEFAULT ''; DECLARE sp_track_filter TEXT DEFAULT ''; DECLARE archive_table_create_sql TEXT DEFAULT ''; # 一些临时变量等 DECLARE column_name VARCHAR(128) DEFAULT ''; # DECLARE @s_sql TEXT DEFAULT ''; # 建表用的sql部件字符串 ct_part_* : create_table_part1,2,3 DECLARE ct_part_1 TEXT DEFAULT ''; DECLARE ct_part_2 TEXT DEFAULT ''; DECLARE ct_part_3 TEXT DEFAULT ''; DECLARE table_engine VARCHAR(32) DEFAULT ''; DECLARE default_charset VARCHAR(32) DEFAULT ''; # 版本序列 DECLARE rev_stamp BIGINT DEFAULT 0; # *** 0. 检查及准备环境相关参数 # 调试消息 SET @dmesg=''; SET SESSION group_concat_max_len = 65535; # 定义归档表字段名称:主键,版本号,删除标识; (如果确信有必要,可以修改这几个名称;推荐保持不变) SET archive_table_pk='`_id`'; SET revision_column='`_revision`'; SET del_flag_column='`_del`'; # 计算当前数据库名存入变量current_db SELECT database() into current_db; # 计算源/目标数据库名及表名 source_db, source_table, target_db, target_table SET p1=LOCATE('.',data_source); IF p1 > 0 THEN SET source_db=SUBSTRING(data_source,1,p1-1); SET source_table=SUBSTRING(data_source,p1+1); ELSE SET source_db=current_db; SET source_table=data_source; END IF; SET p1=LOCATE('.',archive_target); IF p1 > 0 THEN SET target_db=SUBSTRING(archive_target,1,p1-1); SET target_table=SUBSTRING(archive_target,p1+1); ELSE SET target_db=current_db; SET target_table=archive_target; END IF; # 计算快照表名 IF RIGHT(source_table,1)='`' THEN SET snap_table=CONCAT(SUBSTRING(source_table,1,CHAR_LENGTH(source_table)-1),'_snap`'); SET snap_checking_table=CONCAT(SUBSTRING(source_table,1,CHAR_LENGTH(source_table)-1),'_snap_checking_out`'); ELSE SET snap_table=CONCAT(source_table,'_snap'); SET snap_checking_table=CONCAT(snap_table,'_checking_out'); END IF; SET @dmesg=CONCAT(@dmesg,'current_db: ',current_db,'\n'); SET @dmesg=CONCAT(@dmesg,'source: ',source_db,'..',source_table,'\n'); SET @dmesg=CONCAT(@dmesg,'target: ',target_db,'..',target_table,'\n'); SET @dmesg=CONCAT(@dmesg,'snap_table/snap_checking_table: ',snap_table,' ',snap_checking_table,'\n'); # 检查版本类型 revision_type ## 可选值: inc 自增序列; date_string 年月日; st 时间序列; ## TODO... 暂时不实现。只使用 bigint 型存储的 年月日时分秒字符串 /* IF revision_type = 'increment' elseif end if */ # 计算 sp_identify_join SET p1=1; SET p2=0; REPEAT SET p2=locate(',',identify_columns,p1); SET column_name=IF(p2=0, SUBSTRING(identify_columns,p1), SUBSTRING(identify_columns,p1,p2-p1) ); #SET sp_identify_join=CONCAT(sp_identify_join ,'AND ', source_table, '.' , column_name, '=' , snap_table, '.', column_name, ' '); SET sp_identify_join=CONCAT(sp_identify_join ,'AND ', 'd.' , column_name, '=' , 'sn.', column_name, ' '); SET p1=p2+1; UNTIL p2 <= 0 END REPEAT; SET sp_identify_join=SUBSTRING(sp_identify_join,5); # 截掉前 4 字符的 "AND " # 计算 sp_track_filter SET p1=1; SET p2=0; REPEAT SET p2=locate(',',track_columns,p1); SET column_name=IF(p2=0, SUBSTRING(track_columns,p1), SUBSTRING(track_columns,p1,p2-p1) ); #SET sp_track_filter=CONCAT(sp_track_filter ,'OR ', source_table, '.' , column_name, '=' , snap_table, '.', column_name, ' '); SET sp_track_filter=CONCAT(sp_track_filter ,'OR ', 'd.' , column_name, '!=' , 'sn.', column_name, ' '); SET p1=p2+1; UNTIL p2 <= 0 END REPEAT; SET sp_track_filter=SUBSTRING(sp_track_filter,4); # 截掉前 3 字符的 "OR " SET @dmesg=CONCAT(@dmesg,sp_identify_join,'\n\n'); SET @dmesg=CONCAT(@dmesg,sp_track_filter,'\n\n'); # 本次执行的 revision 号 SET rev_stamp = DATE_FORMAT(now(),'%Y%m%d%H%i%s'); SET @dmesg=CONCAT(@dmesg,'rev_stamp: ',rev_stamp,'\n\n'); # *** 1. 准备表结构(归档表、上次快照表),并创建 /* --- 不检查了,直接使用 CREATE TABLE IF EXISTS ... # 检查归档表 target_db.target_table 是否在在,不在在则需要自动创建 SET @s_sql=CONCAT('SELECT count(*) INTO @archive_table_exists FROM information_schema.`TABLES` WHERE TABLE_SCHEMA="',target_db,'" AND TABLE_NAME="', target_table, '" '); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; #SET @dmesg=CONCAT(@dmesg,'archive_table_exists ',CONCAT(target_db,'.',target_table),' : ',@archive_table_exists,'\n\n'); */ # 读源数据表结构,构造归档表建表语句部件, # 字段部分: 关联标识 ct_part_1, 追踪字段 ct_part_2,索引部分 ct_part_3; # 表属性变量:存储引擎,默认字符集 # # 关联标识、追踪数据两部分字段,分再批单独计算,以使两批字段在归档表中分别集中,以便于阅读 ## 关联标识 ct_part_1, 通过 identify_columns 计算 SET @s_sql=CONCAT(" SELECT GROUP_CONCAT( CONCAT('`',COLUMN_NAME,'` ',COLUMN_TYPE,' ' ,IF(CHARACTER_SET_NAME IS NULL,'',CONCAT('CHARACTER SET ',CHARACTER_SET_NAME) ) ,IF(COLLATION_NAME IS NULL,'',CONCAT(' COLLATE ',COLLATION_NAME) ) ,IF(IS_NULLABLE='YES',' NULL', ' NOT NULL') ) ORDER BY ORDINAL_POSITION ASC SEPARATOR ',\n ' ) INTO @rtn_value FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = '",REPLACE(source_db,'`',''),"' AND TABLE_NAME = '",REPLACE(source_table,'`',''),"' AND COLUMN_NAME IN ('", REPLACE(REPLACE(identify_columns,'`',''),",","','") ,"') "); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET ct_part_1 = @rtn_value; SET @dmesg=CONCAT(@dmesg,'---- sql retriving columns for ct_part_1 -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); ## 追踪字段 ct_part_2, 通过 track_columns 计算 SET @s_sql=CONCAT(" SELECT GROUP_CONCAT( CONCAT('`',COLUMN_NAME,'` ',COLUMN_TYPE,' ' ,IF(CHARACTER_SET_NAME IS NULL,'',CONCAT('CHARACTER SET ',CHARACTER_SET_NAME) ) ,IF(COLLATION_NAME IS NULL,'',CONCAT(' COLLATE ',COLLATION_NAME) ) ,IF(IS_NULLABLE='YES',' NULL', ' NOT NULL') ) ORDER BY ORDINAL_POSITION ASC SEPARATOR ',\n ' ) INTO @rtn_value FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = '",REPLACE(source_db,'`',''),"' AND TABLE_NAME = '",REPLACE(source_table,'`',''),"' AND COLUMN_NAME IN ('", REPLACE(REPLACE(track_columns,'`',''),",","','") ,"') "); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET ct_part_2 = @rtn_value; ## 索引部分 ct_part_3 SET ct_part_3 = CONCAT("INDEX `_identify`(", identify_columns , ",",revision_column,")"); # 计算表属性:存储引擎,默认字符集 SET @s_sql=CONCAT(" SELECT substring(TABLE_COLLATION,1,locate('_',TABLE_COLLATION)-1),`ENGINE` INTO @rtn_value, @rtn_value2 FROM information_schema.`TABLES` WHERE TABLE_SCHEMA = '",REPLACE(source_db,'`',''),"' AND TABLE_NAME = '",REPLACE(source_table,'`',''),"' " ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET default_charset = @rtn_value; SET table_engine = @rtn_value2; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @dmesg=CONCAT(@dmesg,'---- ct_part_1,2,3 -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,ct_part_1,'\n\n'); SET @dmesg=CONCAT(@dmesg,ct_part_2,'\n\n'); SET @dmesg=CONCAT(@dmesg,ct_part_3,'\n\n'); # 拼接完整的建表语句(归档表),并执行 ## 注意CREATE前不要带换行符,否则快照表创建语句的构造将异常 SET @s_sql=CONCAT( 'CREATE TABLE IF NOT EXISTS ', CONCAT(target_db,'.',target_table), ' ( ',archive_table_pk,' int(11) NOT NULL AUTO_INCREMENT, ',revision_column,' bigint(20) NOT NULL DEFAULT 0, ',del_flag_column,' TINYINT(1) NOT NULL DEFAULT 0, ',ct_part_1,', ',ct_part_2,', PRIMARY KEY (',archive_table_pk,'), ',ct_part_3,', INDEX (',revision_column,', ',del_flag_column,') ) ENGINE=',table_engine,' DEFAULT CHARSET=',default_charset,' '); # SELECT @s_sql; PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; # 保存语句,后面创建快照表参考 SET archive_table_create_sql=@s_sql; SET @dmesg=CONCAT(@dmesg,'---- sql for create archive table -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # *** 2. 检出上次归档的快照数据,包括已删除行 # TODO 这是个潜在的低性能点:如果数据表曾经删除过非常多行,检出时,这些行都将出现在快照表中,可能会比较影响性能。 SET @s_sql=CONCAT('DROP TABLE IF EXISTS ', snap_table ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; # 构造快照表(与归档表完全一致,使用变量 archive_table_create_sql )、临时表(从归档表中筛选到的行主键) SET p1=LOCATE('\n',archive_table_create_sql); SET @s_sql=CONCAT( 'CREATE TABLE IF NOT EXISTS ', snap_table, ' (' ,SUBSTRING(archive_table_create_sql,p1) , " COMMENT 'snapshot table for the last revision from archive_table, can be deleted safely after procedure finished run ' " ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @s_sql=CONCAT('DROP TABLE IF EXISTS ', snap_checking_table ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @s_sql=CONCAT( 'CREATE TABLE ', snap_checking_table, ' ( ',revision_column,' bigint(20) NOT NULL DEFAULT 0, ',ct_part_1,', ',ct_part_3,' ) ENGINE=',table_engine,' DEFAULT CHARSET=',default_charset , " COMMENT 'snapshot table for the last revision from ",CONCAT(target_db,'.',target_table),", can be deleted safely after procedure finished run ' " ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; # 导入快照数据 SET @s_sql=CONCAT( 'INSERT INTO ', snap_checking_table, ' (', identify_columns, ', ',revision_column,') SELECT ', identify_columns, ', max(',revision_column,') AS ',revision_column,' FROM ',CONCAT(target_db,'.',target_table),' GROUP BY ', identify_columns, '' ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @s_sql=CONCAT( 'INSERT INTO ', snap_table, ' SELECT d.* FROM ',CONCAT(target_db,'.',target_table),' d INNER JOIN ',snap_checking_table,' sn ON ',sp_identify_join,' AND d.',revision_column,'=sn.',revision_column,'' ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- sql for INSERT INTO snap_checking_table table -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # *** 3. 新表与快照表比较数据差异:新增、改变、删除 # 新增及有改变的数据 SET @s_sql=CONCAT( 'INSERT INTO ', CONCAT(target_db,'.',target_table), ' (',revision_column,', ',identify_columns,', ',track_columns,') SELECT ',rev_stamp,',d.',REPLACE(identify_columns,',',',d.'),', d.',REPLACE(track_columns,',',',d.'),' FROM ',CONCAT(source_db,'.',source_table),' d LEFT JOIN ',snap_table,' sn ON ',sp_identify_join,' WHERE sn.',archive_table_pk,' IS NULL OR ',sp_track_filter,' '); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- sql for Archive NEW DATA -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # 删除的数据 SET @s_sql=CONCAT( 'INSERT INTO ', CONCAT(target_db,'.',target_table), ' (',revision_column,',',del_flag_column,', ',identify_columns,', ',track_columns,') SELECT ',rev_stamp,',1,sn.',REPLACE(identify_columns,',',',sn.'),', sn.',REPLACE(track_columns,',',',sn.'),' FROM ',CONCAT(source_db,'.',source_table),' d RIGHT JOIN ',snap_table,' sn ON ',sp_identify_join,' WHERE sn.',del_flag_column,'=0 AND d.',REPLACE(identify_columns,',',' IS NULL AND d.'),' IS NULL'); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- sql for Mark Deleted DATA -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # *** 4. 清理临时表 SET @s_sql=CONCAT('DROP TABLE IF EXISTS ', snap_table ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @s_sql=CONCAT('DROP TABLE IF EXISTS ', snap_checking_table,'; ' ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- done -------------- ','\n\n'); SELECT @dmesg; END // DELIMITER ; # select 112 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ DROP PROCEDURE IF EXISTS revision_dump; DELIMITER // CREATE PROCEDURE revision_dump( IN archive_location VARCHAR(256), IN dump_location VARCHAR(256), IN identify_columns TEXT, IN track_columns TEXT, IN revision BIGINT, OUT mesg MEDIUMTEXT ) BEGIN /* 存储过程说明 按revision检出版本快照 参数说明 IN archive_location VARCHAR(256), 归档目标表,第一次执行时会自动创建;规则同上。 IN dump_location VARCHAR(256), 倒出的快照表名,自动创建,与归档表archive_target结构一致,允许带数据库名 IN identify_columns TEXT, 惟一标识数据的字段,支持多个字段,以英文逗号分隔,逗号前后不要带空格。 IN track_columns TEXT, 追踪数据的字段,只有(且只要)这些字段中数据有改变,即自动留存新版本,否则不会;规则同上。 IN revision BIGINT, 检出版本号,按BIGINT数字传递,即检出该版本的归档时的数据快照 OUT mesg MEDIUMTEXT 调试消息,执行log */ # 声明配置参数,用于存储数据源变量及归档目标变量(通过传入参数 data_source, archive_target 计算) DECLARE archive_db VARCHAR(512); DECLARE archive_table VARCHAR(64); DECLARE snap_db VARCHAR(512); DECLARE snap_table VARCHAR(64); DECLARE current_db VARCHAR(512) DEFAULT ''; # 声明配置参数,归档表字段名称:主键,版本号,删除标识。 可在后面赋值处修改,推荐保持默认。 DECLARE revision_column VARCHAR(64); DECLARE del_flag_column VARCHAR(64); DECLARE archive_table_pk VARCHAR(64); # 倒出快照表用的临时表,或许不需要??? DECLARE snap_checking_table VARCHAR(64) DEFAULT ''; # 定义两个 用于拆分字符串的起止位置 的整形变量 DECLARE p1 int DEFAULT 0; DECLARE p2 int DEFAULT 0; # sql 部件们 ## 关联列 JOIN 条件, track 列的过滤条件 DECLARE sp_identify_join TEXT DEFAULT ''; DECLARE sp_track_filter TEXT DEFAULT ''; # DECLARE archive_table_create_sql TEXT DEFAULT ''; # 一些临时变量等 DECLARE column_name VARCHAR(128) DEFAULT ''; /* # DECLARE @s_sql TEXT DEFAULT ''; # 建表用的sql部件字符串 ct_part_* : create_table_part1,2,3 # DECLARE ct_part_1 TEXT DEFAULT ''; # DECLARE ct_part_2 TEXT DEFAULT ''; # DECLARE ct_part_3 TEXT DEFAULT ''; # DECLARE table_engine VARCHAR(32) DEFAULT ''; # DECLARE default_charset VARCHAR(32) DEFAULT ''; */ # 版本序列 DECLARE rev_stamp BIGINT DEFAULT 0; # *** 0. 检查及准备环境相关参数 # 调试消息 SET @dmesg=''; # 定义归档表字段名称:主键,版本号,删除标识; (如果确信有必要,可以修改这几个名称,当然要与归档存储过程中一致;推荐保持不变) SET archive_table_pk='`_id`'; SET revision_column='`_revision`'; SET del_flag_column='`_del`'; # 计算当前数据库名存入变量current_db SELECT database() into current_db; SET rev_stamp=revision; # 归档表位置:数据库名,表名 SET p1=LOCATE('.',archive_location); IF p1 > 0 THEN SET archive_db=SUBSTRING(archive_location,1,p1-1); SET archive_table=SUBSTRING(archive_location,p1+1); ELSE SET archive_db=current_db; SET archive_table=archive_location; END IF; SET p1=LOCATE('.',dump_location); IF p1 > 0 THEN SET snap_db=SUBSTRING(dump_location,1,p1-1); SET snap_table=SUBSTRING(dump_location,p1+1); ELSE SET snap_db=current_db; SET snap_table=dump_location; END IF; SET snap_checking_table=IF(RIGHT(snap_table,1)='`' ,CONCAT(SUBSTRING(snap_table,1,CHAR_LENGTH(snap_table)-1),'_checking_out`') ,CONCAT(snap_table,'_checking_out') ); SET @dmesg=CONCAT(@dmesg,'current_db: ',current_db,'\n'); SET @dmesg=CONCAT(@dmesg,'archive_location: ',archive_db,'..',archive_table,'\n'); SET @dmesg=CONCAT(@dmesg,'dump_location: ',snap_db,'..',snap_table,' ~ ',snap_checking_table,'\n'); SET @dmesg=CONCAT(@dmesg,'rev_stamp: ',rev_stamp,'\n\n'); # 计算 sp_identify_join SET p1=1; SET p2=0; REPEAT SET p2=locate(',',identify_columns,p1); SET column_name=IF(p2=0, SUBSTRING(identify_columns,p1), SUBSTRING(identify_columns,p1,p2-p1) ); SET sp_identify_join=CONCAT(sp_identify_join ,'AND ', 'd.' , column_name, '=' , 'sn.', column_name, ' '); SET p1=p2+1; UNTIL p2 <= 0 END REPEAT; SET sp_identify_join=SUBSTRING(sp_identify_join,5); # 截掉前 4 字符的 "AND " # 计算 sp_track_filter SET p1=1; SET p2=0; REPEAT SET p2=locate(',',track_columns,p1); SET column_name=IF(p2=0, SUBSTRING(track_columns,p1), SUBSTRING(track_columns,p1,p2-p1) ); SET sp_track_filter=CONCAT(sp_track_filter ,'OR ', 'd.' , column_name, '!=' , 'sn.', column_name, ' '); SET p1=p2+1; UNTIL p2 <= 0 END REPEAT; SET sp_track_filter=SUBSTRING(sp_track_filter,4); # 截掉前 3 字符的 "OR " SET @dmesg=CONCAT(@dmesg,sp_identify_join,'\n\n'); SET @dmesg=CONCAT(@dmesg,sp_track_filter,'\n\n'); # *** 1. 准备表结构:检出快照表、临时表,临时表使用快照表结构,删除数字字段,这样实现简单些。 SET @dmesg=CONCAT(@dmesg,'---- CREATE checkout table -------------- ','\n\n'); SET @s_sql=CONCAT('DROP TABLE IF EXISTS ',snap_db,'.',snap_table,''); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @s_sql=CONCAT('DROP TABLE IF EXISTS ',snap_db,'.',snap_checking_table,''); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @s_sql=CONCAT('CREATE TABLE ',snap_db,'.',snap_table,' LIKE ',archive_db,'.',archive_table,' '); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @s_sql=CONCAT('CREATE TABLE ',snap_db,'.',snap_checking_table,' LIKE ',archive_db,'.',archive_table,' '); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @s_sql=CONCAT('ALTER TABLE ',snap_db,'.',snap_checking_table,' DROP ',del_flag_column,', DROP ', REPLACE(track_columns,',',', DROP ') ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @dmesg=CONCAT(@dmesg,'---- CREATE checkout table -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # 导入快照临时数据 SET @s_sql=CONCAT( 'INSERT INTO ',snap_db,'.', snap_checking_table, ' (', identify_columns, ', ',revision_column,') SELECT ', identify_columns, ', max(',revision_column,') AS ',revision_column,' FROM ',archive_db,'.',archive_table,' WHERE ',revision_column,' <= ',rev_stamp,' GROUP BY ', identify_columns, '' ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- CREATE checkout TMP table -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); SET @s_sql=CONCAT(' INSERT INTO ', snap_table, ' SELECT d.* FROM ',CONCAT(archive_db,'.',archive_table),' d INNER JOIN ',snap_db,'.',snap_checking_table,' sn ON ',sp_identify_join,' AND d.',revision_column,'=sn.',revision_column,' AND d.',del_flag_column,'=0 ORDER BY d.',archive_table_pk,' ASC '); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @dmesg=CONCAT(@dmesg,'---- sql for INSERT INTO snap_checking_table table -------------- ','\n\n'); SET @dmesg=CONCAT(@dmesg,@s_sql,'\n\n'); # *** 4. 清理临时表 /* 调试阶段不清 SET @s_sql=CONCAT('DROP TABLE IF EXISTS ', snap_checking_table,'; ' ); PREPARE stmt FROM @s_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; */ SET @dmesg=CONCAT(@dmesg,'---- done -------------- ','\n\n'); SELECT @dmesg; END // DELIMITER ; # select finished