DB2重跑数据-数据分区的设计
1.背景
系统重新跑批时会导致当天的数据重复,单表数据量约2G,故重跑数据时需要先删除当天的数据,再重新跑当天的数,然后加载在业务表中。
2.思路
我们可以根据数据日期对业务表进行数据分区,每天跑数的时候先删除分区(即删除当天的数据),然后重新给业务表添加分区,然后再跑当天的数据。
3.实现
单个业务表每天数据量最大会有2G左右,数据量较大,故而先把数据分区分离到目标表中进行删除。
- 把业务表的分区拆到目标表,
- 删掉目标表。
- 给业务表重新添加分区。
3.代码(存储过程中的实现)
添加分区
CREATE OR REPLACE PROCEDURE "REPORT"."PROC_ADD_TABLE_PARTITION" (
IN "P_AS_OF_DATE" DATE,
IN "P_SCHEMA" VARCHAR(20),
IN "P_TABLE_NAME" VARCHAR(80) )
/*=====================================================================+
函数名称:PROC_ADD_TABLE_PARTITION
参 数:
输入参数 P_AS_OF_DATE 数据日期
输入参数 P_SCHEMA SCHEMA名称
输入参数 P_TABLE_NAME 表名
返 回 值:
功能描述:添加分区,如果分区存在则删除分区重新创建
目 标 表:
源 表:
编写人员:
编写日期:
修改历史:
修改日期:
修改人员:
修改原因:
-----------------------------------------------------------------------
-----------------------------------------------------------------------
+=====================================================================*/
SPECIFIC SQL220830152116953
BEGIN
/*************************开始定义变量*********/
DECLARE V_SQL VARCHAR(200);--sql
DECLARE SQLERRM VARCHAR(2000); --SQL错误信息
DECLARE V_COUNT INTEGER; --计数
DECLARE V_DPT_PID INTEGER DEFAULT -1; -- 该分区的dpid
DECLARE V_COUNT_TEMP INTEGER; --计数临时表
DECLARE V_TEMP_TABLE VARCHAR(80); --临时表
DECLARE V_PARTITION VARCHAR(15); --分区名称
DECLARE V_DATE VARCHAR(10);--字符串日期
DECLARE V_TIMESTAMP TIMESTAMP; --时间
DECLARE RET_FLG INTEGER DEFAULT 0; --
DECLARE RET_MSG VARCHAR(1024) DEFAULT ''; --
/*************************结束变量定义**********/
--异常处理
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS EXCEPTION 1 SQLERRM = MESSAGE_TEXT; -- 取得错误信息
ROLLBACK;-- 回滚
CALL REPORT.PROC_ETL_LOG(P_AS_OF_DATE, 'PROC_ADD_TABLE_PARTITION-'||UPPER(P_TABLE_NAME), UPPER(P_TABLE_NAME)||'添加分区', 0, 0, -1, 1, SQLERRM); -- 记录日志
END;--
/*初始化变量--开始*/
SET V_TEMP_TABLE = 'TEMP'||UPPER(P_TABLE_NAME);--
SET V_PARTITION = 'P'||TO_CHAR(P_AS_OF_DATE,'YYYYMMDD');--
SET V_DATE = TO_CHAR(P_AS_OF_DATE,'YYYY-MM-DD');--
/*初始化变量--结束*/
--开始日志
CALL REPORT.PROC_ETL_LOG(P_AS_OF_DATE, 'PROC_ADD_TABLE_PARTITION-'||UPPER(P_TABLE_NAME), UPPER(P_TABLE_NAME)||'添加分区', 0, 0, 0, '', ''); -- 记录日志
--查看临时表是否存在,如果存在需要先删除临时表,否则会报错
SELECT COUNT(*) INTO V_COUNT_TEMP FROM SYSCAT.TABLES B WHERE B.TABNAME = V_TEMP_TABLE AND B.TABSCHEMA = UPPER(P_SCHEMA);--
--V_COUNT_TEMP>0临时表存在
IF V_COUNT_TEMP > 0 THEN
--删除临时表
EXECUTE IMMEDIATE 'DROP TABLE '||UPPER(P_SCHEMA)||'.'||V_TEMP_TABLE;--
COMMIT; --
END IF;--
--查看分区是否存在
SELECT COUNT(*) INTO V_COUNT FROM SYSCAT.DATAPARTITIONS A WHERE A.TABNAME =UPPER(P_TABLE_NAME) AND A.TABSCHEMA =UPPER(P_SCHEMA) AND A.DATAPARTITIONNAME = V_PARTITION;--
--V_COUNT>0分区存在
IF V_COUNT > 0 THEN
SELECT DATAPARTITIONID INTO V_DPT_PID
FROM SYSCAT.DATAPARTITIONS
WHERE TABSCHEMA =UPPER(P_SCHEMA)
AND TABNAME = UPPER(P_TABLE_NAME)
AND DATAPARTITIONNAME=V_PARTITION;--
--删除分区,将分区移入临时表
EXECUTE IMMEDIATE 'ALTER TABLE '||UPPER(P_SCHEMA)||'.'||UPPER(P_TABLE_NAME)||' DETACH PARTITION '||V_PARTITION||' INTO '||UPPER(P_SCHEMA)||'.'||V_TEMP_TABLE;--
COMMIT;--
--分区移入临时表有一定延迟,此处等待一会后删除临时表
/* --记录当前时间
SET V_TIMESTAMP = CURRENT TIMESTAMP;--
--循环等待,根据时间判断,等待5秒
WHILE (TIMESTAMPDIFF(2,CHAR(CURRENT TIMESTAMP - V_TIMESTAMP))) <=10 DO
END WHILE; */
CALL REPORT.SP_PRC_WAITDETACH(UPPER(P_SCHEMA),UPPER(P_TABLE_NAME),V_DPT_PID,RET_FLG,RET_MSG); --
--删除临时表,完成删除分区
EXECUTE IMMEDIATE 'DROP TABLE '||UPPER(P_SCHEMA)||'.'||V_TEMP_TABLE;--
COMMIT; --
END IF;--
--创建分区
EXECUTE IMMEDIATE 'ALTER TABLE '||UPPER(P_SCHEMA)||'.'||UPPER(P_TABLE_NAME)||' ADD PARTITION '||V_PARTITION||' STARTING('''||V_DATE||''') ENDING('''||V_DATE||''')' ;--
COMMIT;--
--结束日志
CALL REPORT.PROC_ETL_LOG(P_AS_OF_DATE, 'PROC_ADD_TABLE_PARTITION-'||UPPER(P_TABLE_NAME), UPPER(P_TABLE_NAME)||'添加分区', 0, 0, 1, '0', '执行成功'); -- 记录日志
END
检查分区分离
CREATE OR REPLACE PROCEDURE "REPORT"."SP_PRC_WAITDETACH" (
IN I_SCMNAM VARCHAR(128),
IN I_TABNAM VARCHAR(128),
IN I_PATID INTEGER DEFAULT -1,
OUT O_RTCD INTEGER,
OUT O_SYSMSG VARCHAR(128) )
SPECIFIC SQL220830151953749
BEGIN
DECLARE V_DPID int;--
DECLARE V_DPSTS char;--
DECLARE DONE boolean DEFAULT FALSE;--
DECLARE V_CURCLOSE boolean DEFAULT FALSE;--
DECLARE oldLockTimeout INTEGER;--
DECLARE allDetachCheck CURSOR FOR
select DATAPARTITIONID, STATUS FROM syscat.datapartitions
where tabschema = I_SCMNAM and tabname = I_TABNAM and
(status = 'L' OR status = 'D') WITH CS;--
DECLARE oneDetachCheck CURSOR FOR
select DATAPARTITIONID, STATUS FROM syscat.datapartitions
where tabschema = I_SCMNAM and tabname = I_TABNAM and
DATAPARTITIONID = I_PATID WITH CS;--
-- Add signal handlers FOR errors that you consider non-fatal here
-- example FOR no row found and lock timeout shown below
DECLARE continue handler FOR NOT FOUND
BEGIN
-- If no record was found there is nothing to wait FOR, so we are DONE
SET DONE = TRUE;--
END;--
DECLARE continue handler FOR SQLSTATE '40001'
BEGIN
-- on a lock timeout we are not DONE, we want to loop again
SET DONE = FALSE;--
-- the CURSOR will be CLOSEd as part of the rollback during the lock timeout
SET V_CURCLOSE = TRUE;--
END;--
-- initialize return code to 0
SET O_RTCD = 0;--
-- save CURRENT lock timeout value
values CURRENT lock timeout INTO oldLockTimeout;--
SET CURRENT lock timeout -1;--
-- if table does not exist in syscat.datapartitions, return error
IF NOT EXISTS(select DATAPARTITIONID FROM syscat.datapartitions
where tabschema = I_SCMNAM and tabname = I_TABNAM
WITH UR)
THEN
--IF tableExists (I_SCMNAM, I_TABNAM) = 0
SET O_SYSMSG = 'Table ' || I_SCMNAM || '.' || I_TABNAM || ' not found';--
SET O_RTCD = -1;--
GOTO EXIT;--
END IF;--
WHILE DONE = FALSE DO
SET V_CURCLOSE = FALSE;--
IF I_PATID <> -1
THEN
OPEN oneDetachCheck;--
FETCH oneDetachCheck INTO V_DPID, V_DPSTS;--
-- two cases here:
-- (i) detach has already completed hence partition entry not
-- found in catalogs (indicated by DONE = TRUE, handled later)
-- (ii) detach in progress, partition state should not be visible
IF DONE <> TRUE AND (V_DPSTS = '' OR V_DPSTS = 'A')
THEN
SET O_SYSMSG = 'Cannot SP_PRC_WAITDETACH if DETACH was not issued on ' ||
'DATAPARTITIONID ' || I_PATID;--
SET O_RTCD = -1;--
GOTO EXIT;--
END IF;--
IF V_CURCLOSE <> TRUE
THEN
CLOSE oneDetachCheck;--
END IF;--
ELSE
OPEN allDetachCheck;--
FETCH allDetachCheck INTO V_DPID, V_DPSTS;--
IF V_CURCLOSE <> TRUE
THEN
CLOSE allDetachCheck;--
END IF;--
END IF;--
END WHILE;--
IF I_PATID <> -1
THEN
SET O_SYSMSG = 'DETACH completed on DATAPARTITIONID ' || I_PATID;--
ELSE
SET O_SYSMSG = 'All detach operations completed on table ' ||
I_SCMNAM || '.' || I_TABNAM;--
END IF;--
EXIT:
-- restore lock timeout value
SET CURRENT lock timeout oldLockTimeout;--
RETURN O_RTCD;--
END
4.说明
- 为了使 ALTER TABLE…DETACH 在 DB2® 版本 9.7 FP 1 及更高发行版中尽可能快地执行,异步分区拆离任务以异步方式完成拆离操作。故在删除目标表前需要调用SP_PRC_WAITDETACH检查数据分区分离状态。
- SP_PRC_WAITDETACH中为防止锁超时导致的操作失败,timeout loke设为不会超时,操作完成后再恢复。