P_TRUNCATE_TABLES_DAY.sql 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. --/
  2. CREATE PROCEDURE P_TRUNCATE_TABLES_DAY IS
  3. V_END_DATE DATE;
  4. V_PARTVALUE DATE;
  5. V_TABLENAME VARCHAR2(200);
  6. V_PARTNAME VARCHAR2(200);
  7. V_SQL VARCHAR2(2000);
  8. V_HIGH_VALUE VARCHAR2(2000);
  9. V_MONTH NUMBER;
  10. V_CURR_DATE DATE := SYSDATE;
  11. V_ERR VARCHAR2(200);
  12. /*每日1点定时清理表表T_TRUNCATE_TABLES_DAY中表的数据*/
  13. BEGIN
  14. UPDATE T_TRUNCATE_TABLES_DAY SET SQLTEXT = NULL, TRUN_DATE = NULL;
  15. COMMIT;
  16. FOR CUR_TABLES IN (SELECT TABLE_NAME, TYPE_CODE, TYPE_NUM, PART
  17. FROM T_TRUNCATE_TABLES_DAY
  18. WHERE ISDELETE = 1) LOOP
  19. V_MONTH := CUR_TABLES.TYPE_NUM;
  20. V_TABLENAME := CUR_TABLES.TABLE_NAME;
  21. --取得需要清理的数据的日期范围
  22. IF CUR_TABLES.TYPE_CODE = 'MONTH' THEN
  23. SELECT TRUNC(ADD_MONTHS(V_CURR_DATE, -V_MONTH))
  24. INTO V_END_DATE
  25. FROM DUAL;
  26. ELSIF CUR_TABLES.TYPE_CODE = 'DAY' THEN
  27. SELECT TRUNC(V_CURR_DATE - V_MONTH) INTO V_END_DATE FROM DUAL;
  28. END IF;
  29. --删除分区表数据
  30. IF CUR_TABLES.PART = 0 THEN
  31. --取得分区表的分区名称
  32. FOR CUR_PARTNAME IN (SELECT PARTITION_NAME, HIGH_VALUE
  33. FROM USER_TAB_PARTITIONS
  34. WHERE TABLE_NAME = V_TABLENAME
  35. AND (PARTITION_NAME LIKE 'SYS_P%'
  36. --OR PARTITION_NAME LIKE 'FM_ALARM_HISTORY%'
  37. )
  38. ORDER BY PARTITION_NAME) LOOP
  39. V_HIGH_VALUE := CUR_PARTNAME.HIGH_VALUE;
  40. V_PARTVALUE := TO_DATE(SUBSTR(V_HIGH_VALUE, 11, 10), 'YYYY-MM-DD');
  41. V_PARTNAME := CUR_PARTNAME.PARTITION_NAME;
  42. IF V_PARTVALUE <= V_END_DATE then
  43. --表被锁定,等2秒后重试10次
  44. FOR I IN 1 .. 20 LOOP
  45. BEGIN
  46. V_SQL := 'ALTER TABLE ' || V_TABLENAME || ' DROP PARTITION ' || V_PARTNAME || ' UPDATE INDEXES PARALLEL 3';
  47. /*
  48. add by dufs 2018-10-20 由于 FM_ALARM_HISTORY、FM_ALARM_PERMANENT表访问频率很高,且不断有超期数据要写入
  49. 如果直接drop超期的分区,写入超期数据时要新增分区,而且数量极大, FM_ALARM_HISTORY 每天要删除的达到400多个,删了400多个后,还要陆续增加上来
  50. 如果改为truncate,不用新增分区,但积累的分区会越来越多,每次操作耗时、锁表会越来越严重
  51. 改成分区设置时间与结束时间相告,即每次只清刚好超期的那一个分区,既可以达到清空分区减少存储数据的目标,又能降低操作次数,从几百次操作减少到1次
  52. */
  53. if V_TABLENAME='FM_ALARM_HISTORY' or V_TABLENAME='FM_ALARM_PERMANENT'
  54. then
  55. --dbms_output.put_line('V_PARTVALUE '||to_char(V_PARTVALUE,'yyyy-mm-dd hh24:mi:ss'));
  56. --dbms_output.put_line('V_END_DATE-1'||to_char(V_END_DATE-1,'yyyy-mm-dd hh24:mi:ss'));
  57. if V_PARTVALUE <= V_END_DATE-1
  58. then
  59. exit;
  60. end if;
  61. V_SQL := 'ALTER TABLE ' || V_TABLENAME || ' TRUNCATE PARTITION ' || V_PARTNAME || ' UPDATE GLOBAL INDEXES PARALLEL 3';
  62. end if;
  63. dbms_output.put_line(V_SQL);
  64. EXECUTE IMMEDIATE V_SQL;
  65. UPDATE T_TRUNCATE_TABLES_DAY
  66. SET SQLTEXT = V_PARTNAME ,
  67. TRUN_DATE = V_CURR_DATE
  68. WHERE TABLE_NAME = V_TABLENAME;
  69. COMMIT;
  70. EXIT;
  71. EXCEPTION
  72. WHEN OTHERS THEN
  73. V_ERR := SUBSTR(SQLERRM, 1, 200);
  74. IF SUBSTR(V_ERR, 1, 9) = 'ORA-00054' THEN
  75. IF I = 10 THEN
  76. INSERT INTO T_ISS_SQLERRM VALUES (SEQ_T_ISS_SQLERRM.NEXTVAL,'P_TRUNCATE_TABLES_DAY',V_ERR,SYSDATE);
  77. COMMIT;
  78. END IF;
  79. DBMS_LOCK.SLEEP(2);
  80. CONTINUE;
  81. ELSE
  82. INSERT INTO T_ISS_SQLERRM VALUES (SEQ_T_ISS_SQLERRM.NEXTVAL,'P_TRUNCATE_TABLES_DAY',V_ERR,SYSDATE);
  83. COMMIT;
  84. EXIT;
  85. END IF;
  86. END;
  87. END LOOP;
  88. END IF;
  89. END LOOP;
  90. END IF;
  91. END LOOP;
  92. EXCEPTION
  93. WHEN OTHERS THEN
  94. V_ERR := SUBSTR(SQLERRM, 1, 200);
  95. ROLLBACK;
  96. INSERT INTO T_ISS_SQLERRM
  97. VALUES
  98. (SEQ_T_ISS_SQLERRM.NEXTVAL, 'P_TRUNCATE_TABLES_DAY', V_ERR, SYSDATE);
  99. COMMIT;
  100. END;
  101. /