Tuesday, July 28, 2026

workdlow

-- Which Workflow processes are generating the most events?
SELECT 
  event_name,
  event_key,
  COUNT(*) AS event_count,
  MAX(event_date) AS latest_event
FROM wf_events
WHERE event_date >= TRUNC(SYSDATE) + (18/24)
GROUP BY event_name, event_key
ORDER BY event_count DESC
FETCH FIRST 20 ROWS ONLY;

-- Check WF_EVENT listener queue backlog
SELECT 
  qt.queue_table,
  q.name,
  q.status,
  DBMS_AQADM.queue_depth(q.name) AS queue_depth
FROM dba_queues q
JOIN dba_queue_tables qt ON q.queue_table = qt.queue_table
WHERE q.name IN ('WF_EVENT_T', 'WF_EVENT_Q');

-- Find expensive WF_EVENT queries
SELECT 
  sql_id,
  parsing_schema_name,
  executions,
  elapsed_time / 1e6 AS elapsed_sec,
  ROUND(elapsed_time / executions / 1e3, 2) AS ms_per_exec,
  sql_text
FROM v$sqlarea
WHERE LOWER(sql_text) LIKE '%wf_event%'
AND executions > 0
ORDER BY elapsed_time DESC
FETCH FIRST 10 ROWS ONLY;

-- Check WF_EVENT listener latency
SELECT 
  event_filter_guid,
  event_name,
  COUNT(*) AS processed_count,
  MAX(event_date) AS last_processed
FROM wf_event_subscriptions
WHERE event_date >= TRUNC(SYSDATE) + (18/24)
GROUP BY event_filter_guid, event_name
ORDER BY processed_count DESC;


-- Which Workflow processes are generating the most events?
SELECT 
  event_name,
  event_key,
  COUNT(*) AS event_count,
  MAX(event_date) AS latest_event
FROM wf_events
WHERE event_date >= TRUNC(SYSDATE) + (18/24)
GROUP BY event_name, event_key
ORDER BY event_count DESC
FETCH FIRST 20 ROWS ONLY;

-- Check WF_EVENT listener queue backlog
SELECT 
  qt.queue_table,
  q.name,
  q.status,
  DBMS_AQADM.queue_depth(q.name) AS queue_depth
FROM dba_queues q
JOIN dba_queue_tables qt ON q.queue_table = qt.queue_table
WHERE q.name IN ('WF_EVENT_T', 'WF_EVENT_Q');

-- Find expensive WF_EVENT queries
SELECT 
  sql_id,
  parsing_schema_name,
  executions,
  elapsed_time / 1e6 AS elapsed_sec,
  ROUND(elapsed_time / executions / 1e3, 2) AS ms_per_exec,
  sql_text
FROM v$sqlarea
WHERE LOWER(sql_text) LIKE '%wf_event%'
AND executions > 0
ORDER BY elapsed_time DESC
FETCH FIRST 10 ROWS ONLY;

-- Check WF_EVENT listener latency
SELECT 
  event_filter_guid,
  event_name,
  COUNT(*) AS processed_count,
  MAX(event_date) AS last_processed
FROM wf_event_subscriptions
WHERE event_date >= TRUNC(SYSDATE) + (18/24)
GROUP BY event_filter_guid, event_name
ORDER BY processed_count DESC;


-- Trace where setNavigationParama is being called FROM
SELECT 
  sql_id,
  parsing_schema_name,
  executions,
  ROUND(elapsed_time / 1e6 / executions, 2) AS avg_sec_per_exec,
  SUBSTR(sql_text, 1, 100) AS sql_snippet
FROM v$sqlarea
WHERE sql_id = '0bu1jgc94rg3fj'
OR LOWER(sql_text) LIKE '%setnavigationparam%';

-- Find what's IN THE CALL STACK for this SQL
SELECT 
  sql_id,
  plan_hash_value,
  depth,
  operation,
  options,
  object_name,
  object_type,
  cardinality,
  bytes
FROM v$sql_plan
WHERE sql_id = '0bu1jgc94rg3fj'
ORDER BY id;

-- Get the actual SQL text
SELECT 
  sql_fulltext
FROM v$sql
WHERE sql_id = '0bu1jgc94rg3fj'
FETCH FIRST 1 ROW ONLY;
-- Find what's calling setNavigationParams
SELECT 
  sql_id,
  parsing_schema_name,
  executions,
  SUBSTR(sql_text, 1, 200) AS sql_caller
FROM v$sqlarea
WHERE LOWER(sql_text) LIKE '%setnavigationparam%'
OR (sql_id != '0bu1jgc94rg3fj' 
  AND LOWER(sql_text) LIKE '%wf_event%'
  AND executions > 1000)
ORDER BY executions DESC
FETCH FIRST 20 ROWS ONLY;

-- Check concurrent request log for WF jobs
SELECT 
  request_id,
  program_application_id,
  concurrent_program_id,
  phase_code,
  status_code,
  actual_start_date,
  actual_completion_date,
  argument_text
FROM fnd_concurrent_requests
WHERE program_application_id IN (
  SELECT application_id FROM fnd_application WHERE application_short_name IN ('WF', 'FUN')
)
AND actual_start_date >= TRUNC(SYSDATE - 7)
AND status_code IN ('R', 'C', 'E') -- Running, Complete, Error
ORDER BY request_id DESC
FETCH FIRST 30 ROWS ONLY;

-- Check for stuck/errored requests
SELECT 
  request_id,
  user_concurrent_program_name,
  phase_code,
  status_code,
  actual_start_date,
  ROUND((SYSDATE - actual_start_date) * 86400, 0) AS age_seconds,
  logfile_node_name,
  logfile_path,
  logfile_filename
FROM fnd_concurrent_requests_vl
WHERE status_code IN ('R', 'E')
AND actual_start_date >= TRUNC(SYSDATE - 1)
ORDER BY actual_start_date DESC;


-- Find what's calling setNavigationParams
SELECT 
  sql_id,
  parsing_schema_name,
  executions,
  SUBSTR(sql_text, 1, 200) AS sql_caller
FROM v$sqlarea
WHERE LOWER(sql_text) LIKE '%setnavigationparam%'
OR (sql_id != '0bu1jgc94rg3fj' 
  AND LOWER(sql_text) LIKE '%wf_event%'
  AND executions > 1000)
ORDER BY executions DESC
FETCH FIRST 20 ROWS ONLY;

-- Check concurrent request log for WF jobs
SELECT 
  request_id,
  program_application_id,
  concurrent_program_id,
  phase_code,
  status_code,
  actual_start_date,
  actual_completion_date,
  argument_text
FROM fnd_concurrent_requests
WHERE program_application_id IN (
  SELECT application_id FROM fnd_application WHERE application_short_name IN ('WF', 'FUN')
)
AND actual_start_date >= TRUNC(SYSDATE - 7)
AND status_code IN ('R', 'C', 'E') -- Running, Complete, Error
ORDER BY request_id DESC
FETCH FIRST 30 ROWS ONLY;

-- Check for stuck/errored requests
SELECT 
  request_id,
  user_concurrent_program_name,
  phase_code,
  status_code,
  actual_start_date,
  ROUND((SYSDATE - actual_start_date) * 86400, 0) AS age_seconds,
  logfile_node_name,
  logfile_path,
  logfile_filename
FROM fnd_concurrent_requests_vl
WHERE status_code IN ('R', 'E')
AND actual_start_date >= TRUNC(SYSDATE - 1)
ORDER BY actual_start_date DESC;



SELECT 
  request_id,
  user_concurrent_program_name,
  concurrent_program_name,
  program_application_id,
  concurrent_program_id,
  actual_start_date,
  actual_completion_date,
  ROUND((actual_completion_date - actual_start_date) * 86400, 0) AS duration_sec,
  phase_code,
  status_code
FROM fnd_concurrent_requests_vl
WHERE actual_start_date >= TRUNC(SYSDATE - 2)
AND (status_code IN ('R', 'E', 'I')
  OR actual_start_date BETWEEN TO_DATE('2026-07-25', 'YYYY-MM-DD') AND TO_DATE('2026-07-28', 'YYYY-MM-DD'))
ORDER BY actual_start_date DESC
FETCH FIRST 50 ROWS ONLY;


SELECT 
  owner,
  name,
  type
FROM dba_source
WHERE owner = 'APPS'
AND (LOWER(text) LIKE '%WF_ITEM_ACTIVITY_STATUSES%'
  OR LOWER(text) LIKE '%OJMSTEXT%')
GROUP BY owner, name, type
ORDER BY name;



-- Find sessions currently executing the loop
SELECT 
  sid,
  serial#,
  username,
  program,
  status,
  sql_id,
  ROUND((SYSDATE - logon_time) * 86400, 0) AS session_age_sec,
  event
FROM v$session
WHERE username = 'APPS'
AND status = 'ACTIVE'
AND (sql_id IN ('5z0s7q1a976mp', '8qf0gsw8scbr', 'a4gzfqk1c25n', 
                 'daphqlqsnymv8', '0bu1jgc94rg3fj')
  OR ROUND((SYSDATE - logon_time) * 86400) > 3600);




SELECT
    s.sid,
    s.serial#,
    s.sql_id,
    s.prev_sql_id,
    s.status,
    s.username,
    s.module,
    s.action,
    s.client_identifier,
    s.client_info,
    s.program,
    s.machine,
    s.process AS client_os_pid,
    p.spid AS database_os_pid,
    s.logon_time
FROM gv$session s
LEFT JOIN gv$process p
       ON p.addr = s.paddr
      AND p.inst_id = s.inst_id
WHERE s.sql_id IN (
          '5z0s7q1a976mp',
          '8qf0gsw8scbr',
          'a4gzfqk1c25n',
          'daphqlqsnymv8',
          '0bu1jgc94rg3fj'
      )
   OR s.prev_sql_id IN (
          '5z0s7q1a976mp',
          '8qf0gsw8scbr',
          'a4gzfqk1c25n',
          'daphqlqsnymv8',
          '0bu1jgc94rg3fj'
      )
ORDER BY s.inst_id, s.sid;



SELECT 
  request_id,
  user_concurrent_program_name,
  concurrent_program_name,
  actual_start_date,
  actual_completion_date,
  ROUND((actual_completion_date - actual_start_date) * 86400, 0) AS duration_sec,
  phase_code,
  status_code,
  user_name
FROM fnd_concurrent_requests_vl
WHERE actual_start_date BETWEEN TO_DATE('2026-07-24', 'YYYY-MM-DD') AND TO_DATE('2026-07-28 16:47', 'YYYY-MM-DD HH24:MI')
ORDER BY actual_start_date DESC
FETCH FIRST 50 ROWS ONLY;



SELECT 
  request_id,
  user_concurrent_program_name,
  concurrent_program_name,
  actual_start_date,
  actual_completion_date,
  ROUND((actual_completion_date - actual_start_date) * 86400, 0) AS duration_sec,
  phase_code,
  status_code,
  user_name
FROM fnd_concurrent_requests_vl
WHERE actual_start_date BETWEEN TO_DATE('2026-07-24', 'YYYY-MM-DD') AND TO_DATE('2026-07-28 16:47', 'YYYY-MM-DD HH24:MI')
ORDER BY actual_start_date DESC
FETCH FIRST 50 ROWS ONLY;



-- Find which Workflow event subscription is triggered by WSHPSRS
SELECT 
  event_filter_guid,
  event_name,
  owner_name,
  owner_tag,
  function_name,
  status
FROM wf_event_subscriptions
WHERE LOWER(event_name) LIKE '%pick%'
OR LOWER(event_name) LIKE '%ship%'
OR LOWER(function_name) LIKE '%navigation%'
ORDER BY event_name;


Monday, July 27, 2026

arc

1. Check archive generation by hour
SET LINES 200
SET PAGES 100

SELECT TO_CHAR(first_time,'DD-MON-YYYY HH24') AS archive_hour,
       COUNT(*) AS archive_count,
       ROUND(SUM(blocks * block_size) / 1024 / 1024 / 1024, 2) AS size_gb
FROM   v$archived_log
WHERE  first_time >= SYSDATE - 3
AND    dest_id = 1
GROUP BY TO_CHAR(first_time,'DD-MON-YYYY HH24')
ORDER BY archive_hour;
2. Check daily archive generation
SELECT TO_CHAR(first_time,'DD-MON-YYYY') AS archive_date,
       COUNT(*) AS log_count,
       ROUND(SUM(blocks * block_size) / 1024 / 1024 / 1024, 2) AS total_gb
FROM   v$archived_log
WHERE  first_time >= SYSDATE - 7
AND    dest_id = 1
GROUP BY TO_CHAR(first_time,'DD-MON-YYYY')
ORDER BY MIN(first_time);
3. Find recently growing objects in APPS_TS_TX_IDX
SET LINES 220
SET PAGES 100

COLUMN owner FORMAT A20
COLUMN segment_name FORMAT A45
COLUMN partition_name FORMAT A30
COLUMN segment_type FORMAT A20

SELECT *
FROM (
    SELECT owner,
           segment_name,
           partition_name,
           segment_type,
           ROUND(bytes / 1024 / 1024 / 1024, 2) AS size_gb
    FROM   dba_segments
    WHERE  tablespace_name = 'APPS_TS_TX_IDX'
    ORDER BY bytes DESC
)
WHERE ROWNUM <= 30;
4. Check recent extent allocation
This helps identify objects that received new extents recently, provided auditing or historical views are available:
SELECT owner,
       segment_name,
       segment_type,
       partition_name,
       COUNT(*) AS extent_count,
       ROUND(SUM(bytes) / 1024 / 1024 / 1024, 2) AS allocated_gb
FROM   dba_extents
WHERE  tablespace_name = 'APPS_TS_TX_IDX'
GROUP BY owner,
         segment_name,
         segment_type,
         partition_name
ORDER BY allocated_gb DESC
FETCH FIRST 30 ROWS ONLY;
5. Check active sessions generating redo
Run the first query, wait 10–15 minutes, and run it again. Sessions with the largest increase are likely generating the redo.
SELECT s.sid,
       s.serial#,
       s.username,
       s.program,
       s.module,
       s.action,
       s.sql_id,
       ROUND(st.value / 1024 / 1024, 2) AS redo_mb
FROM   v$session s
JOIN   v$sesstat st
       ON st.sid = s.sid
JOIN   v$statname sn
       ON sn.statistic# = st.statistic#
WHERE  sn.name = 'redo size'
AND    s.username IS NOT NULL
ORDER BY st.value DESC
FETCH FIRST 30 ROWS ONLY;
6. Check EBS concurrent requests running during the issue
SELECT r.request_id,
       p.user_concurrent_program_name,
       r.phase_code,
       r.status_code,
       r.actual_start_date,
       r.actual_completion_date,
       ROUND((NVL(r.actual_completion_date, SYSDATE)
             - r.actual_start_date) * 24, 2) AS runtime_hours,
       r.oracle_process_id
FROM   apps.fnd_concurrent_requests r
JOIN   apps.fnd_concurrent_programs_vl p
       ON p.concurrent_program_id = r.concurrent_program_id
      AND p.application_id = r.program_application_id
WHERE  r.actual_start_date >= SYSDATE - 2
ORDER BY r.actual_start_date DESC;
7. Check tablespace usage
SELECT df.tablespace_name,
       ROUND(df.total_gb, 2) AS total_gb,
       ROUND(df.total_gb - fs.free_gb, 2) AS used_gb,
       ROUND(fs.free_gb, 2) AS free_gb,
       ROUND((df.total_gb - fs.free_gb) / df.total_gb * 100, 2) AS used_pct
FROM (
    SELECT tablespace_name,
           SUM(bytes) / 1024 / 1024 / 1024 AS total_gb
    FROM   dba_data_files
    WHERE  tablespace_name = 'APPS_TS_TX_IDX'
    GROUP BY tablespace_name
) df
JOIN (
    SELECT tablespace_name,
           SUM(bytes) / 1024 / 1024 / 1024 AS free_gb
    FROM   dba_free_space
    WHERE  tablespace_name = 'APPS_TS_TX_IDX'
    GROUP BY tablespace_name
) fs
ON fs.tablespace_name = df.tablespace_name;
Likely causes include a bulk data load, index rebuild, CREATE INDEX, heavy updates or deletes, statistics collection with index maintenance, clone/post-clone processing, interface imports, or a repeatedly failing concurrent program. The archive-





What to check next
1. Is debug logging enabled?
Check the profile options:
SELECT profile_option_name,
       profile_option_value
FROM apps.fnd_profile_option_values v,
     apps.fnd_profile_options p
WHERE v.profile_option_id = p.profile_option_id
AND p.profile_option_name LIKE 'AFLOG%';
Or from the EBS front end:
FND: Debug Log Enabled
FND: Debug Log Level
If enabled at Statement or Procedure, it can generate very large volumes of log records.
2. Which module is writing to FND_LOG_MESSAGES?
SELECT module,
       COUNT(*) cnt
FROM applsys.fnd_log_messages
WHERE timestamp >= SYSDATE - 3
GROUP BY module
ORDER BY cnt DESC;
3. Which workflow events are busiest?
SELECT event_name,
       COUNT(*)
FROM apps.wf_events
GROUP BY event_name
ORDER BY COUNT(*) DESC;
(If WF_EVENTS does not contain the required data, we can query the relevant runtime workflow tables instead.)




COL profile_level FOR A15
COL profile_option_value FOR A20

SELECT
    DECODE(level_id,
           10001,'SITE',
           10002,'APPLICATION',
           10003,'RESPONSIBILITY',
           10004,'USER',
           10005,'SERVER',
           TO_CHAR(level_id)) profile_level,
    level_value,
    profile_option_value
FROM apps.fnd_profile_option_values v,
     apps.fnd_profile_options p
WHERE v.profile_option_id = p.profile_option_id
AND p.profile_option_name = 'AFLOG_ENABLED';

Friday, June 19, 2026

PARAMETERES

 COL con_name FOR A20

COL parameter_name FOR A40

COL value FOR A80


SELECT c.name con_name,

       p.name parameter_name,

       p.value

FROM   v$parameter p,

       v$containers c

WHERE  p.con_id=c.con_id

AND    p.name IN (

'db_name',

'db_unique_name',

'service_names',

'utl_file_dir',

'local_listener',

'remote_listener',

'db_create_file_dest',

'log_archive_dest_1'

)

ORDER BY c.name,p.name;

Sunday, May 17, 2026

Oracle EBS 12.2 – Patch File System Validation Checklist

 

Saturday, May 16, 2026

Oracle SQL Plan Migration Runbook

 

Oracle SQL Plan Migration Runbook

Objective

This runbook explains how to move a good execution plan from a source environment (Source Environment) to a Production environment using:

  • SQL Tuning Set (STS)
  • Data Pump (expdp/impdp)
  • SQL Plan Management (SPM)

This approach is commonly used by Oracle Apps DBAs during:

  • SQL Plan Regression
  • Month-End Performance Issues
  • Concurrent Program Slow Performance
  • Optimizer Plan Changes after Statistics Gathering
  • Oracle EBS 12.2 Performance Stabilization

High-Level Flow

Identify Good Plan
Create SQL Tuning Set (STS)
Load Good SQL into STS
Create STS Staging Table
Export STS Table using Data Pump
Transfer Dump File to Production
Import STS Table into Production
Unpack STS
Load Plan into SQL Plan Baseline
Purge Old Cursor from Shared Pool
Re-execute SQL / Concurrent Program
Validate Improved Execution Plan

Source Environment Steps (Source Environment)

Step 1: Identify the Good SQL Plan

Find the SQL_ID and PLAN_HASH_VALUE of the good plan.

SELECT sql_id,
plan_hash_value
FROM v$sqlarea
WHERE sql_id IN ('f9k42ab71mn8q');

OR

SELECT sql_id,
plan_hash_value
FROM v$sql
WHERE sql_id IN ('f9k42ab71mn8q');

Step 2: Create Empty SQL Tuning Set (STS)

BEGIN
DBMS_SQLTUNE.CREATE_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
description => 'STS to move better plan to Production');
END;
/

Step 3: Load SQL Information into STS

DECLARE
s_sqlarea_cursor DBMS_SQLTUNE.SQLSET_CURSOR;
BEGIN

OPEN s_sqlarea_cursor FOR
SELECT VALUE(p)
FROM TABLE(
DBMS_SQLTUNE.SELECT_CURSOR_CACHE(
'sql_id = ''f9k42ab71mn8q''
AND plan_hash_value = 1847263512')) p;

DBMS_SQLTUNE.LOAD_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
populate_cursor => s_sqlarea_cursor);

END;
/

Step 4: Verify SQL Tuning Set Contents

SELECT name,
statement_count,
description
FROM dba_sqlset;

Formatting commands:

COLUMN sql_text FORMAT a30
COLUMN sch FORMAT a3
COLUMN elapsed FORMAT 999999999

Query STS contents:

SELECT sql_id,
parsing_schema_name AS "SCH",
sql_text,
elapsed_time AS "ELAPSED",
buffer_gets
FROM TABLE(
DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Detailed verification:

SELECT *
FROM TABLE(
DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Step 5: Create Staging Table

Note: Table names and parameters are case-sensitive.

EXEC DBMS_SQLTUNE.CREATE_STGTAB_SQLSET(
table_name => 'TEST');

Step 6: Pack STS into Staging Table

BEGIN

DBMS_SQLTUNE.PACK_STGTAB_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
sqlset_owner => 'SYSTEM',
staging_table_name => 'TEST',
staging_schema_owner=> 'SYSTEM');

END;
/

Step 7: Create Oracle Directory Object

CREATE DIRECTORY TEST AS
'/opt/mis/backup_1/EBS_SQLSET_BACKUP';

Step 8: Export Staging Table using Data Pump

expdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 9: Transfer Dump File to Production

Transfer the dump file using:

  • SCP
  • FTP
  • SFTP

Example:

scp f9k42ab71mn8q_STS.dmp oracle@targetserver:/backup

Target Environment Steps (Production)

Step 10: Import the Dump File

impdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 11: Unpack SQL Tuning Set

BEGIN

DBMS_SQLTUNE.UNPACK_STGTAB_SQLSET(
sqlset_name => '%',
sqlset_owner => 'SYSTEM',
replace => TRUE,
staging_table_name => 'TEST',
staging_schema_owner => 'SYSTEM');

END;
/

Step 12: Load Plan from STS into SQL Plan Baseline

VARIABLE v_plan_cnt NUMBER

EXECUTE :v_plan_cnt := DBMS_SPM.LOAD_PLANS_FROM_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
sqlset_owner => 'SYSTEM',
basic_filter =>
'sql_id = ''f9k42ab71mn8q''
AND plan_hash_value = 1847263512');

Step 13: Purge Existing SQL from Shared Pool

This forces Oracle to re-parse and pick the new SQL Plan Baseline.

Generate purge command:

SELECT 'exec DBMS_SHARED_POOL.PURGE('''
|| ADDRESS || ',' || HASH_VALUE || ''',''C'');'
FROM v$sqlarea
WHERE sql_id IN ('f9k42ab71mn8q');

Execute generated command:

EXEC DBMS_SHARED_POOL.PURGE(
'0000001006B396C8,2113289046',
'C');

Step 14: Alternative Method - Load from Cursor Cache

DECLARE
i NATURAL;
BEGIN

i := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
'f9k42ab71mn8q',
1847263512);

END;
/

Validation Queries

Check SQL Baselines

SELECT sql_handle,
plan_name,
enabled,
accepted,
fixed
FROM dba_sql_plan_baselines
WHERE signature IN (
SELECT exact_matching_signature
FROM v$sql
WHERE sql_id='f9k42ab71mn8q');

Verify Active Plan

SELECT sql_id,
child_number,
plan_hash_value,
executions,
elapsed_time
FROM v$sql
WHERE sql_id='f9k42ab71mn8q';

Check if Baseline is Used

SELECT sql_id,
sql_plan_baseline,
plan_hash_value
FROM v$sql
WHERE sql_id='f9k42ab71mn8q';

Oracle Apps DBA Production Checklist

CheckStatus
Good SQL_ID identified
Correct PLAN_HASH_VALUE captured
STS created successfully
SQL loaded into STS
Staging table created
STS packed successfully
Data Pump export completed
Dump transferred securely
Import completed in Production
STS unpacked successfully
SQL Plan Baseline loaded
Old cursor purged
Concurrent request rerun
Improved performance validated

Real-Time Oracle EBS Scenario

Problem

A month-end concurrent request that normally completes in 2 minutes suddenly started taking 4 hours after statistics gathering.

Root Cause

Optimizer selected a new bad execution plan with:

  • Full Table Scan
  • High Logical Reads
  • Excessive Nested Loop Operations
  • Large TEMP Usage

Solution

DBA identified a good historical plan from Source Environment and migrated it to Production using:

  • SQL Tuning Set (STS)
  • SQL Plan Baseline (SPM)

Result

BeforeAfter
Runtime: 4 HoursRuntime: 2 Minutes
TEMP SpikeStable TEMP
CPU HighCPU Normal
Business DelayBusiness Success

Important Notes

Best Practices

  • Always validate the plan in lower environments first.
  • Never purge shared pool aggressively in peak production hours.
  • Take business approval before rerunning concurrent requests.
  • Verify plan stability after stats gathering.
  • Monitor AWR and ASH after implementation.

Important DBA Views

ViewPurpose
V$SQLActive SQL details
V$SQLAREAAggregated SQL statistics
DBA_SQLSETSQL Tuning Sets
DBA_SQL_PLAN_BASELINESSQL Baselines
V$SQL_MONITORReal-time SQL monitoring
DBA_HIST_SQLSTATHistorical SQL statistics
DBA_HIST_ACTIVE_SESS_HISTORYASH performance analysis

Conclusion

Using SQL Plan Management (SPM) and SQL Tuning Sets (STS) is one of the safest methods to stabilize SQL performance in Oracle EBS 12.2 Production environments.

This approach helps Oracle Apps DBAs:

  • Avoid risky code changes
  • Restore performance quickly
  • Reduce month-end failures
  • Stabilize execution plans
  • Improve business confidence

It is a critical real-world DBA skill for handling production SQL regressions.

Friday, May 15, 2026

plan migration

Oracle SQL Plan Migration Runbook

Objective

This runbook explains how to move a good execution plan from a source environment (Source Environment) to a Production environment using:

  • SQL Tuning Set (STS)
  • Data Pump (expdp/impdp)
  • SQL Plan Management (SPM)

This approach is commonly used by Oracle Apps DBAs during:

  • SQL Plan Regression
  • Month-End Performance Issues
  • Concurrent Program Slow Performance
  • Optimizer Plan Changes after Statistics Gathering
  • Oracle EBS 12.2 Performance Stabilization

High-Level Flow

Identify Good Plan
        ↓
Create SQL Tuning Set (STS)
        ↓
Load Good SQL into STS
        ↓
Create STS Staging Table
        ↓
Export STS Table using Data Pump
        ↓
Transfer Dump File to Production
        ↓
Import STS Table into Production
        ↓
Unpack STS
        ↓
Load Plan into SQL Plan Baseline
        ↓
Purge Old Cursor from Shared Pool
        ↓
Re-execute SQL / Concurrent Program
        ↓
Validate Improved Execution Plan

Source Environment Steps (Source Environment)

Step 1: Identify the Good SQL Plan

Find the SQL_ID and PLAN_HASH_VALUE of the good plan.

SELECT sql_id,
       plan_hash_value
FROM   v$sqlarea
WHERE  sql_id IN ('f9k42ab71mn8q');

OR

SELECT sql_id,
       plan_hash_value
FROM   v$sql
WHERE  sql_id IN ('f9k42ab71mn8q');

Step 2: Create Empty SQL Tuning Set (STS)

BEGIN
  DBMS_SQLTUNE.CREATE_SQLSET(
      sqlset_name => 'f9k42ab71mn8q_STS',
      description => 'STS to move better plan to Production');
END;
/

Step 3: Load SQL Information into STS

DECLARE
  s_sqlarea_cursor DBMS_SQLTUNE.SQLSET_CURSOR;
BEGIN

  OPEN s_sqlarea_cursor FOR
  SELECT VALUE(p)
  FROM TABLE(
       DBMS_SQLTUNE.SELECT_CURSOR_CACHE(
       'sql_id = ''f9k42ab71mn8q''
        AND plan_hash_value = 1847263512')) p;

  DBMS_SQLTUNE.LOAD_SQLSET(
      sqlset_name     => 'f9k42ab71mn8q_STS',
      populate_cursor => s_sqlarea_cursor);

END;
/

Step 4: Verify SQL Tuning Set Contents

SELECT name,
       statement_count,
       description
FROM   dba_sqlset;

Formatting commands:

COLUMN sql_text FORMAT a30
COLUMN sch FORMAT a3
COLUMN elapsed FORMAT 999999999

Query STS contents:

SELECT sql_id,
       parsing_schema_name AS "SCH",
       sql_text,
       elapsed_time AS "ELAPSED",
       buffer_gets
FROM TABLE(
     DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Detailed verification:

SELECT *
FROM TABLE(
     DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Step 5: Create Staging Table

Note: Table names and parameters are case-sensitive.

EXEC DBMS_SQLTUNE.CREATE_STGTAB_SQLSET(
     table_name => 'TEST');

Step 6: Pack STS into Staging Table

BEGIN

  DBMS_SQLTUNE.PACK_STGTAB_SQLSET(
      sqlset_name         => 'f9k42ab71mn8q_STS',
      sqlset_owner        => 'SYSTEM',
      staging_table_name  => 'TEST',
      staging_schema_owner=> 'SYSTEM');

END;
/

Step 7: Create Oracle Directory Object

CREATE DIRECTORY TEST AS
'/opt/mis/ebs_backup_lv/backup_1/EBS_SQLSET_BACKUP';

Step 8: Export Staging Table using Data Pump

expdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 9: Transfer Dump File to Production

Transfer the dump file using:

  • SCP
  • FTP
  • SFTP

Example:

scp f9k42ab71mn8q_STS.dmp oracle@targetserver:/backup

Target Environment Steps (Production)

Step 10: Import the Dump File

impdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 11: Unpack SQL Tuning Set

BEGIN

  DBMS_SQLTUNE.UNPACK_STGTAB_SQLSET(
      sqlset_name          => '%',
      sqlset_owner         => 'SYSTEM',
      replace              => TRUE,
      staging_table_name   => 'TEST',
      staging_schema_owner => 'SYSTEM');

END;
/

Step 12: Load Plan from STS into SQL Plan Baseline

VARIABLE v_plan_cnt NUMBER

EXECUTE :v_plan_cnt := DBMS_SPM.LOAD_PLANS_FROM_SQLSET(
         sqlset_name => 'f9k42ab71mn8q_STS',
         sqlset_owner => 'SYSTEM',
         basic_filter =>
         'sql_id = ''f9k42ab71mn8q''
          AND plan_hash_value = 1847263512');

Step 13: Purge Existing SQL from Shared Pool

This forces Oracle to re-parse and pick the new SQL Plan Baseline.

Generate purge command:

SELECT 'exec DBMS_SHARED_POOL.PURGE('''
       || ADDRESS || ',' || HASH_VALUE || ''',''C'');'
FROM   v$sqlarea
WHERE  sql_id IN ('f9k42ab71mn8q');

Execute generated command:

EXEC DBMS_SHARED_POOL.PURGE(
'0000001006B396C8,2113289046',
'C');

Step 14: Alternative Method - Load from Cursor Cache

DECLARE
  i NATURAL;
BEGIN

  i := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
       'f9k42ab71mn8q',
       1847263512);

END;
/

Validation Queries

Check SQL Baselines

SELECT sql_handle,
       plan_name,
       enabled,
       accepted,
       fixed
FROM   dba_sql_plan_baselines
WHERE  signature IN (
       SELECT exact_matching_signature
       FROM   v$sql
       WHERE  sql_id='f9k42ab71mn8q');

Verify Active Plan

SELECT sql_id,
       child_number,
       plan_hash_value,
       executions,
       elapsed_time
FROM   v$sql
WHERE  sql_id='f9k42ab71mn8q';

Check if Baseline is Used

SELECT sql_id,
       sql_plan_baseline,
       plan_hash_value
FROM   v$sql
WHERE  sql_id='f9k42ab71mn8q';

Oracle Apps DBA Production Checklist

Check Status
Good SQL_ID identified
Correct PLAN_HASH_VALUE captured
STS created successfully
SQL loaded into STS
Staging table created
STS packed successfully
Data Pump export completed
Dump transferred securely
Import completed in Production
STS unpacked successfully
SQL Plan Baseline loaded
Old cursor purged
Concurrent request rerun
Improved performance validated

Real-Time Oracle EBS Scenario

Problem

A month-end concurrent request that normally completes in 2 minutes suddenly started taking 4 hours after statistics gathering.

Root Cause

Optimizer selected a new bad execution plan with:

  • Full Table Scan
  • High Logical Reads
  • Excessive Nested Loop Operations
  • Large TEMP Usage

Solution

DBA identified a good historical plan from Source Environment and migrated it to Production using:

  • SQL Tuning Set (STS)
  • SQL Plan Baseline (SPM)

Result

Before After
Runtime: 4 Hours Runtime: 2 Minutes
TEMP Spike Stable TEMP
CPU High CPU Normal
Business Delay Business Success

Important Notes

Best Practices

  • Always validate the plan in lower environments first.
  • Never purge shared pool aggressively in peak production hours.
  • Take business approval before rerunning concurrent requests.
  • Verify plan stability after stats gathering.
  • Monitor AWR and ASH after implementation.

Important DBA Views

View Purpose
V$SQL Active SQL details
V$SQLAREA Aggregated SQL statistics
DBA_SQLSET SQL Tuning Sets
DBA_SQL_PLAN_BASELINES SQL Baselines
V$SQL_MONITOR Real-time SQL monitoring
DBA_HIST_SQLSTAT Historical SQL statistics
DBA_HIST_ACTIVE_SESS_HISTORY ASH performance analysis

Conclusion

Using SQL Plan Management (SPM) and SQL Tuning Sets (STS) is one of the safest methods to stabilize SQL performance in Oracle EBS 12.2 Production environments.

This approach helps Oracle Apps DBAs:

  • Avoid risky code changes
  • Restore performance quickly
  • Reduce month-end failures
  • Stabilize execution plans
  • Improve business confidence

It is a critical real-world DBA skill for handling production SQL regressions.

Oracle SQL Plan Migration Runbook

 

Oracle SQL Plan Migration Runbook

Objective

This runbook explains how to move a good execution plan from a source environment (Source Environment) to a Production environment using:

  • SQL Tuning Set (STS)
  • Data Pump (expdp/impdp)
  • SQL Plan Management (SPM)

This approach is commonly used by Oracle Apps DBAs during:

  • SQL Plan Regression
  • Month-End Performance Issues
  • Concurrent Program Slow Performance
  • Optimizer Plan Changes after Statistics Gathering
  • Oracle EBS 12.2 Performance Stabilization

High-Level Flow

Identify Good Plan
Create SQL Tuning Set (STS)
Load Good SQL into STS
Create STS Staging Table
Export STS Table using Data Pump
Transfer Dump File to Production
Import STS Table into Production
Unpack STS
Load Plan into SQL Plan Baseline
Purge Old Cursor from Shared Pool
Re-execute SQL / Concurrent Program
Validate Improved Execution Plan

Source Environment Steps (Source Environment)

Step 1: Identify the Good SQL Plan

Find the SQL_ID and PLAN_HASH_VALUE of the good plan.

SELECT sql_id,
plan_hash_value
FROM v$sqlarea
WHERE sql_id IN ('f9k42ab71mn8q');

OR

SELECT sql_id,
plan_hash_value
FROM v$sql
WHERE sql_id IN ('f9k42ab71mn8q');

Step 2: Create Empty SQL Tuning Set (STS)

BEGIN
DBMS_SQLTUNE.CREATE_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
description => 'STS to move better plan to Production');
END;
/

Step 3: Load SQL Information into STS

DECLARE
s_sqlarea_cursor DBMS_SQLTUNE.SQLSET_CURSOR;
BEGIN

OPEN s_sqlarea_cursor FOR
SELECT VALUE(p)
FROM TABLE(
DBMS_SQLTUNE.SELECT_CURSOR_CACHE(
'sql_id = ''f9k42ab71mn8q''
AND plan_hash_value = 1847263512')) p;

DBMS_SQLTUNE.LOAD_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
populate_cursor => s_sqlarea_cursor);

END;
/

Step 4: Verify SQL Tuning Set Contents

SELECT name,
statement_count,
description
FROM dba_sqlset;

Formatting commands:

COLUMN sql_text FORMAT a30
COLUMN sch FORMAT a3
COLUMN elapsed FORMAT 999999999

Query STS contents:

SELECT sql_id,
parsing_schema_name AS "SCH",
sql_text,
elapsed_time AS "ELAPSED",
buffer_gets
FROM TABLE(
DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Detailed verification:

SELECT *
FROM TABLE(
DBMS_SQLTUNE.SELECT_SQLSET('f9k42ab71mn8q_STS'));

Step 5: Create Staging Table

Note: Table names and parameters are case-sensitive.

EXEC DBMS_SQLTUNE.CREATE_STGTAB_SQLSET(
table_name => 'TEST');

Step 6: Pack STS into Staging Table

BEGIN

DBMS_SQLTUNE.PACK_STGTAB_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
sqlset_owner => 'SYSTEM',
staging_table_name => 'TEST',
staging_schema_owner=> 'SYSTEM');

END;
/

Step 7: Create Oracle Directory Object

CREATE DIRECTORY TEST AS
'tmp/EBS_SQLSET_BACKUP';

Step 8: Export Staging Table using Data Pump

expdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 9: Transfer Dump File to Production

Transfer the dump file using:

  • SCP
  • FTP
  • SFTP

Example:

scp f9k42ab71mn8q_STS.dmp oracle@targetserver:/backup

Target Environment Steps (Production)

Step 10: Import the Dump File

impdp system DIRECTORY=TEST \
DUMPFILE=f9k42ab71mn8q_STS.dmp \
TABLES=TEST

Step 11: Unpack SQL Tuning Set

BEGIN

DBMS_SQLTUNE.UNPACK_STGTAB_SQLSET(
sqlset_name => '%',
sqlset_owner => 'SYSTEM',
replace => TRUE,
staging_table_name => 'TEST',
staging_schema_owner => 'SYSTEM');

END;
/

Step 12: Load Plan from STS into SQL Plan Baseline

VARIABLE v_plan_cnt NUMBER

EXECUTE :v_plan_cnt := DBMS_SPM.LOAD_PLANS_FROM_SQLSET(
sqlset_name => 'f9k42ab71mn8q_STS',
sqlset_owner => 'SYSTEM',
basic_filter =>
'sql_id = ''f9k42ab71mn8q''
AND plan_hash_value = 1847263512');

Step 13: Purge Existing SQL from Shared Pool

This forces Oracle to re-parse and pick the new SQL Plan Baseline.

Generate purge command:

SELECT 'exec DBMS_SHARED_POOL.PURGE('''
|| ADDRESS || ',' || HASH_VALUE || ''',''C'');'
FROM v$sqlarea
WHERE sql_id IN ('f9k42ab71mn8q');

Execute generated command:

EXEC DBMS_SHARED_POOL.PURGE(
'0000001006B396C8,2113289046',
'C');

Step 14: Alternative Method - Load from Cursor Cache

DECLARE
i NATURAL;
BEGIN

i := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
'f9k42ab71mn8q',
1847263512);

END;
/

Validation Queries

Check SQL Baselines

SELECT sql_handle,
plan_name,
enabled,
accepted,
fixed
FROM dba_sql_plan_baselines
WHERE signature IN (
SELECT exact_matching_signature
FROM v$sql
WHERE sql_id='f9k42ab71mn8q');

Verify Active Plan

SELECT sql_id,
child_number,
plan_hash_value,
executions,
elapsed_time
FROM v$sql
WHERE sql_id='f9k42ab71mn8q';

Check if Baseline is Used

SELECT sql_id,
sql_plan_baseline,
plan_hash_value
FROM v$sql
WHERE sql_id='f9k42ab71mn8q';

Oracle Apps DBA Production Checklist

CheckStatus
Good SQL_ID identified
Correct PLAN_HASH_VALUE captured
STS created successfully
SQL loaded into STS
Staging table created
STS packed successfully
Data Pump export completed
Dump transferred securely
Import completed in Production
STS unpacked successfully
SQL Plan Baseline loaded
Old cursor purged
Concurrent request rerun
Improved performance validated

Real-Time Oracle EBS Scenario

Problem

A month-end concurrent request that normally completes in 2 minutes suddenly started taking 4 hours after statistics gathering.

Root Cause

Optimizer selected a new bad execution plan with:

  • Full Table Scan
  • High Logical Reads
  • Excessive Nested Loop Operations
  • Large TEMP Usage

Solution

DBA identified a good historical plan from Source Environment and migrated it to Production using:

  • SQL Tuning Set (STS)
  • SQL Plan Baseline (SPM)

Result

BeforeAfter
Runtime: 4 HoursRuntime: 2 Minutes
TEMP SpikeStable TEMP
CPU HighCPU Normal
Business DelayBusiness Success

Important Notes

Best Practices

  • Always validate the plan in lower environments first.
  • Never purge shared pool aggressively in peak production hours.
  • Take business approval before rerunning concurrent requests.
  • Verify plan stability after stats gathering.
  • Monitor AWR and ASH after implementation.

Important DBA Views

ViewPurpose
V$SQLActive SQL details
V$SQLAREAAggregated SQL statistics
DBA_SQLSETSQL Tuning Sets
DBA_SQL_PLAN_BASELINESSQL Baselines
V$SQL_MONITORReal-time SQL monitoring
DBA_HIST_SQLSTATHistorical SQL statistics
DBA_HIST_ACTIVE_SESS_HISTORYASH performance analysis

Conclusion

Using SQL Plan Management (SPM) and SQL Tuning Sets (STS) is one of the safest methods to stabilize SQL performance in Oracle EBS 12.2 Production environments.

This approach helps Oracle Apps DBAs:

  • Avoid risky code changes
  • Restore performance quickly
  • Reduce month-end failures
  • Stabilize execution plans
  • Improve business confidence

It is a critical real-world DBA skill for handling production SQL regressions.