Wednesday, December 27, 2023

APEX application to display tablespace usage of databases registered in an Oracle Enterprise Manager installation

 


This is an APEX application (created with APEX 23.2) to present tablespace usage data, retrieved from your OEM 13c. It uses spaceUsed, bytesFree and pctUsed metrics to display graphs about Used (GB) space, Free (GB) space and Used (%).

First of all, you will need to create three views under your APEX workspace schema (tbs_usage_objs.sql). If your APEX installation lies in the same database with your OEM installation, you will need to give to your APEX workspace schema/user the necessary SELECT privileges on the appropriate SYSMAN objects (MGMT$TARGET_TYPE, MGMT$METRIC_DAILY, etc). If your APEX installation is in another database than your OEM installation, then create a database link from your APEX workspace schema/user to the OEM's SYSMAN and make the appropriate changes to the views and procedure's definitions.

Finally, import in your APEX workspace the application (f1002.sql).

Tuesday, November 18, 2014

Shrink a datafile, when ALTER TABLESPACE COALESCE is not working.

In a 11.2.0.3 database, we tried to reclaim space from USERS tablespace.
We faced a problem.
We use the following query to the the end of a particular USERS' datafile [file_id=72, filename=/oradb/SBLCCPRD/oradata4/users03.dbf]

SELECT
        A.FILE_ID,
        A.BLOCK_ID,
        A.BLOCK_ID + A.BLOCKS - 1   END_BLOCK,
        A.BLOCKS,
        --ROUND(A.BLOCKS*B.BLOCK_SIZE/1024/1024,2) MBYTES,
        A.BLOCKS*B.BLOCK_SIZE "SIZE",
        A.OWNER,
        A.SEGMENT_NAME,
        A.PARTITION_NAME,
        A.SEGMENT_TYPE
    FROM
        DBA_EXTENTS A, DBA_TABLESPACES B
    WHERE
        A.FILE_ID=:A AND A.TABLESPACE_NAME=B.TABLESPACE_NAME
UNION ALL
SELECT
        A.FILE_ID,
        A.BLOCK_ID,
        A.BLOCK_ID + A.BLOCKS - 1   END_BLOCK,
        A.BLOCKS,
        --ROUND(A.BLOCKS*B.BLOCK_SIZE/1024/1024,2) MBYTES,
        A.BLOCKS*B.BLOCK_SIZE "SIZE",
        'free'          OWNER,
        'free'          SEGMENT_NAME,
        NULL            PARTITION_NAME,
        NULL            SEGMENT_TYPE
    FROM
        DBA_FREE_SPACE A, DBA_TABLESPACES B
    WHERE
        A.FILE_ID=:A AND A.TABLESPACE_NAME=B.TABLESPACE_NAME
    ORDER BY 2 DESC;


We can see there were 5 contiguous free extents of 64 Kbytes each, total 320 Kbytes.

   
FILE_IDBLOCK_IDEND_BLOCKBLOCKSSIZEOWNERSEGMENT_NAMEPARTITION_NAMESEGMENT_TYPE
7216149521614959865536freefree
7216149441614951865536freefree
7216149361614943865536freefree
7216149281614935865536freefree
7216149201614927865536freefree
7216149121614919865536AMARGARICX_OTE_LOV_BACKUP_06052014TABLE
7216149041614911865536AMARGARICX_OTE_LOV_BACKUP_06052014TABLE
7216148961614903865536EFOTEINOPCX_OTE_LOV_0106TABLE
...........................


We tried to coalesce the tablespace, but it did not. The free extents did not get merged to one of 320 Kbytes.

SQL> alter tablespace users coalesce;
Tablespace altered. 



This is the datafile's size in bytes:
SELECT FILE_NAME, BYTES
FROM DBA_DATA_FILES
WHERE FILE_ID=72;

FILE_NAMEBYTES
/oradb/SBLCCPRD/oradata4/users03.dbf13229744128


When we tried to shrink the datafile, even by just 8 Kbytes, we got ORA-03297:

SQL> ALTER DATABASE DATAFILE 72 RESIZE 13229735936;
ALTER DATABASE DATAFILE 72 RESIZE 13229735936
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value


The solution was to purge the tablespace's recycle bin, even if there were no recycled objects near the end of the datafile. Furthermore, before I begin all this procedure, I had purged the database's recycle bin, using "PURGE DBA_RECYCLEBIN".

SQL> PURGE TABLESPACE USERS;
Tablespace purged.

SQL> ALTER DATABASE DATAFILE 72 RESIZE 13229735936;
Database altered.

   
FILE_IDBLOCK_IDEND_BLOCKBLOCKSSIZEOWNERSEGMENT_NAMEPARTITION_NAMESEGMENT_TYPE
721614920161495132262144freefree
7216149121614919865536AMARGARICX_OTE_LOV_BACKUP_06052014TABLE
7216149041614911865536AMARGARICX_OTE_LOV_BACKUP_06052014TABLE
7216148961614903865536EFOTEINOPCX_OTE_LOV_0106TABLE
...........................

Monday, August 4, 2014

ORA-01653, ORA-01658, ORA-01688, ORA-03233 even when there is free space in the tablespace.

ORA-01653: Unable to extend table %s.%s by string in tablespace %s
ORA-01658: Unable to create INITIAL extent for segment in tablespace %s
ORA-01688: Unable to extend table %s.%s partition %s by %s in tablespace %s
ORA-03233: Unable to extend table %s.%s subpartition %s by %s in tablespace %s
ORA-01654: Unable to extend index %s.%s by %s in tablespace %s
ORA-01683: Unable to extend index %s.%s partition %s by %s in tablespace %s
ORA-03234: Unable to extend index %s.%s subpartition %s by %s in tablespace %s

You may get the above errors, even if the is a lot of free space in the tablespace of the segment to be extended. This is because that free space is very fragmented.
When a segment asks for a new extent, the database will try to fit it in an equal or larger free extent. It will not fit it in more than one smaller free extents.
So, for instance, it the next extent to be created will be 2 MB and the largest available free extent is 1MB, you will get the above errors, even if the tablespace has gigabytes of total free space.

To check if this is the case, run the following query, which displays the free extents of a tablespace ordered by their size descending.
SELECT  C.NAME "DATAFILE NAME",
        A.FILE_ID "DATAFILE #",
        A.BLOCK_ID "BLOCK #",
        A.BLOCKS,
        ROUND(A.BLOCKS*B.BLOCK_SIZE/1024/1024,2) "MBYTES"
    FROM
        DBA_FREE_SPACE A,
        DBA_TABLESPACES B,
        V$DATAFILE C
    WHERE
        A.TABLESPACE_NAME = '[TABLESPACE NAME]'
        AND A.TABLESPACE_NAME = B.TABLESPACE_NAME
        AND C.FILE# = A.FILE_ID
ORDER BY 4 DESC;

If this is the case, you need to create larger free extents by reorganizing the tablespace or to add a new datafile, which at the moment it is created is a continuous free extent by itself.

Sunday, September 15, 2013

Use DBMS_SQLTUNE.IMPORT_SQL_PROFILE to force a hint to a query

DBMS_SQLTUNE.IMPORT_SQL_PROFILE is actually the procedure used when you implement a SQL Tuning Advisor's recommendation involving a better execution plan. But the SQL Tuning Advisor may not recommend the execution plan you know is the correct one or may even not recommend a new execution plan. If you know the hint that will improve your query's performance, then you may want to use DBMS_SQLTUNE.IMPORT_SQL_PROFILE to force it.

Let's create a scenario. Firstly, create 2 tables and 1 index in each one.
sqlplus system

SQL*Plus: Release 11.2.0.3.0 Production on Sun Sep 15 20:32:10 2013
Copyright (c) 1982, 2011, Oracle.  All rights reserved.
Enter password: 

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> CREATE TABLE SYSTEM.OBJECTS AS SELECT * FROM DBA_OBJECTS;
Table created.

SQL> CREATE INDEX SYSTEM.OBJ_NAME ON SYSTEM.OBJECTS(OBJECT_NAME);
Index created.

SQL> CREATE TABLE SYSTEM.SEGMENTS AS SELECT * FROM DBA_SEGMENTS;
Table created.

SQL> CREATE INDEX SYSTEM.SEG_NAME ON SYSTEM.SEGMENTS(SEGMENT_NAME);
Index created.

We will use a query that joins those tables using the indexed columns.
SQL> SET AUTOT TRACEONLY
SQL> SET TIMING ON
SQL> SET LINESIZE 200
SQL> SELECT OBJECT_ID,
       OBJECT_NAME,
       SEGMENT_TYPE,
       TABLESPACE_NAME
  FROM SYSTEM.OBJECTS, SYSTEM.SEGMENTS
 WHERE OBJECT_NAME = SEGMENT_NAME AND SEGMENT_NAME LIKE 'DBA%';

Elapsed: 00:00:00.02

Execution Plan
----------------------------------------------------------
Plan hash value: 98178177

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |   895 |   130K|     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN                   |          |   895 |   130K|     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| SEGMENTS |   784 | 54880 |     1   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SEG_NAME |   141 |       |     1   (0)| 00:00:01 |
|   4 |   TABLE ACCESS BY INDEX ROWID| OBJECTS  |  4970 |   383K|     3   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN          | OBJ_NAME |   895 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("OBJECT_NAME"="SEGMENT_NAME")
   3 - access("SEGMENT_NAME" LIKE 'DBA%')
       filter("SEGMENT_NAME" LIKE 'DBA%')
   5 - access("OBJECT_NAME" LIKE 'DBA%')
       filter("OBJECT_NAME" LIKE 'DBA%')


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         14  consistent gets
          0  physical reads
          0  redo size
        892  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

Now, let's find our query's SQL_ID.
SQL> SELECT SQL_ID, SQL_TEXT
FROM V$SQLAREA
WHERE SQL_TEXT LIKE 'SELECT OBJECT_ID%';

SQL_IDSQL_TEXT
gy6fj888vt27ySELECT OBJECT_ID, OBJECT_NAME, SEGMENT_TYPE, TABLESPACE_NAME FROM SYSTEM.OBJECTS, SYSTEM.SEGMENTS WHERE OBJECT_NAME = SEGMENT_NAME AND SEGMENT_NAME LIKE 'DBA%'

Both indexes are used, but let's assume we want to force full table scans on both tables.

SQL> DECLARE
  2  SQL_FTEXT CLOB;
BEGIN
SELECT SQL_FULLTEXT INTO SQL_FTEXT FROM V$SQLAREA WHERE SQL_ID = 'gy6fj888vt27y';

DBMS_SQLTUNE.IMPORT_SQL_PROFILE(
  SQL_TEXT => SQL_FTEXT,
  PROFILE => SQLPROF_ATTR('FULL(@"SEL$1" "OBJECTS"@"SEL$1") FULL(@"SEL$1" "SEGMENTS"@"SEL$1")'),
  NAME => 'PROFILE_gy6fj888vt27y',
  REPLACE => TRUE,
  FORCE_MATCH => TRUE
);
END;
/
PL/SQL procedure successfully completed.

Let's see the new execution plan.
Execution Plan
----------------------------------------------------------
Plan hash value: 1823853794 [New plan hash value]

-------------------------------------------------------------------------------
| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |          |   895 |   130K|   388   (2)| 00:00:05 |
|*  1 |  HASH JOIN         |          |   895 |   130K|   388   (2)| 00:00:05 |
|*  2 |   TABLE ACCESS FULL| SEGMENTS |   784 | 54880 |    53   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| OBJECTS  |  4970 |   383K|   334   (1)| 00:00:05 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("OBJECT_NAME"="SEGMENT_NAME")
   2 - filter("SEGMENT_NAME" LIKE 'DBA%')
   3 - filter("OBJECT_NAME" LIKE 'DBA%')

Note
-----
   - SQL profile "PROFILE_gy6fj888vt27y" used for this statement [The SQL profile we created, is used]


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       1419  consistent gets
        192  physical reads
          0  redo size
        892  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

You've may noticed the hint's format.
/*+ FULL(OBJECTS) FULL(SEGMENTS) */ is equal to 'FULL(@"SEL$1" "OBJECTS"@"SEL$1") FULL(@"SEL$1" "SEGMENTS"@"SEL$1")'.
Similarly, /*+ INDEX(OBJECTS OBJ_NAME) */ is equal to 'INDEX(@"SEL$1" "OBJECTS"@"SEL$1" "OBJ_NAME")'.

Tuesday, June 4, 2013

Upgrade from OEM 11g to 12c: Step DeployAgentNotRequired ignores agent deployment

We created a new OEM 12c [12.1.0.2.2], using the 2-system upgrade approach from our OEM 11g.

I tried to run the "Deploy and Configure Agent" step on 2 servers A and B and the installation proceeded only to server A. For server B, I was getting a "DeployAgentNotRequired" step with output "Ignoring deployment".

The problem was that there is wrong information in the repository and OEM considered some agents as NFS shared, even though they were not.

Execute the following query as SYSMAN in your OEM 11g repository database and identify which of those agents considered as NFS shared, actually are not.
SELECT TARGET_NAME, MASTER_AGENT_GUID, TARGET_GUID, IS_SHARED
FROM SYSMAN.PRE_UPGC_AGT_STAT_MGMT
WHERE IS_SHARED=1;

Keep the TARGET_NAME of those agents (for our example "B:3872") and set IS_SHARED=0 for them:
UPDATE SYSMAN.PRE_UPGC_AGT_STAT_MGMT
SET IS_SHARED = '0', MASTER_AGENT_GUID=TARGET_GUID
WHERE TARGET_NAME='B:3872';

COMMIT;

Also, check their predeployment status [PRE_DEP_STATUS column] and if the value is STATUS_SUCCESS, change it to STATUS_FAILED:
SELECT TARGET_NAME, PRE_DEP_STATUS
FROM SYSMAN.PRE_UPGC_AGT_STAT_MGMT
WHERE TARGET_NAME = 'B:3872';

UPDATE SYSMAN.PRE_UPGC_AGT_STAT_MGMT
SET PRE_DEP_STATUS='STATUS_FAILED'
WHERE TARGET_NAME = 'B:3872';

COMMIT;

Now, retry the "Deploy and Configure Agent" step for those servers.

Wednesday, April 24, 2013

Upgrade from OEM 11g to 12c: Step unzipAndDeploySoftware fails

We created a new OEM 12c [12.1.0.2.2], using the 2-system upgrade approach from our OEM 11g.

I tried to run the "Deploy and Configure Agent" step on a Linux 64-bit machine and unzipAndDeploySoftware step failed with error:
Check complete: Failed <<<<
Problem: The 32bit software cannot be installed on a 64 bit platform. Similarly the vice-versa is also true where 64bit product cannot be installed on a 32bit machine.
Recommendation: Please install the software on a 32-bit machine if the product is 32bit, otherwise install the product on the 64bit machine if it is a 64bit software.

This is Bug 15995299.
The problem is the OS has been discovered as a Linux 32-bit by the upgrade process.
SELECT TARGET_NAME, PLATFORM_ID, PLATFORM_NAME, CURRENT_VERSION
FROM SYSMAN.PRE_UPGC_AGT_STAT_MGMT
WHERE TARGET_NAME='your_server:3872';

TARGET_NAMEPLATFORM_IDPLATFORM_NAMECURRENT_VERSION
your_server:387246Linux x8611.1.0.1.0

Also, the targets which are monitored by that agent have wrong information:
SELECT EMD_URL, AGENT_NAME, TARGET_NAME, TARGET_TYPE, PLATFORM_ID, PLATFORM_NAME
FROM SYSMAN.PRE_UPGC_TGT_SW
WHERE AGENT_NAME='your_server:3872';

EMD_URLAGENT_NAMETARGET_NAMETARGET_TYPEPLATFORM_IDPLATFORM_NAME
https://your_server:3872/emd/main/your_server:3872your_server:3872oracle_emd46Linux x86
https://your_server:3872/emd/main/your_server:3872your_serverhost46Linux x86

You have to update these tables with the correct OS information:
UPDATE SYSMAN.PRE_UPGC_AGT_STAT_MGMT
    SET PLATFORM_ID='226', PLATFORM_NAME='Linux x86-64'
WHERE TARGET_NAME ='your_server:3872';

UPDATE SYSMAN.PRE_UPGC_TGT_SW SW
SET (PLATFORM_ID,PLATFORM_NAME,OS_VERSION ) =
(SELECT DISTINCT AGT.PLATFORM_ID,AGT.PLATFORM_NAME,AGT.OS_VERSION 
FROM SYSMAN.PRE_UPGC_AGT_STAT_MGMT AGT, SYSMAN.PRE_UPGC_TGT_SW TGT
WHERE TGT.TARGET_GUID=AGT.TARGET_GUID AND SW.EMD_URL=TGT.EMD_URL);

COMMIT;

 Rerun the "Deploy and Configure Agent" step for the particular agent.

You may also check Oracle Note 1519366.1: EM 12c: PreUpgrade Console shows Linux 64-bit Host As 32-bit for RHEL / OEL 5 64-bit servers.

Friday, April 19, 2013

Upgrade from OEM 11g to 12c: EM Console Service & EM Jobs Service have status Pending, Database System discovered had no members and status Pending.

We created a new OEM 12c [12.1.0.2.2], using the 2-system upgrade approach from our OEM 11g.
The first agent I installed, in the machine where I installed OMS and repository database had every target discovered with status Up, except the Database System, which had status Pending.
Also, EM Console Service & EM Jobs Service had status Pending.

Strangely, both issues had the same solution.
Although, in our old 11g repository database the parameter job_queue_processes had a value of 1000, some where in the upgrade process, in the new 12c repository database this changed to 0.
Probably, that happened during the database upgrade from 11.2.0.2 to 11.2.0.3 using dbua.

So the solution was:
Run as SYS:
SQL> show parameter job_queue_processes

NAME                TYPE     VALUE
------------------- -------- -----
job_queue_processes integer  0

SQL> ALTER SYSTEM SET job_queue_processes=1000 SCOPE=BOTH;
System altered.

SQL> show parameter  job_queue_processes

NAME                TYPE     VALUE
------------------- -------- -----
job_queue_processes integer  1000


Run as SYSMAN:
SQL> exec  emd_maintenance.remove_em_dbms_jobs;
PL/SQL procedure successfully completed.

SQL> exec emd_maintenance.submit_em_dbms_jobs;
PL/SQL procedure successfully completed.

Wednesday, April 10, 2013

Upgrade from OEM 11g to 12c: ORA-06532 during repository upgrade

In our 2-system upgrade, during the repository upgrade step, we got an ORA-06532 error:
2013-04-08 13:19:07,497 [Thread-514] ERROR - ERROR:ORA-06532: Subscript outside of limit
ORA-06512: at "SYSMAN.NOTIFICATION_MIGRATION_HELPER", line 576
ORA-06512: at "SYSMAN.NOTIFICATION_MIGRATION_HELPER", line 898
ORA-06512: at "SYSMAN.NOTIFICATION_MIGRATION_HELPER", line 1686
ORA-06512: at "SYSMAN.NOTIFICATION_MIGRATION_HELPER", line 2526
ORA-06512: at line 5
File:/oms/sysman/admin/emdrep/sql/core/11.2.0.1/notification/em_notification_data_upgrade.sql
Statement:DECLARE
l_upgrade_type VARCHAR2(64);
BEGIN
-- Migrate notification rules
NOTIFICATION_MIGRATION_HELPER.MIGRATE_RULES(p_batch_size => 50);

-- Find the upgrade type
l_upgrade_type := em_upgsoln_util.get_upgrade_property(EM_UPGSOLN_UTIL.G_UPGRADE_TYPE);

-- Perform pending violation specific upgrade for 1-system upgrade
IF l_upgrade_type IS NOT NULL AND
l_upgrade_type = EM_UPGSOLN_UTIL.G_UPG_ONE_SYSTEM
THEN
-- Migrate pending violations from aq$mgmt_notify_qtable
NOTIFICATION_MIGRATION_HELPER.MIGRATE_DELIVERY_BUS_DATA(p_batch_size => 25);

-- Migrate pending violations from mgmt_notify_requeue table
NOTIFICATION_MIGRATION_HELPER.MIGRATE_REQUEUE_DATA(p_batch_size => 25);

-- Migrate pending rule engine entries from aq$mgmt_notify_input_qtable
NOTIFICATION_MIGRATION_HELPER.MIGRATE_RULE_BUS_DATA(p_batch_size => 25);
END IF;
END;

Bug 15904251 : UPGRADE OF 11G NOTIFICATION RULES FAIL FOR RULE WITH LARGE NUMEBRS OF CONDITIONS was relevant, but applying it, did not solve the issue.
post_config.sh failed, with error: "No OMS is found to be associated with the current Oracle Home"

To continue, first, run these commands as SYSMAN to the new repository database:

ALTER TYPE EM_RULE_EXPRESSION_ARRAY MODIFY LIMIT 100000 CASCADE;
ALTER TYPE EM_RULE_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_SET_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_COND_ACTIONS_OBJ compile;
ALTER TYPE EM_RULE_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_COND_ACTIONS_OBJ compile;
ALTER TYPE EM_RULE_SET_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_COND_ACTIONS_OBJ compile;
ALTER TYPE EM_RULE_COND_ACTIONS_OBJ compile;
ALTER TYPE EM_RULE_SET_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_SET_DETAILS_OBJ compile;
ALTER TYPE EM_RULE_COND_ACTIONS_OBJ compile body;
ALTER TYPE EM_RULE_DETAILS_OBJ compile body;
ALTER TYPE EM_RULE_SET_DETAILS_OBJ compile body;
ALTER PACKAGE EM_RULE_BAM_INTG compile;
ALTER PACKAGE EM_INCIDENT_RULES compile;
ALTER PACKAGE MGMT_RCA compile;
ALTER PACKAGE NOTIFICATION_MIGRATION_HELPER compile;
ALTER PACKAGE EM_RULE_BAM_INTG compile body;
ALTER PACKAGE EM_INCIDENT_RULES compile body;
ALTER PACKAGE MGMT_RCA compile body;
ALTER PACKAGE NOTIFICATION_MIGRATION_HELPER compile body;


After that, run:

$OMS_HOME/oui/bin/runConfig.sh ORACLE_HOME=$OMS_HOME ACTION=configure MODE=perform


At the end, your OMS processes will be running ["emctl status oms" to verify] and you may login to your new 12c OEM.

Monday, April 1, 2013

Upgrade from OEM 11g to 12c: Clustered agents reported as "agents with Invalid Inventory"

We are in the process of upgrading our OEM 11g installation to 12c.
In the "Upgrade Console", agents installed to monitor a RAC were reported as "agents with Invalid Inventory".
These agents were installed using this method, in a custom Oracle inventory.
Additionally, from the "Setup", "Agents" tab, you can see a particular agent's page. There, by pressing the "Oracle Home" link, I was getting the following error:

Could not find Oracle Home <ORACLE_HOME> in the inventory collected for <HOSTNAME> on <TIMESTAMP>


As described in Oracle note 735952.1 - "Error Could not find Oracle Home in the inventory collected for on ", although some changes were necessary, to fix this problem:
  • Edit $AGENT_HOME/sysman/config/OUIinventories.add
  • Put an entry for your custom oraInst.loc, for example: inventory: /var/opt/oragrid/oraInst.loc
  • Restart the agent
  • From the OEM, go to the particular host's page, "Configuration" tab and press the "Refresh Host Configuration" button.
  • Wait for it to complete and then go to your "Upgrade Console" and press the "Refresh Agents and Targets List" button.
Now, those agents are removed from the "agents with Invalid Inventory" list.

Thursday, March 21, 2013

Procedure to check if the filesystems can accommodate a database's autoextensible datafiles

The following procedure will sum the space still to be claimed by the autoextensible datafiles per filesystem and compare those sums will the filesystems' free space. Be aware, this assumes there are no more than one database's autoextensible datafiles per filesystem. Otherwise, you may try to use database links to gather all required information in one database and get an aggregated result.

First of all, we need to retrieve information about the filesystems in our server, so since this is a Linux server, we will use the "df" command. In the home directory [/home/oracle], create a df.sh:
#!/bin/sh
/bin/df -Pl

And make it executable:
chmod u+x df.sh

Execute it:
Filesystem    1024-blocks Used Available Capacity Mounted on
/dev/mapper/rootvg00-lvol_root  18368380 5043772 12391540 29%  /
tmpfs     1961068  683460 1277608  35%  /dev/shm
/dev/sda1    198337  73094 115003  39%  /boot
/dev/mapper/rootvg00-lvol_home  2064208  137952 1821400  8%  /home
/dev/mapper/rootvg00-lvol_tmp  4128448  139296 3779440  4%  /tmp
/dev/mapper/rootvg00-lvol_var  8260812  3771552 4069628  49%  /var
/dev/mapper/rootvg00-lvol_vartmp 4128448  227884 3690852  6%  /var/tmp
/dev/mapper/OracleVG-lvbinaries  11123304 6210032 4348232  59%  /orabin
/dev/mapper/OracleVG-lvredo2  444276  177541 243798  43%  /redo2
/dev/mapper/OracleVG-lvredo1  444276  177541 243798  43%  /redo1
/dev/mapper/OracleVG-lvarch  21778916 35900 20644052 1%  /orarch
/dev/mapper/OracleVG-lvdata  9079280  7751656 866416  90%  /oradata
/dev/mapper/vgoradata2-lvoradata2 20428668 6516652 12874296 34%  /oradata2

For instance, we can see /oradata filesystem has 866416 Kbytes of unused space.

Now, we need to make this information available to our database.
We create a directory to point to our home directory:
CREATE OR REPLACE DIRECTORY HOME_DIR AS '/home/oracle';

And an external table to query this information:
CREATE TABLE FILESYSTEMS
(FSNAME VARCHAR2(100), BLOCKS NUMBER, USED NUMBER, AVAILABLE NUMBER, CAPACITY VARCHAR2(5), MOUNT VARCHAR2(100))
ORGANIZATION EXTERNAL
(TYPE ORACLE_LOADER
 DEFAULT DIRECTORY HOME_DIR
 ACCESS PARAMETERS
 (RECORDS DELIMITED BY NEWLINE
  NOLOGFILE
  PREPROCESSOR
  HOME_DIR:'df.sh'
  SKIP 1
  FIELDS TERMINATED BY WHITESPACE LDRTRIM)
 LOCATION
 (HOME_DIR:'df.sh'));

Let's use it:
SELECT * FROM FILESYSTEMS;
   
FSNAMEBLOCKSUSEDAVAILABLECAPACITYMOUNT
/dev/mapper/rootvg00-lvol_root1836838050437721239154029%/
tmpfs1961068683460127760835%/dev/shm
/dev/sda11983377309411500339%/boot
/dev/mapper/rootvg00-lvol_home206420813795218214008%/home
/dev/mapper/rootvg00-lvol_tmp412844813929637794404%/tmp
/dev/mapper/rootvg00-lvol_var82608123772356406882449%/var
/dev/mapper/rootvg00-lvol_vartmp412844822795236907846%/var/tmp
/dev/mapper/OracleVG-lvbinaries111233046210100434816459%/orabin
/dev/mapper/OracleVG-lvredo244427617754124379843%/redo2
/dev/mapper/OracleVG-lvredo144427617754124379843%/redo1
/dev/mapper/OracleVG-lvarch217789161971922437981%/orarch
/dev/mapper/OracleVG-lvdata9079280775165686641690%/oradata
/dev/mapper/vgoradata2-lvoradata22042866865166521287429634%/oradata2

We create one more table [FILESYSTEMS_INFO], identical to FILESYSTEMS, plus 2 more columns: TO_BE_USED, where we will sum the space still to be claimed by the autoextensible datafiles from DBA_DATA_FILES and DBA_TEMP_FILES, and OK, which will get value 'YES' if TO_BE_USED<AVAILABLE and 'NO' otherwise.
CREATE TABLE FILESYSTEMS_INFO
(FSNAME VARCHAR2(100), MOUNT VARCHAR2(100), BLOCKS NUMBER, USED NUMBER, AVAILABLE NUMBER, CAPACITY VARCHAR2(5), TO_BE_USED NUMBER, OK VARCHAR2(3))
TABLESPACE USERS;

 We run the following PL/SQL code:
DECLARE

BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE FILESYSTEMS_INFO';

INSERT INTO FILESYSTEMS_INFO(FSNAME, MOUNT, BLOCKS, USED, AVAILABLE, CAPACITY, TO_BE_USED, OK)
SELECT FSNAME, MOUNT, BLOCKS, USED, AVAILABLE, CAPACITY, 0, 'YES'
FROM FILESYSTEMS;

UPDATE FILESYSTEMS_INFO B SET (B.TO_BE_USED, B.OK)=
(SELECT NVL(SUM(A.MAXBYTES - A.USER_BYTES)/1024, 0), CASE WHEN SUM(A.MAXBYTES - A.USER_BYTES)/1024 >= B.AVAILABLE THEN 'NO' ELSE 'YES' END
FROM (SELECT FILE_NAME, AUTOEXTENSIBLE, MAXBYTES, USER_BYTES FROM DBA_DATA_FILES UNION SELECT FILE_NAME, AUTOEXTENSIBLE, MAXBYTES, USER_BYTES FROM  DBA_TEMP_FILES) A
WHERE A.AUTOEXTENSIBLE = 'YES'
AND INSTR(A.FILE_NAME, B.MOUNT||'/', 1, 1) = 1);

COMMIT;

END;
/

Now, we query FILESYSTEMS_INFO:
SELECT MOUNT, ROUND(AVAILABLE/1024/1024, 2) "AVAILABLE GB", ROUND(TO_BE_USED/1024/1024, 2) "TO BE USED GB", OK
FROM FILESYSTEMS_INFO;
   
MOUNTAVAILABLE GBTO BE USED GBOK
/11,820YES
/dev/shm1,220YES
/boot0,110YES
/home1,740YES
/tmp3,60YES
/var3,880YES
/var/tmp3,520YES
/orabin4,150YES
/redo20,230YES
/redo10,230YES
/orarch19,570YES
/oradata0,830,63YES
/oradata212,2835,09NO

/oradata filesystem is OK, because it has 0,83 GBytes of free space and all autoextensible datafiles in it could claim 0,63 GBytes maximum.
On the other hand, /oradata2 has 12,28 GBytes of free space and 35,09 GBytes could be claimed. We should decrease one or more autoextensible datafiles' MAXSIZE.

Friday, March 8, 2013

Optimize Siebel Repository Import in Oracle Database

It was noticed during siebel repository import in an Oracle Database 11.2.0.3, most of database wait time was log file switch.

srvrupgwiz /m master_imprep.ucf

It took 163 minutes for 3,639,432 rows (98% waits log file switch).
2012-11-18 09:53:02     TOTAL TABLES: 328
2012-11-18 09:53:02     TOTAL ROWS  : 3639432
2012-11-18 09:53:02     Cleaning up, disconnecting from the database.
2012-11-18 09:53:02     Elapsed time: 163 min 28 sec.

The database session did ~730.000 commits (meaning 1 commit every 5 rows).
repimexp /a I /G ENU /u sadmin /p ***** /c siebelpro_DSN /d siebel /r "Siebel Repository" /Z 5000 /h 5000 /f siebel_rep.data /l imprep_prim.log

If the /Z and /h option are used (with 5000 as argument) the time is less than 10 minutes for the same  repository.
2013-01-27 11:34:35     Verified successfully.
2013-01-27 11:34:35     TOTAL TABLES: 328
2013-01-27 11:34:35     TOTAL ROWS  : 3928282
2013-01-27 11:34:35     Cleaning up, disconnecting from the database.
2013-01-27 11:34:35     Elapsed time: 9 min 46 sec.

There is no way (until Dec 2012) to put /Z or /h in ucm file, so the only solution is to use the command line interface (Oracle support replied that is a product defect).

Reference from Oracle documentation:
Importing a Siebel Repository

NOTE:  When you import data into the Siebel Repository tables, a commit is performed once for each table into which repository data is imported. Alternatively, the commit frequency can be set to a specified number of rows by including the command-line option /h num_rows_per_commit when the repimexp.exe utility is invoked.
 /H [number]        (Optional) Number of rows per commit
 /Z [number]        (Optional) Array Insert Size (Default: 5)

Monday, February 4, 2013

A database Create User Like... procedure

The following procedure creates the set of commands to duplicate an existing database user.
The new user will have identical password, default and temporary tablespaces, profile, lock status, tablespace quotas, roles, system, object, column privileges and audit options with the existing user.
The procedure has 2 arguments. The first is the name of the existing user and the second the name your new user will have. If the second argument is left blank, the existing user's name will be used.

SET SERVEROUTPUT ON
SET LINESIZE 200

DECLARE
    TYPE T_CURSOR IS    REF CURSOR;
    V_CURSOR            T_CURSOR;
    V_USER                VARCHAR2 (30) := UPPER ('&1');
    V_NEW_USER            VARCHAR2 (30) := UPPER ('&2');
    V_PASSWORD1            VARCHAR2 (30);
    V_PASSWORD2            VARCHAR2 (30);
    V_D_TBS                VARCHAR2 (30);
    V_T_TBS                VARCHAR2 (30);
    V_PROFILE            VARCHAR2 (30);
    V_LOCK_DATE            DATE;
    V_COUNTER            NUMBER;
    V_MAX_BYTES            NUMBER;
    V_PRIVILEGE            VARCHAR2 (40);
    V_ADMIN_OPT            VARCHAR2 (3);
    V_ROLE                VARCHAR2 (30);
    V_DEF_ROLE            VARCHAR2 (3);
    V_DEFROLE            NUMBER;
    V_OUTPUT            VARCHAR2 (500);
    V_TABLE_NAME        VARCHAR2 (30);
    V_OWNER                VARCHAR2 (30);
    V_GRANT_OPT            VARCHAR2 (3);
    V_HIER_OPT            VARCHAR2 (3);
    V_COL_NAME            VARCHAR2 (30);
    V_AUDIT_OPT            VARCHAR2 (40);
    V_SUCCESS            VARCHAR2 (10);
    V_FAILURE            VARCHAR2 (10);

BEGIN
    SELECT COUNT (*)
      INTO V_COUNTER
      FROM DBA_USERS
     WHERE USERNAME = V_USER;

    IF V_COUNTER <> 0
    THEN
        IF V_NEW_USER IS NULL
        THEN
            V_NEW_USER := V_USER;
        END IF;

        SELECT B.PASSWORD, A.PASSWORD, A.DEFAULT_TABLESPACE, A.TEMPORARY_TABLESPACE, A.PROFILE, A.LOCK_DATE
          INTO V_PASSWORD1, V_PASSWORD2, V_D_TBS, V_T_TBS, V_PROFILE, V_LOCK_DATE
          FROM DBA_USERS A, SYS.USER$ B
         WHERE A.USERNAME = V_USER
         AND A.USERNAME = B.NAME;

        --Create User Command * Begin
        DBMS_OUTPUT.PUT_LINE ('--Create User command');
        DBMS_OUTPUT.PUT_LINE ('CREATE USER ' || V_NEW_USER);
        IF V_PASSWORD2 = 'EXTERNAL'
        THEN
            DBMS_OUTPUT.PUT_LINE ('IDENTIFIED EXTERNALLY');
        ELSE
            DBMS_OUTPUT.PUT_LINE ('IDENTIFIED BY VALUES ''' || V_PASSWORD1 || '''');
        END IF;
        DBMS_OUTPUT.PUT_LINE ('DEFAULT TABLESPACE ' || V_D_TBS);
        DBMS_OUTPUT.PUT_LINE ('TEMPORARY TABLESPACE ' || V_T_TBS);
        DBMS_OUTPUT.PUT_LINE ('PROFILE ' || V_PROFILE);
        IF V_LOCK_DATE IS NULL THEN
            DBMS_OUTPUT.PUT_LINE ('ACCOUNT UNLOCK;');
        ELSE
            DBMS_OUTPUT.PUT_LINE ('ACCOUNT LOCK;');
        END IF;
        --Create User Command * End

        --Tablespace Quotas * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_TS_QUOTAS
         WHERE USERNAME = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' tablespace quotas');

            OPEN V_CURSOR FOR
                SELECT TABLESPACE_NAME, MAX_BYTES
                  FROM DBA_TS_QUOTAS
                 WHERE USERNAME = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_D_TBS, V_MAX_BYTES;

                EXIT WHEN V_CURSOR%NOTFOUND;

                IF V_MAX_BYTES = -1
                THEN
                    DBMS_OUTPUT.PUT_LINE ('ALTER USER ' || V_NEW_USER || ' QUOTA UNLIMITED ON ' || V_D_TBS || ';');
                ELSE
                    DBMS_OUTPUT.PUT_LINE ('ALTER USER ' || V_NEW_USER || ' QUOTA ' || V_MAX_BYTES || ' ON ' || V_D_TBS || ';');
                END IF;
            END LOOP;

            CLOSE V_CURSOR;
        END IF;
        --Tablespace Quotas * End

        --Roles * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_ROLE_PRIVS
         WHERE GRANTEE = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' Roles');

            SELECT DEFROLE
              INTO V_DEFROLE
              FROM SYS.USER$
             WHERE NAME = V_USER;

            CASE V_DEFROLE
                WHEN 0
                THEN
                    DBMS_OUTPUT.PUT_LINE (
                        'ALTER USER ' || V_NEW_USER || ' DEFAULT ROLE NONE;');
                WHEN 1
                THEN
                    DBMS_OUTPUT.PUT_LINE (
                        'ALTER USER ' || V_NEW_USER || ' DEFAULT ROLE ALL;');
                ELSE
                    NULL;
            END CASE;

            OPEN V_CURSOR FOR
                SELECT GRANTED_ROLE, ADMIN_OPTION, DEFAULT_ROLE
                  FROM DBA_ROLE_PRIVS
                 WHERE GRANTEE = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_ROLE, V_ADMIN_OPT, V_DEF_ROLE;

                EXIT WHEN V_CURSOR%NOTFOUND;
                V_OUTPUT := 'GRANT ' || V_ROLE || ' TO ' || V_NEW_USER;

                IF V_ADMIN_OPT = 'NO'
                THEN
                    V_OUTPUT := V_OUTPUT || ';';
                ELSE
                    V_OUTPUT := V_OUTPUT || ' WITH ADMIN OPTION;';
                END IF;

                DBMS_OUTPUT.PUT_LINE (V_OUTPUT);

                IF V_DEFROLE = 2 AND V_DEF_ROLE='YES'
                THEN
                    DBMS_OUTPUT.PUT_LINE ('ALTER USER ' || V_NEW_USER || ' DEFAULT ROLE ' || V_ROLE || ';');
                END IF;
            END LOOP;

            CLOSE V_CURSOR;
        END IF;
        --Roles * End

        --System Privileges * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_SYS_PRIVS
         WHERE GRANTEE = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' system privileges');

            OPEN V_CURSOR FOR
                SELECT PRIVILEGE, ADMIN_OPTION
                  FROM DBA_SYS_PRIVS
                 WHERE GRANTEE = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_PRIVILEGE, V_ADMIN_OPT;

                EXIT WHEN V_CURSOR%NOTFOUND;
                V_OUTPUT := 'GRANT ' || V_PRIVILEGE || ' TO ' || V_NEW_USER;

                IF V_ADMIN_OPT = 'NO'
                THEN
                    V_OUTPUT := V_OUTPUT || ';';
                ELSE
                    V_OUTPUT := V_OUTPUT || ' WITH ADMIN OPTION;';
                END IF;

                DBMS_OUTPUT.PUT_LINE (V_OUTPUT);
            END LOOP;

            CLOSE V_CURSOR;
        END IF;
        --System Privileges * End

        --Object Privileges * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_TAB_PRIVS
         WHERE GRANTEE = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' object privileges');

            OPEN V_CURSOR FOR
                SELECT PRIVILEGE, OWNER, TABLE_NAME, GRANTABLE, HIERARCHY
                  FROM DBA_TAB_PRIVS
                 WHERE GRANTEE = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_PRIVILEGE, V_OWNER, V_TABLE_NAME, V_GRANT_OPT, V_HIER_OPT;

                EXIT WHEN V_CURSOR%NOTFOUND;
                V_OUTPUT := 'GRANT ' || V_PRIVILEGE || ' ON ' || V_OWNER || '.' || V_TABLE_NAME || ' TO ' || V_NEW_USER;

                IF V_HIER_OPT = 'YES'
                THEN
                    V_OUTPUT := V_OUTPUT || ' WITH HIERARCHY OPTION';
                END IF;

                IF V_GRANT_OPT = 'NO'
                THEN
                    V_OUTPUT := V_OUTPUT || ';';
                ELSE
                    V_OUTPUT := V_OUTPUT || ' WITH GRANT OPTION;';
                END IF;

                DBMS_OUTPUT.PUT_LINE (V_OUTPUT);
            END LOOP;

            CLOSE V_CURSOR;
        END IF;
        --Object Privileges * End

        --Column Privileges * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_COL_PRIVS
         WHERE GRANTEE = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' column privileges');

            OPEN V_CURSOR FOR
                SELECT PRIVILEGE, OWNER, TABLE_NAME, COLUMN_NAME, GRANTABLE
                  FROM DBA_COL_PRIVS
                 WHERE GRANTEE = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_PRIVILEGE, V_OWNER, V_TABLE_NAME, V_COL_NAME, V_GRANT_OPT;

                EXIT WHEN V_CURSOR%NOTFOUND;

                V_OUTPUT := 'GRANT ' || V_PRIVILEGE || ' (' || V_COL_NAME || ') ON ' || V_OWNER || '.' || V_TABLE_NAME || ' TO ' || V_NEW_USER;

                IF V_GRANT_OPT = 'NO'
                THEN
                    V_OUTPUT := V_OUTPUT || ';';
                ELSE
                    V_OUTPUT := V_OUTPUT || ' WITH GRANT OPTION;';
                END IF;

                DBMS_OUTPUT.PUT_LINE (V_OUTPUT);
            END LOOP;
            
            CLOSE V_CURSOR;
        END IF;
        --Column Privileges * End
    
        --Auditing options * Begin
        SELECT COUNT (*)
          INTO V_COUNTER
          FROM DBA_STMT_AUDIT_OPTS
         WHERE USER_NAME = V_USER;

        IF V_COUNTER > 0
        THEN
            DBMS_OUTPUT.PUT_LINE ('-- ' || V_COUNTER || ' auditing options');

            OPEN V_CURSOR FOR
                SELECT AUDIT_OPTION, SUCCESS, FAILURE
                  FROM DBA_STMT_AUDIT_OPTS
                 WHERE USER_NAME = V_USER;

            LOOP
                FETCH V_CURSOR
                INTO V_AUDIT_OPT, V_SUCCESS, V_FAILURE;

                EXIT WHEN V_CURSOR%NOTFOUND;

                IF V_SUCCESS <> 'NOT SET'
                THEN
                    DBMS_OUTPUT.PUT_LINE('AUDIT '||V_AUDIT_OPT||' BY '||V_NEW_USER||' '||V_SUCCESS||' WHENEVER SUCCESSFUL;');
                END IF;
                IF V_FAILURE <> 'NOT SET'
                THEN
                    DBMS_OUTPUT.PUT_LINE('AUDIT '||V_AUDIT_OPT||' BY '||V_NEW_USER||' '||V_FAILURE||' WHENEVER NOT SUCCESSFUL;');
                END IF;

            END LOOP;
            
            CLOSE V_CURSOR;
        END IF;
    --Auditing options * End

    ELSE
        DBMS_OUTPUT.PUT_LINE ('User ' || V_USER || ' does not exist.');
    END IF;
END;
/

Friday, December 21, 2012

Use Sqltxplain or DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE to change execution plans

This demonstration is based on Metalink Note 1487302.1 "Using Sqltxplain to create a 'SQL Profile' to consistently reproduce a good plan".

First of all, we need to install the Sqltxplain tool. Go to Metalink Note 215187.1 "SQLT (SQLTXPLAIN) - Tool that helps to diagnose a SQL statement performing poorly or one that produces wrong results", download sqlt.zip and install it under your ORACLE_HOME.

Let's test it. First we create 2 tables, based on DBA_OBJECTS and DBA_SEGMENTS and indexes on OBJECT_NAME and SEGMENT_NAME columns respectively.
sqlplus helpdesk

SQL*Plus: Release 11.2.0.3.0 Production on Fri Dec 21 11:55:47 2012
Copyright (c) 1982, 2011, Oracle.  All rights reserved.
Enter password: 
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> CREATE TABLE HELPDESK.OBJECTS AS SELECT * FROM DBA_OBJECTS;
Table created.

SQL> CREATE INDEX HELPDESK.OBJ_NAME ON HELPDESK.OBJECTS(OBJECT_NAME);
Index created.

SQL> CREATE TABLE HELPDESK.SEGMENTS AS SELECT * FROM DBA_SEGMENTS;
Table created.

SQL> CREATE INDEX HELPDESK.SEG_NAME ON HELPDESK.SEGMENTS(SEGMENT_NAME);
Index created.

We will use a query that joins those tables using the indexed columns.
SQL> SET AUTOT TRACEONLY
SQL> SET TIMING ON
SQL> SET LINESIZE 200
SQL> SELECT A.OBJECT_ID,
         A.OBJECT_NAME,
         B.SEGMENT_TYPE,
         B.TABLESPACE_NAME
  FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%';
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 917395572

------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |     2 |   298 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                 |          |       |       |            |          |
|   2 |   NESTED LOOPS                |          |     2 |   298 |     6   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID| OBJECTS  |     2 |   158 |     3   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | OBJ_NAME |     2 |       |     2   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN           | SEG_NAME |     1 |       |     1   (0)| 00:00:01 |
|   6 |   TABLE ACCESS BY INDEX ROWID | SEGMENTS |     1 |    70 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("A"."OBJECT_NAME" LIKE 'HELP%')
       filter("A"."OBJECT_NAME" LIKE 'HELP%')
   5 - access("A"."OBJECT_NAME"="B"."SEGMENT_NAME")
       filter("B"."SEGMENT_NAME" LIKE 'HELP%')

Note
-----
   - dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
      0  recursive calls
      0  db block gets
     12  consistent gets
      0  physical reads
      0  redo size
    855  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      2  rows processed

In this execution plan with plan hash value 917395572, both indexes are used.
Let's find out our query's SQL_ID. Run as SYSDBA:
SELECT SQL_ID, SQL_TEXT
FROM V$SQL
WHERE SQL_TEXT LIKE 'SELECT A.OBJECT_ID%';

SQL_IDSQL_TEXT
8qu7mmcwnygtvSELECT A.OBJECT_ID,    A.OBJECT_NAME,    B.SEGMENT_TYPE,    B.TABLESPACE_NAME   FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B  WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%'

Now, we will drop index HELPDESK.OBJ_NAME, in order to get a new execution plan for our query, where a full table scan will be used on table HELPDESK.OBJECTS.
SQL> DROP INDEX HELPDESK.OBJ_NAME;
Index dropped.

SQL> SELECT A.OBJECT_ID,
         A.OBJECT_NAME,
         B.SEGMENT_TYPE,
         B.TABLESPACE_NAME
  FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%';
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 2874684176

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows    | Bytes | Cost (%CPU)| Time    |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |     2 |   298 |    83   (2)| 00:00:01 |
|*  1 |  HASH JOIN                   |          |     2 |   298 |    83   (2)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| SEGMENTS |     2 |   140 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SEG_NAME |     2 |       |     2   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | OBJECTS  |     3 |   237 |    79   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("A"."OBJECT_NAME"="B"."SEGMENT_NAME")
   3 - access("B"."SEGMENT_NAME" LIKE 'HELP%')
       filter("B"."SEGMENT_NAME" LIKE 'HELP%')
   4 - filter("A"."OBJECT_NAME" LIKE 'HELP%')

Note
-----
   - dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
     54  recursive calls
      0  db block gets
    424  consistent gets
      0  physical reads
      0  redo size
    855  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      4  sorts (memory)
      0  sorts (disk)
      2  rows processed

A new plan was created with a full table scan on HELPDESK.OBJECTS and plan hash value 2874684176.
We recreate the index and we check the old plan is being used again.
SQL> CREATE INDEX HELPDESK.OBJ_NAME ON HELPDESK.OBJECTS(OBJECT_NAME);
Index created.

SQL> SELECT A.OBJECT_ID,
         A.OBJECT_NAME,
         B.SEGMENT_TYPE,
         B.TABLESPACE_NAME
  FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%';
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 917395572

------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |     2 |   298 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                 |          |       |       |            |          |
|   2 |   NESTED LOOPS                |          |     2 |   298 |     6   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID| OBJECTS  |     2 |   158 |     3   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | OBJ_NAME |     2 |       |     2   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN           | SEG_NAME |     1 |       |     1   (0)| 00:00:01 |
|   6 |   TABLE ACCESS BY INDEX ROWID | SEGMENTS |     1 |    70 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("A"."OBJECT_NAME" LIKE 'HELP%')
       filter("A"."OBJECT_NAME" LIKE 'HELP%')
   5 - access("A"."OBJECT_NAME"="B"."SEGMENT_NAME")
       filter("B"."SEGMENT_NAME" LIKE 'HELP%')

Note
-----
   - dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
     15  recursive calls
      0  db block gets
    139  consistent gets
      1  physical reads
      0  redo size
    855  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      2  rows processed

Now, we have 2 execution plans for our query.
We create an AWR snapshot as SYSDBA and we will use a query from Document 1487302.1 to compare those execution plans.
SQL> EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;
PL/SQL procedure successfully completed.

WITH
P AS (
SELECT PLAN_HASH_VALUE
  FROM GV$SQL_PLAN
 WHERE SQL_ID = TRIM('8qu7mmcwnygtv')
   AND OTHER_XML IS NOT NULL
 UNION
SELECT PLAN_HASH_VALUE
  FROM DBA_HIST_SQL_PLAN
 WHERE SQL_ID = TRIM('8qu7mmcwnygtv')
   AND OTHER_XML IS NOT NULL ),
M AS (
SELECT PLAN_HASH_VALUE,
       SUM(ELAPSED_TIME)/SUM(EXECUTIONS) AVG_ET_SECS
  FROM GV$SQL
 WHERE SQL_ID = TRIM('8qu7mmcwnygtv')
   AND EXECUTIONS > 0
 GROUP BY
       PLAN_HASH_VALUE ),
A AS (
SELECT PLAN_HASH_VALUE,
       SUM(ELAPSED_TIME_TOTAL)/SUM(EXECUTIONS_TOTAL) AVG_ET_SECS
  FROM DBA_HIST_SQLSTAT
 WHERE SQL_ID = TRIM('8qu7mmcwnygtv')
   AND EXECUTIONS_TOTAL > 0
 GROUP BY
       PLAN_HASH_VALUE )
SELECT P.PLAN_HASH_VALUE,
       ROUND(NVL(M.AVG_ET_SECS, A.AVG_ET_SECS)/1E6, 3) AVG_ET_SECS
  FROM P, M, A
 WHERE P.PLAN_HASH_VALUE = M.PLAN_HASH_VALUE(+)
   AND P.PLAN_HASH_VALUE = A.PLAN_HASH_VALUE(+)
 ORDER BY
       AVG_ET_SECS NULLS LAST;

PLAN_HASH_VALUEAVG_ET_SECS
9173955720,008
28746841760,019

Obviously, the execution plan with the 2 index range scans (917395572) is faster, but for the sake of our scenario we want to force plan 2874684176 to be used, when both indexes exist.
We will use Sqltxplain's coe_xfr_sql_profile.sql [SQL_ID] [PLAN_HASH_VALUE] as SYSDBA.
SQL> @$ORACLE_HOME/sqlt/utl/coe_xfr_sql_profile.sql '8qu7mmcwnygtv' '2874684176';

Parameter 1:
SQL_ID (required)



PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
      917395572        .008
     2874684176        .019

Parameter 2:
PLAN_HASH_VALUE (required)


Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID           : "8qu7mmcwnygtv"
PLAN_HASH_VALUE: "2874684176"

SQL>BEGIN
  2    IF :sql_text IS NULL THEN
  3       RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;
SQL>BEGIN
  2    IF :other_xml IS NULL THEN
  3       RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;

Execute coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql
on TARGET system in order to create a custom SQL Profile
with plan 2874684176 linked to adjusted sql_text.


COE_XFR_SQL_PROFILE completed.

Now, we need to execute as SYSDBA the generated coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql.
SQL>@$ORACLE_HOME/sqlt/utl/coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql
SQL>REM
SQL>REM $Header: 215187.1 coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql 11.4.4.4 2012/12/21 carlos.sierra $
SQL>REM
SQL>REM Copyright (c) 2000-2012, Oracle Corporation. All rights reserved.
SQL>REM
SQL>REM AUTHOR
SQL>REM   carlos.sierra@oracle.com
SQL>REM
SQL>REM SCRIPT
SQL>REM   coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql
SQL>REM
SQL>REM DESCRIPTION
SQL>REM   This script is generated by coe_xfr_sql_profile.sql
SQL>REM   It contains the SQL*Plus commands to create a custom
SQL>REM   SQL Profile for SQL_ID 8qu7mmcwnygtv based on plan hash
SQL>REM   value 2874684176.
SQL>REM   The custom SQL Profile to be created by this script
SQL>REM   will affect plans for SQL commands with signature
SQL>REM   matching the one for SQL Text below.
SQL>REM   Review SQL Text and adjust accordingly.
SQL>REM
SQL>REM PARAMETERS
SQL>REM   None.
SQL>REM
SQL>REM EXAMPLE
SQL>REM   SQL> START coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql;
SQL>REM
SQL>REM NOTES
SQL>REM   1. Should be run as SYSTEM or SYSDBA.
SQL>REM   2. User must have CREATE ANY SQL PROFILE privilege.
SQL>REM   3. SOURCE and TARGET systems can be the same or similar.
SQL>REM   4. To drop this custom SQL Profile after it has been created:
SQL>REM     EXEC DBMS_SQLTUNE.DROP_SQL_PROFILE('coe_8qu7mmcwnygtv_2874684176');
SQL>REM   5. Be aware that using DBMS_SQLTUNE requires a license
SQL>REM     for the Oracle Tuning Pack.
SQL>REM   6. If you modified a SQL putting Hints in order to produce a desired
SQL>REM     Plan, you can remove the artifical Hints from SQL Text pieces below.
SQL>REM     By doing so you can create a custom SQL Profile for the original
SQL>REM     SQL but with the Plan captured from the modified SQL (with Hints).
SQL>REM
SQL>WHENEVER SQLERROR EXIT SQL.SQLCODE;
SQL>REM
SQL>VAR signature NUMBER;
SQL>VAR signaturef NUMBER;
SQL>REM
SQL>DECLARE
  2  sql_txt CLOB;
  3  h         SYS.SQLPROF_ATTR;
  4  PROCEDURE wa (p_line IN VARCHAR2) IS
  5  BEGIN
  6  DBMS_LOB.WRITEAPPEND(sql_txt, LENGTH(p_line), p_line);
  7  END wa;
  8  BEGIN
  9  DBMS_LOB.CREATETEMPORARY(sql_txt, TRUE);
 10  DBMS_LOB.OPEN(sql_txt, DBMS_LOB.LOB_READWRITE);
 11  -- SQL Text pieces below do not have to be of same length.
 12  -- So if you edit SQL Text (i.e. removing temporary Hints),
 13  -- there is no need to edit or re-align unmodified pieces.
 14  wa(q'[SELECT A.OBJECT_ID,
 15                A.OBJECT_NAME,
 16                B.SEGMENT_TYPE,
 17                B.TA]');
 18  wa(q'[BLESPACE_NAME
 19    FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 20   WH]');
 21  wa(q'[ERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP]');
 22  wa(q'[%']');
 23  DBMS_LOB.CLOSE(sql_txt);
 24  h := SYS.SQLPROF_ATTR(
 25  q'[BEGIN_OUTLINE_DATA]',
 26  q'[IGNORE_OPTIM_EMBEDDED_HINTS]',
 27  q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.3')]',
 28  q'[DB_VERSION('11.2.0.3')]',
 29  q'[ALL_ROWS]',
 30  q'[OUTLINE_LEAF(@"SEL$1")]',
 31  q'[INDEX_RS_ASC(@"SEL$1" "B"@"SEL$1" ("SEGMENTS"."SEGMENT_NAME"))]',
 32  q'[FULL(@"SEL$1" "A"@"SEL$1")]',
 33  q'[LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")]',
 34  q'[USE_HASH(@"SEL$1" "A"@"SEL$1")]',
 35  q'[END_OUTLINE_DATA]');
 36  :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);
 37  :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);
 38  DBMS_SQLTUNE.IMPORT_SQL_PROFILE (
 39  sql_text     => sql_txt,
 40  profile     => h,
 41  name     => 'coe_8qu7mmcwnygtv_2874684176',
 42  description => 'coe 8qu7mmcwnygtv 2874684176 '||:signature||' '||:signaturef||'',
 43  category     => 'DEFAULT',
 44  validate     => TRUE,
 45  replace     => TRUE,
 46  force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );
 47  DBMS_LOB.FREETEMPORARY(sql_txt);
 48  END;
 49  /

PL/SQL procedure successfully completed.

SQL>WHENEVER SQLERROR CONTINUE
SQL>SET ECHO OFF;

        SIGNATURE
---------------------
  5858216496569750688


       SIGNATUREF
---------------------
  1896484766659468535


... manual custom SQL Profile has been created


COE_XFR_SQL_PROFILE_8qu7mmcwnygtv_2874684176 completed

Now, the plan  2874684176 will be used, every time our query is run.
SQL> SELECT A.OBJECT_ID,
         A.OBJECT_NAME,
         B.SEGMENT_TYPE,
         B.TABLESPACE_NAME
  FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%'; 
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 2874684176

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows    | Bytes | Cost (%CPU)| Time    |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |   436 | 64964 |    98   (2)| 00:00:02 |
|*  1 |  HASH JOIN                   |          |   436 | 64964 |    98   (2)| 00:00:02 |
|   2 |   TABLE ACCESS BY INDEX ROWID| SEGMENTS |   417 | 29190 |    18   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SEG_NAME |    75 |       |     2   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | OBJECTS  |  1176 | 92904 |    79   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("A"."OBJECT_NAME"="B"."SEGMENT_NAME")
   3 - access("B"."SEGMENT_NAME" LIKE 'HELP%')
       filter("B"."SEGMENT_NAME" LIKE 'HELP%')
   4 - filter("A"."OBJECT_NAME" LIKE 'HELP%')

Note
-----
   - SQL profile "coe_8qu7mmcwnygtv_2874684176" used for this statement <-- This is the SQL profile created by coe_xfr_sql_profile_8qu7mmcwnygtv_2874684176.sql


Statistics
----------------------------------------------------------
      6  recursive calls
      0  db block gets
    288  consistent gets
      1  physical reads
      0  redo size
    855  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      2  rows processed

In 11g databases, it's recommended to use DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE function, instead:
SQL> SET SERVEROUTPUT ON
VAR N NUMBER
BEGIN
:N:=DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(SQL_ID=>'8qu7mmcwnygtv', PLAN_HASH_VALUE=>2874684176, FIXED =>'NO', ENABLED=>'YES');
DBMS_OUTPUT.PUT_LINE('Number of plans loaded: '||:N);
END;
/
Number of plans loaded: 1

PL/SQL procedure successfully completed.

SQL> SELECT A.OBJECT_ID,
         A.OBJECT_NAME,
         B.SEGMENT_TYPE,
         B.TABLESPACE_NAME
  FROM HELPDESK.OBJECTS A, HELPDESK.SEGMENTS B
 WHERE A.OBJECT_NAME = B.SEGMENT_NAME AND B.SEGMENT_NAME LIKE 'HELP%'; 
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 2874684176

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows    | Bytes | Cost (%CPU)| Time    |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |   436 | 64964 |    98   (2)| 00:00:02 |
|*  1 |  HASH JOIN                   |          |   436 | 64964 |    98   (2)| 00:00:02 |
|   2 |   TABLE ACCESS BY INDEX ROWID| SEGMENTS |   417 | 29190 |    18   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SEG_NAME |    75 |       |     2   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | OBJECTS  |  1176 | 92904 |    79   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("A"."OBJECT_NAME"="B"."SEGMENT_NAME")
   3 - access("B"."SEGMENT_NAME" LIKE 'HELP%')
       filter("B"."SEGMENT_NAME" LIKE 'HELP%')
   4 - filter("A"."OBJECT_NAME" LIKE 'HELP%')

Note
-----
   - SQL plan baseline "SQL_PLAN_52m4hvq7pz550a442f8ad" used for this statement <-- This is the SQL plan baseline we have just created

Statistics
----------------------------------------------------------
      6  recursive calls
      0  db block gets
    288  consistent gets
      1  physical reads
      0  redo size
    855  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      2  rows processed