database procedure - automatic database maintenance - linkedin

18
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

Upload: ardiels

Post on 07-Apr-2017

190 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 2: Database Procedure - Automatic Database Maintenance - linkedin

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.

Page 3: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 4: Database Procedure - Automatic Database Maintenance - linkedin

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);

Page 5: Database Procedure - Automatic Database Maintenance - linkedin

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);

Page 6: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 7: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 8: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 9: Database Procedure - Automatic Database Maintenance - linkedin

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);

Page 10: Database Procedure - Automatic Database Maintenance - linkedin

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;

/

Page 11: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 12: Database Procedure - Automatic Database Maintenance - linkedin

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,

Page 13: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 14: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 15: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 16: Database Procedure - Automatic Database Maintenance - linkedin

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%'';

Page 17: Database Procedure - Automatic Database Maintenance - linkedin

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

Page 18: Database Procedure - Automatic Database Maintenance - linkedin

Page 18

Author: Ardiel Soodyall

EOF

done ------------------------------------- Cut off here ----------------------------------------------