Database Procedure - Automatic Database Maintenance - linkedin
Transcript of 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Author: Ardiel Soodyall
EOF
done ------------------------------------- Cut off here ----------------------------------------------