Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Database Procedure - Automatic Database Maintenance - linkedin

225 views

Published on

  • Be the first to comment

  • Be the first to like this

Database Procedure - Automatic Database Maintenance - linkedin

  1. 1. Page 1 Author: Ardiel Soodyall Database Procedure: Automatic Database Maintenance Version 2.01 (10-04-2015) Function The database package db_maintenance comprises of the driving database procedure action_weekend_tasks which currently performs the following actions on specified segments (tables and indexes): 1) Shrinking (reorganization of segments) 2) Gathering optimizer statistics Owner The DBMAINT schema owns this procedure Call From the SQL*Plus prompt, you may execute the following call to action the procedure. SQL> exec dbmaint.db_maintenance.action_weekend_tasks(p_preview => true); Note: 1. Whenever the p_preview parameter is set to TRUE a listing of the intended actions is generated instead of actually executing the relevant operations. 2. If you are using SQL*Plus, set the following environment “set serveroutput on” and “set feedback on” to echo to the monitor the abovementioned. Assumptions The following assumptions are applicable:  The user/schema dbmaint does not exist  The tablespaces USERS and TEMP exists
  2. 2. Page 2 Author: Ardiel Soodyall  At least Oracle Database Version 11g Release 1 Scheduling This action is integrated into the Oracle Scheduler via the job SYS.AUTOMATIC MAINTENANCE. The scheduler executes the following PL/SQL code: begin Insert into dbmaint.db_maintenance_control_list (segment_owner, segment_name, segment_type, segment_shrink_flag) SELECT segment_owner, segment_name, segment_type,'Y' FROM TABLE (DBMS_SPACE.asa_recommendations ('FALSE', 'FALSE', 'FALSE')) WHERE segment_type in ('TABLE','INDEX') And recommendations like '%shrink%'; commit; dbmaint.db_maintenance.action_weekend_tasks(p_preview => false); end; The full deployment script is found in the Error! Reference source not found.. Note: The window db_automatic_maintenance opens every Sunday at 8am and closes 16 hours later on Sunday at 10pm. In addition, the maintenance operations terminate immediately on closure of the window. Algorithm The following steps are applicable: PRE-CONDITION: A control list is populated with candidate table and index names to be shrunk or for optimizer statistics to be gathered on them. In addition, a status flag is also populated with a (Y)es or (N)o value. These are the only two values permitted and it’s controlled via a check constraint. MAIN PROCESS: The control list is read through from beginning to end. The following actions are executed on all segments where their relevant status flag values set to (Y)es and not in the exclusion list (in the case of the shrink operation) a. Shrink segment b. Gather optimizer statistics POST-CONDITION: Either the shrink or gather optimizer statistics operation is successful or not. If the operation is successful then the relevant status flag is set to (N)o and a date stamp is set; otherwise, the error log table is populated with the relevant database error code and error message on the failure.
  3. 3. Page 3 Author: Ardiel Soodyall Populating the Control List Execute the following SQL code to populate the control list from the outcome of the last Segment Advisor run. Insert into dbmaint.db_maintenance_control_list (segment_owner, segment_name, segment_type, segment_shrink_flag) SELECT segment_owner, segment_name, segment_type,'Y' FROM TABLE (DBMS_SPACE.asa_recommendations ('FALSE', 'FALSE', 'FALSE')) WHERE segment_type in ('TABLE','INDEX') And recommendations like '%shrink%'; Commit; This code cannot be integrated into the database package because of a bug found in this version of the database (11.2.0.3). The bug Metalink reference is 13840704. Incidentally, this issue is fixed in Oracle database version 11.2.0.4. However, it can be run as an anonymous block of PL/SQL via SQL*Plus. To gather optimizer statistic for the specified table, we execute the following SQL code to populate the control list to gather optimizer statistic for the specified table: INSERT INTO "DBMAINT"."DB_MAINTENANCE_CONTROL_LIST" (SEGMENT_OWNER, SEGMENT_NAME, SEGMENT_TYPE, SEGMENT_SHRINK_FLAG, SEGMENT_STATS_FLAG) VALUES ('DBMAINT', 'DB_MAINTENANCE_ERROR_LOG', 'TABLE', 'N', 'Y'); COMMIT; Database Objects Tabularized below Table 1 in are the database objects created. Table 1: List of Database Objected Created Object Name Object Type Owner DB_MAINTENANCE PL/SQL package DBMAINT ACTIONS_WEEKEND_TASKS Procedure DBMAINT DB_MAINTENANCE_CONTROL_LIST Table DBMAINT DB_MAINTENANCE_EXCLUSION_LIST Table DBMAINT DB_MAINTENANCE_ERROR_LOG Table DBMAINT AUTOMATIC_MAINTENANCE Oracle Job DBMAINT DB_AUTOMATIC_MAINTENANCE Window DBMAINT Privileges Grant the following quota and privileges to the DBMAINT user: -- QUOTAS ALTER USER DBMAINT QUOTA UNLIMITED ON USERS; -- ROLES
  4. 4. Page 4 Author: Ardiel Soodyall grant RESOURCE,CONNECT to dbmaint; -- SYSTEM PRIVILEGES grant alter any table to dbmaint; grant execute on dbms_lock to dbmaint; grant select any dictionary to dbmaint; grant execute on dbms_space to dbmaint; grant analyze any to dbmaint; Backup This solution is backed up as part of the database Recovery Manager (RMAN) backup because its constituent components are all database objects such as:  Oracle Job  PL/SQL package  Procedures  Tables  Window Source Code The code for the package DB_MAINTENANCE follows: create or replace PACKAGE DB_MAINTENANCE AS /****************************************************************************** NAME: DB_maintenance PURPOSE: The purpose of this database package is to automate the maintenance of the Oracle database scheduled to run over weekends. DEPENDENCY: Table db_maintenance_control_list NOTE: issue the following SQL statement: grant alter any table to dbmaint; grant execute on dbms_lock to dbmaint; grant select any dictionary to dbmaint; grant execute on dbms_space to dbmaint; grant analyze any to dbmaint; REVISIONS: Ver Date Author Description --------- ---------- --------------- -------------------------------------- 1.0 2014/02/04 Ardiel Soodyall - Created this package. 1.1 2014/05/30 Ardiel Soodyall - Added exception to handle objects that cannot be shrunk 1.2 2014/06/29 Ardiel Soodyall - Increased segment name size variable from length 20 to 30. 1.3 2014/08/04 Ardiel Soodyall - Added functionality to gather optimizer statistics on segments 1.4 2014/08/05 Ardiel Soodyall - Removed redundant code statements *********************************************************************************/ procedure action_weekend_tasks (p_preview in boolean);
  5. 5. Page 5 Author: Ardiel Soodyall END DB_maintenance; / create or replace package body DB_maintenance as /* ------------------------------------------------------------------------------ */ procedure log_error (p_segment_owner in varchar2, p_segment_name in varchar2, p_segment_type in varchar2, p_error_message in varchar2) is begin insert into db_maintenance_error_log values (sysdate, p_segment_owner, p_segment_name, p_segment_type, p_error_message); commit; end log_error; /* ------------------------------------------------------------------------------ */ PROCEDURE record_shrink_completed( p_segment_owner IN VARCHAR2, p_segment_name IN VARCHAR2, p_segment_type IN VARCHAR2) IS BEGIN UPDATE DB_MAINTENANCE_CONTROL_LIST SET SEGMENT_LAST_SHRINK = sysdate, segment_shrink_flag = 'N' WHERE segment_owner = p_segment_owner AND segment_name = p_segment_name AND segment_type = p_segment_type AND segment_shrink_flag = 'Y'; COMMIT; exception when others then log_error (p_segment_owner,p_segment_name, p_segment_type, substr(sqlerrm,1,120)); END record_shrink_completed; /* ------------------------------------------------------------------------------ */ PROCEDURE shrink_segments (p_preview in boolean) IS /* --------------------------------------------------------------------------- This procedure performance a shrinks action on specified segments sourced from the Segment Advisor. In addition, extra control is provide to exclude specific segement residing in DB_MAINTENANCE_EXCLUSION_LIST. Exception thrown up to handle object that cannot be shrunk, such as those with function-based indexes. --------------------------------------------------------------------------- */ CURSOR cur1 IS SELECT segment_owner, segment_name, segment_type, segment_shrink_flag from DB_MAINTENANCE_CONTROL_LIST t1 where SEGMENT_SHRINK_FLAG = 'Y' and not exists (select 1 from DB_MAINTENANCE_EXCLUSION_LIST t2 where t1.SEGMENT_OWNER = t2.segment_owner and t1.segment_name = t2.segment_name and t1.segment_type = t2.segment_type); rec cur1%ROWTYPE; v_cmd_1 VARCHAR2 (200); v_cmd_2 VARCHAR2 (200); v_cmd_3 VARCHAR2 (200); v_cmd_4 VARCHAR2 (200);
  6. 6. Page 6 Author: Ardiel Soodyall v_segment_owner VARCHAR2(30); v_segment_name VARCHAR2(30); v_segment_type VARCHAR2(5); begin for rec in cur1 loop v_segment_owner := rec.segment_owner; v_segment_name := rec.segment_name; v_segment_type := rec.segment_type; if rec.segment_type = 'TABLE' then v_cmd_1 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' enable row movement'; v_cmd_2 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' shrink space compact'; v_cmd_3 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' shrink space'; if p_preview = true then dbms_output.put_line(v_cmd_1); dbms_output.put_line(v_cmd_2); dbms_output.put_line(v_cmd_3); else begin execute immediate v_cmd_1; execute immediate v_cmd_2; execute immediate v_cmd_3; record_shrink_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true elsif rec.segment_type = 'INDEX' then v_cmd_4 := 'alter index '||rec.segment_owner||'.'||rec.segment_name||' shrink space'; if p_preview = true then dbms_output.put_line(v_cmd_4); else begin execute immediate v_cmd_4; record_shrink_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true end if; -- rec.segment_type = 'INDEX' end loop; exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end shrink_segments; /* ------------------------------------------------------------------------------ */ PROCEDURE record_gather_stats_completed( p_segment_owner IN VARCHAR2, p_segment_name IN VARCHAR2, p_segment_type IN VARCHAR2) IS
  7. 7. Page 7 Author: Ardiel Soodyall BEGIN UPDATE DB_MAINTENANCE_CONTROL_LIST SET SEGMENT_LAST_STATS = sysdate, segment_stats_flag = 'N' WHERE segment_owner = p_segment_owner AND segment_name = p_segment_name AND segment_type = p_segment_type AND segment_stats_flag = 'Y'; COMMIT; exception when others then log_error (p_segment_owner,p_segment_name, p_segment_type, substr(sqlerrm,1,120)); END record_gather_stats_completed; /* ------------------------------------------------------------------------------ */ PROCEDURE gather_segment_stats (p_preview in boolean) IS /* --------------------------------------------------------------------------- This procedure performance a gather optimizer statistics action on specified segments sourced from the user. --------------------------------------------------------------------------- */ CURSOR cur2 IS SELECT segment_owner, segment_name, segment_type, segment_stats_flag from DB_MAINTENANCE_CONTROL_LIST t1 where SEGMENT_STATS_FLAG = 'Y'; rec cur2%ROWTYPE; v_cmd VARCHAR2 (200); v_segment_owner VARCHAR2(30); v_segment_name VARCHAR2(30); v_segment_type VARCHAR2(5); begin for rec in cur2 loop v_segment_owner := rec.segment_owner; v_segment_name := rec.segment_name; v_segment_type := rec.segment_type; if rec.segment_type = 'TABLE' then v_cmd := 'begin dbms_stats.gather_table_stats('''||rec.segment_owner||''','''||rec.segment_name||'''); end;'; elsif rec.segment_type = 'INDEX' then v_cmd := 'begin dbms_stats.gather_index_stats('''||rec.segment_owner||''','''||rec.segment_name||'''); end;'; end if; if p_preview = true then dbms_output.put_line(v_cmd); else begin execute immediate v_cmd; record_gather_stats_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true end loop; exception when others
  8. 8. Page 8 Author: Ardiel Soodyall then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end gather_segment_stats; /* ---------------------------------------------------------------------------- */ PROCEDURE action_weekend_tasks (p_preview in boolean) IS /* ------------------------------------------------------------------------ This procedure performs a list of database maintenance actions. ------------------------------------------------------------------------ */ BEGIN shrink_segments (p_preview); gather_segment_stats (p_preview); exception when others then log_error ('DUMMY','DUMMY', 'DUMMY', substr(sqlerrm,1,120)); END action_weekend_tasks; /* ---------------------------------------------------------------------------- */ END DB_maintenance; / The code for db_maintenance_control_list follows. CREATE TABLE "DBMAINT"."DB_MAINTENANCE_CONTROL_LIST" ( "SEGMENT_OWNER" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_NAME" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_TYPE" VARCHAR2(10 BYTE) NOT NULL ENABLE, "SEGMENT_SHRINK_FLAG" CHAR(1 BYTE) NOT NULL ENABLE, "SEGMENT_LAST_SHRINK" DATE, "SEGMENT_STATS_FLAG" CHAR(1 BYTE), "SEGMENT_LAST_STATS" DATE, CHECK (SEGMENT_SHRINK_FLAG in ('Y','N')) ENABLE, CHECK (SEGMENT_STATS_FLAG in ('Y','N')) ENABLE ) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS"; The SQL code to create the for db_maintenance_error_log table follows: CREATE TABLE "DBMAINT"."DB_MAINTENANCE_ERROR_LOG" ( "LOG_DATE" DATE, "SEGMENT_OWNER" VARCHAR2(30 BYTE), "SEGMENT_NAME" VARCHAR2(30 BYTE), "SEGMENT_TYPE" VARCHAR2(10 BYTE), "ERROR_MESSAGE" VARCHAR2(120 BYTE) ) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" ;REATE TABLE DBMAINT.DB_MAINTENANCE_ERROR_LOG; The SQL code to create the for db_maintenance_exclusion_list table follows: CREATE TABLE "DBMAINT"."DB_MAINTENANCE_EXCLUSION_LIST" ( "SEGMENT_OWNER" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_NAME" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_TYPE" VARCHAR2(30 BYTE) NOT NULL ENABLE, "DATE_EXCLUDED" DATE DEFAULT sysdate, CONSTRAINT "DB_MAINTENANCE_EXCLUSIONS_PK" PRIMARY KEY ("SEGMENT_OWNER", "SEGMENT_NAME") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" ENABLE ) SEGMENT CREATION IMMEDIATE
  9. 9. Page 9 Author: Ardiel Soodyall PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS"; The SQL code to create window follows: BEGIN DBMS_SCHEDULER.drop_window (window_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE_WNDW"', force => TRUE); END; / BEGIN DBMS_SCHEDULER.CREATE_WINDOW( window_name=>'"DB_AUTOMATIC_MAINTENANCE_WNDW"', resource_plan=>'DEFAULT_PLAN', start_date=>to_timestamp_tz('2014-02-20 +2:00', 'YYYY-MM-DD TZH:TZM'), duration=>numtodsinterval(840, 'minute'), repeat_interval=>'FREQ=WEEKLY;BYDAY=SUN;BYHOUR=8;BYMINUTE=0;BYSECOND=0', end_date=>null, window_priority=>'HIGH', comments=>'DB Automatic Maintenance'); END; / The SQL code to create job scheduler follows: BEGIN DBMS_SCHEDULER.DROP_JOB (job_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"'); END; / BEGIN sys.dbms_scheduler.create_job( job_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', job_type => 'PLSQL_BLOCK', job_action => 'begin Insert into dbmaint.db_maintenance_control_list (segment_owner, segment_name, segment_type, segment_shrink_flag) SELECT segment_owner, segment_name, segment_type,''Y'' FROM TABLE (DBMS_SPACE.asa_recommendations (''FALSE'', ''FALSE'', ''FALSE'')) WHERE segment_type in (''TABLE'',''INDEX'') And recommendations like ''%shrink%''; commit; dbmaint.db_maintenance.action_weekend_tasks(p_preview => false); end;', schedule_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE_WNDW"', job_class => '"DEFAULT_JOB_CLASS"', comments => 'DB Automatic Maintenance to Shrink Segments', auto_drop => FALSE, enabled => FALSE);
  10. 10. Page 10 Author: Ardiel Soodyall sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'raise_events', value => dbms_scheduler.job_started + dbms_scheduler.job_succeeded + dbms_scheduler.job_completed); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'logging_level', value => DBMS_SCHEDULER.LOGGING_FULL); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'job_weight', value => 1); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'stop_on_window_close', value => TRUE); sys.dbms_scheduler.enable( '"SYS"."DB_AUTOMATIC_MAINTENANCE"'); END; /
  11. 11. Page 11 Author: Ardiel Soodyall Document History Date Name Versi on Comment 13-02-2014 Ardiel Soodyall 1.0 Document Creation 14-02-2014 Ardiel Soodyall 1.1 Correct “cut & paste” errors Ardiel Soodyall 1.2 Included package specification 20-02-2014 Ardiel Soodyall 1.3 Code for db_maintenance_job_sentinal 1.4 Removed primary key from db_maintenance_control_list 1.5 Oracle Job – AUTOMATIC MAINTENANCE 1.6 Fix logic bug in PROCEDURE action_weekend_tasks 1.7 Add exclusion table 20-03-2014 Ardiel Soodyall 1.8 Eliminate code for db_maintenance_job_sentinal 30-05-2014 Ardiel Soodyall 1.9 Add exception handling for objects that cannot be shrunk 04-08-2014 Ardiel Soodyall 1.10 Add functionality to gather optimizer statistics for specified segments using new procedures record_gather_stats_completed and gather_segment_stats 04-08-2014 Ardiel Soodyall 1.11 Include updated DB_MAINTENANCE_CONTROL_LIST table 01-09-2014 Ardiel Soodyall 1.14 Include commit commands after insert statements used to populate DB_MAINTENANCE_CONTROL_LIST 25-09-2014 Ardiel Soodyall 1.14 Include window and job scheduler details 29-09-2014 Ardiel Soodyall 2.00 Turn on full logging 10-04-2015 Ardiel Soodyall 2.01 Add UNIX shell script for deployment into PBB database environment
  12. 12. Page 12 Author: Ardiel Soodyall Appendix The deployment SQL and UNIX shell script follows. ------------------------------------- Cut off here ---------------------------------------------- spool h.lst drop user dbmaint cascade; -- USER CREATE USER "DBMAINT" identified by sep192014 DEFAULT TABLESPACE "USERS" TEMPORARY TABLESPACE "TEMP" ACCOUNT UNLOCK ; -- QUOTAS ALTER USER DBMAINT QUOTA UNLIMITED ON USERS; -- ROLES grant RESOURCE,CONNECT to dbmaint; -- SYSTEM PRIVILEGES grant alter any table to dbmaint; grant execute on dbms_lock to dbmaint; grant select any dictionary to dbmaint; grant execute on dbms_space to dbmaint; grant analyze any to dbmaint; CREATE TABLE "DBMAINT"."DB_MAINTENANCE_CONTROL_LIST" ( "SEGMENT_OWNER" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_NAME" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_TYPE" VARCHAR2(10 BYTE) NOT NULL ENABLE, "SEGMENT_SHRINK_FLAG" CHAR(1 BYTE) NOT NULL ENABLE, "SEGMENT_LAST_SHRINK" DATE, "SEGMENT_STATS_FLAG" CHAR(1 BYTE), "SEGMENT_LAST_STATS" DATE, CHECK (SEGMENT_SHRINK_FLAG in ('Y','N')) ENABLE, CHECK (SEGMENT_STATS_FLAG in ('Y','N')) ENABLE ) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" ; CREATE TABLE "DBMAINT"."DB_MAINTENANCE_ERROR_LOG" ( "LOG_DATE" DATE, "SEGMENT_OWNER" VARCHAR2(30 BYTE), "SEGMENT_NAME" VARCHAR2(30 BYTE), "SEGMENT_TYPE" VARCHAR2(10 BYTE), "ERROR_MESSAGE" VARCHAR2(120 BYTE) ) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS"; -------------------------------------------------------- -- DDL for Table DB_MAINTENANCE_EXCLUSION_LIST -------------------------------------------------------- CREATE TABLE "DBMAINT"."DB_MAINTENANCE_EXCLUSION_LIST" ( "SEGMENT_OWNER" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_NAME" VARCHAR2(30 BYTE) NOT NULL ENABLE, "SEGMENT_TYPE" VARCHAR2(30 BYTE) NOT NULL ENABLE, "DATE_EXCLUDED" DATE DEFAULT sysdate,
  13. 13. Page 13 Author: Ardiel Soodyall CONSTRAINT "DB_MAINTENANCE_EXCLUSIONS_PK" PRIMARY KEY ("SEGMENT_OWNER", "SEGMENT_NAME") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" ENABLE ) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS"; --- create or replace PACKAGE dbmaint.DB_MAINTENANCE AS /****************************************************************************** NAME: DB_maintenance PURPOSE: The purpose of this database package is to automate the maintenance of the Oracle database scheduled to run over weekends. DEPENDENCY: Table db_maintenance_control_list NOTE: issue the following SQL statement: grant alter any table to dbmaint; grant execute on dbms_lock to dbmaint; grant select any dictionary to dbmaint; grant execute on dbms_space to dbmaint; grant analyze any to dbmaint; REVISIONS: Ver Date Author Description --------- ---------- --------------- -------------------------------------- 1.0 2014/02/04 Ardiel Soodyall - Created this package. 1.1 2014/05/30 Ardiel Soodyall - Added exception to handle objects that cannot be shrunk 1.2 2014/06/29 Ardiel Soodyall - Increased segment name size variable from length 20 to 30. 1.3 2014/08/04 Ardiel Soodyall - Added functionality to gather optimizer statistics on segments 1.4 2014/08/05 Ardiel Soodyall - Removed redundant code statements *********************************************************************************/ procedure action_weekend_tasks (p_preview in boolean); END DB_maintenance; / create or replace package body dbmaint.DB_maintenance as /* ------------------------------------------------------------------------------ */ procedure log_error (p_segment_owner in varchar2, p_segment_name in varchar2, p_segment_type in varchar2, p_error_message in varchar2) is begin insert into db_maintenance_error_log values (sysdate, p_segment_owner, p_segment_name, p_segment_type, p_error_message); commit; end log_error; /* ------------------------------------------------------------------------------ */ PROCEDURE record_shrink_completed( p_segment_owner IN VARCHAR2, p_segment_name IN VARCHAR2, p_segment_type IN VARCHAR2) IS BEGIN
  14. 14. Page 14 Author: Ardiel Soodyall UPDATE DB_MAINTENANCE_CONTROL_LIST SET SEGMENT_LAST_SHRINK = sysdate, segment_shrink_flag = 'N' WHERE segment_owner = p_segment_owner AND segment_name = p_segment_name AND segment_type = p_segment_type AND segment_shrink_flag = 'Y'; COMMIT; exception when others then log_error (p_segment_owner,p_segment_name, p_segment_type, substr(sqlerrm,1,120)); END record_shrink_completed; /* ------------------------------------------------------------------------------ */ PROCEDURE shrink_segments (p_preview in boolean) IS /* --------------------------------------------------------------------------- This procedure performance a shrinks action on specified segments sourced from the Segment Advisor. In addition, extra control is provide to exclude specific segement residing in DB_MAINTENANCE_EXCLUSION_LIST. Exception thrown up to handle object that cannot be shrunk, such as those with function-based indexes. --------------------------------------------------------------------------- */ CURSOR cur1 IS SELECT segment_owner, segment_name, segment_type, segment_shrink_flag from DB_MAINTENANCE_CONTROL_LIST t1 where SEGMENT_SHRINK_FLAG = 'Y' and not exists (select 1 from DB_MAINTENANCE_EXCLUSION_LIST t2 where t1.SEGMENT_OWNER = t2.segment_owner and t1.segment_name = t2.segment_name and t1.segment_type = t2.segment_type); rec cur1%ROWTYPE; v_cmd_1 VARCHAR2 (200); v_cmd_2 VARCHAR2 (200); v_cmd_3 VARCHAR2 (200); v_cmd_4 VARCHAR2 (200); v_segment_owner VARCHAR2(30); v_segment_name VARCHAR2(30); v_segment_type VARCHAR2(5); begin for rec in cur1 loop v_segment_owner := rec.segment_owner; v_segment_name := rec.segment_name; v_segment_type := rec.segment_type; if rec.segment_type = 'TABLE' then v_cmd_1 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' enable row movement'; v_cmd_2 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' shrink space compact'; v_cmd_3 := 'alter table '||rec.segment_owner||'.'||rec.segment_name||' shrink space'; if p_preview = true then dbms_output.put_line(v_cmd_1); dbms_output.put_line(v_cmd_2); dbms_output.put_line(v_cmd_3); else begin execute immediate v_cmd_1; execute immediate v_cmd_2; execute immediate v_cmd_3; record_shrink_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true
  15. 15. Page 15 Author: Ardiel Soodyall elsif rec.segment_type = 'INDEX' then v_cmd_4 := 'alter index '||rec.segment_owner||'.'||rec.segment_name||' shrink space'; if p_preview = true then dbms_output.put_line(v_cmd_4); else begin execute immediate v_cmd_4; record_shrink_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true end if; -- rec.segment_type = 'INDEX' end loop; exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end shrink_segments; /* ------------------------------------------------------------------------------ */ PROCEDURE record_gather_stats_completed( p_segment_owner IN VARCHAR2, p_segment_name IN VARCHAR2, p_segment_type IN VARCHAR2) IS BEGIN UPDATE DB_MAINTENANCE_CONTROL_LIST SET SEGMENT_LAST_STATS = sysdate, segment_stats_flag = 'N' WHERE segment_owner = p_segment_owner AND segment_name = p_segment_name AND segment_type = p_segment_type AND segment_stats_flag = 'Y'; COMMIT; exception when others then log_error (p_segment_owner,p_segment_name, p_segment_type, substr(sqlerrm,1,120)); END record_gather_stats_completed; /* ------------------------------------------------------------------------------ */ PROCEDURE gather_segment_stats (p_preview in boolean) IS /* --------------------------------------------------------------------------- This procedure performance a gather optimizer statistics action on specified segments sourced from the user. --------------------------------------------------------------------------- */ CURSOR cur2 IS SELECT segment_owner, segment_name, segment_type, segment_stats_flag from DB_MAINTENANCE_CONTROL_LIST t1 where SEGMENT_STATS_FLAG = 'Y'; rec cur2%ROWTYPE; v_cmd VARCHAR2 (200); v_segment_owner VARCHAR2(30); v_segment_name VARCHAR2(30); v_segment_type VARCHAR2(5); begin for rec in cur2 loop v_segment_owner := rec.segment_owner; v_segment_name := rec.segment_name; v_segment_type := rec.segment_type; if rec.segment_type = 'TABLE' then v_cmd := 'begin dbms_stats.gather_table_stats('''||rec.segment_owner||''','''||rec.segment_name||'''); end;'; elsif rec.segment_type = 'INDEX' then
  16. 16. Page 16 Author: Ardiel Soodyall v_cmd := 'begin dbms_stats.gather_index_stats('''||rec.segment_owner||''','''||rec.segment_name||'''); end;'; end if; if p_preview = true then dbms_output.put_line(v_cmd); else begin execute immediate v_cmd; record_gather_stats_completed(rec.segment_owner, rec.segment_name, rec.segment_type); exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end; end if; -- p_preview = true end loop; exception when others then log_error (v_segment_owner,v_segment_name, v_segment_type, substr(sqlerrm,1,120)); end gather_segment_stats; /* ---------------------------------------------------------------------------- */ PROCEDURE action_weekend_tasks (p_preview in boolean) IS /* ------------------------------------------------------------------------ This procedure performs a list of database maintenance actions. ------------------------------------------------------------------------ */ BEGIN shrink_segments (p_preview); gather_segment_stats (p_preview); exception when others then log_error ('DUMMY','DUMMY', 'DUMMY', substr(sqlerrm,1,120)); END action_weekend_tasks; /* ---------------------------------------------------------------------------- */ END DB_maintenance; / BEGIN DBMS_SCHEDULER.drop_window (window_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE_WNDW"', force => TRUE); END; / BEGIN DBMS_SCHEDULER.CREATE_WINDOW( window_name=>'"DB_AUTOMATIC_MAINTENANCE_WNDW"', resource_plan=>'DEFAULT_PLAN', start_date=>to_timestamp_tz('2014-02-20 +2:00', 'YYYY-MM-DD TZH:TZM'), duration=>numtodsinterval(840, 'minute'), repeat_interval=>'FREQ=WEEKLY;BYDAY=SUN;BYHOUR=8;BYMINUTE=0;BYSECOND=0', end_date=>null, window_priority=>'HIGH', comments=>'DB Automatic Maintenance'); END; / BEGIN DBMS_SCHEDULER.DROP_JOB (job_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"'); END; / BEGIN sys.dbms_scheduler.create_job( job_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', job_type => 'PLSQL_BLOCK', job_action => 'begin Insert into dbmaint.db_maintenance_control_list (segment_owner, segment_name, segment_type, segment_shrink_flag) SELECT segment_owner, segment_name, segment_type,''Y'' FROM TABLE (DBMS_SPACE.asa_recommendations (''FALSE'', ''FALSE'', ''FALSE'')) WHERE segment_type in (''TABLE'',''INDEX'') And recommendations like ''%shrink%'';
  17. 17. Page 17 Author: Ardiel Soodyall commit; dbmaint.db_maintenance.action_weekend_tasks(p_preview => false); end;', schedule_name => '"SYS"."DB_AUTOMATIC_MAINTENANCE_WNDW"', job_class => '"DEFAULT_JOB_CLASS"', comments => 'DB Automatic Maintenance to Shrink Segments', auto_drop => FALSE, enabled => FALSE); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'raise_events', value => dbms_scheduler.job_started + dbms_scheduler.job_succeeded + dbms_scheduler.job_completed); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'logging_level', value => DBMS_SCHEDULER.LOGGING_FULL); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'job_weight', value => 1); sys.dbms_scheduler.set_attribute( name => '"SYS"."DB_AUTOMATIC_MAINTENANCE"', attribute => 'stop_on_window_close', value => TRUE); sys.dbms_scheduler.enable( '"SYS"."DB_AUTOMATIC_MAINTENANCE"'); END; / spool off ------------------------------------- Cut off here ---------------------------------------------- Use the following shell script to implement this job for all databases on a specific database server where the “dot oraenv” script is used. ------------------------------------- Cut off here ---------------------------------------------- for db in `cat ${ORATAB} | grep -v * | grep -v "#" | grep -v "agent" | cut - f1 -d':'` do ORACLE_SID=$db ORAENV_ASK="NO" . oraenv echo DBNAME: $ORACLE_SID echo "--------------------------------------------------------" # . oraenv $ORACLE_SID sqlplus -S << EOF connect / as sysdba start h.sql EOF done ------------------------------------- Cut off here ---------------------------------------------- Use the following shell script to implement this job for all databases on a specific database server where the “oraenv” script is used. ------------------------------------- Cut off here ---------------------------------------------- for db in `cat ${ORATAB} | grep -v * | grep -v "#" | grep -v "agent" | grep -v "+ASM" | cut -f1 -d':'` do ORACLE_SID=$db export ORAENV_ASK=NO; export $ORACLE_SID echo $ORACLE_SID echo $ORACLE_HOME sqlplus -S << EOF connect / as sysdba start h.sql
  18. 18. Page 18 Author: Ardiel Soodyall EOF done ------------------------------------- Cut off here ----------------------------------------------

×