Thursday, October 1, 2026

Continue

 ================================================================================

 RT6 - SQL IN THE CURSOR CACHE

================================================================================

 

-- RT6.1  Per-execution profile of SQL currently being executed

-- Note : GV$SQL values are cumulative since the cursor was loaded.

SELECT q.inst_id, q.sql_id, q.child_number, q.plan_hash_value, q.executions, q.users_executing,

       ROUND(q.elapsed_time/1e6/NULLIF(q.executions,0),2)       AS ela_pe_s,

       ROUND(q.cpu_time/1e6/NULLIF(q.executions,0),2)           AS cpu_pe_s,

       ROUND(q.user_io_wait_time/1e6/NULLIF(q.executions,0),2)  AS io_pe_s,

       ROUND(q.buffer_gets/NULLIF(q.executions,0))              AS gets_pe,

       ROUND(q.disk_reads/NULLIF(q.executions,0))               AS reads_pe,

       ROUND(q.rows_processed/NULLIF(q.executions,0),1)         AS rows_pe,

       q.module, q.last_active_time

FROM   gv$sql q

WHERE  (q.inst_id, q.sql_id, q.child_number) IN

       (SELECT inst_id, sql_id, sql_child_number

        FROM   gv$session

        WHERE  type = 'USER' AND status = 'ACTIVE' AND sql_id IS NOT NULL)

ORDER  BY ela_pe_s DESC NULLS LAST;

 

 

-- RT6.2  Top SQL active in the last hour by total elapsed (cumulative stats)

SELECT inst_id, sql_id, plan_hash_value, executions,

       ROUND(elapsed_time/1e6)                             AS ela_s,

       ROUND(cpu_time/1e6)                                 AS cpu_s,

       ROUND(elapsed_time/1e6/NULLIF(executions,0),2)      AS ela_pe_s,

       ROUND(buffer_gets/NULLIF(executions,0))             AS gets_pe,

       ROUND(disk_reads/NULLIF(executions,0))              AS reads_pe,

       last_active_time

FROM   gv$sqlstats

WHERE  last_active_time > SYSDATE - 1/24

ORDER  BY elapsed_time DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT6.3  One SQL_ID: all children and their per-execution cost

SELECT inst_id, sql_id, child_number, plan_hash_value, executions, users_executing,

       ROUND(elapsed_time/1e6/NULLIF(executions,0),3)  AS ela_pe_s,

       ROUND(buffer_gets/NULLIF(executions,0))         AS gets_pe,

       ROUND(disk_reads/NULLIF(executions,0))          AS reads_pe,

       ROUND(rows_processed/NULLIF(executions,0),1)    AS rows_pe,

       optimizer_cost, is_bind_sensitive, is_bind_aware, is_shareable,

       sql_profile, sql_plan_baseline, last_active_time

FROM   gv$sql

WHERE  sql_id = :sql_id

ORDER  BY inst_id, child_number;

 

 

-- RT6.4  Plan with predicates and peeked binds (run on the instance holding the cursor)

-- Note : ALLSTATS / A-Rows only exist if STATISTICS_LEVEL=ALL or the

--        GATHER_PLAN_STATISTICS hint was used. For actual rows use RT7.2.

SELECT *

FROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR(:sql_id, :child_no,

                                       'TYPICAL +PEEKED_BINDS +PREDICATE +OUTLINE'));

 

 

-- RT6.5  Captured binds

SELECT inst_id, sql_id, child_number, position, name, datatype_string, last_captured, value_string

FROM   gv$sql_bind_capture

WHERE  sql_id = :sql_id

ORDER  BY inst_id, child_number, position;

 

 

-- RT6.6  SQL with many child cursors (cursor-sharing problems)

SELECT inst_id, sql_id, COUNT(*) AS children, SUM(executions) AS execs,

       SUBSTR(MAX(sql_text),1,120) AS sql_text

FROM   gv$sql

GROUP  BY inst_id, sql_id

HAVING COUNT(*) > 20

ORDER  BY children DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT6.7  Why children were not shared (look for 'Y' columns)

SELECT *

FROM   gv$sql_shared_cursor

WHERE  sql_id = :sql_id

AND    ROWNUM <= 20;

 

 

-- RT6.8  Optimizer settings that differ between children of one SQL_ID

SELECT inst_id, name,

       COUNT(DISTINCT value) AS distinct_values,

       LISTAGG(child_number || '=' || value, ', ') WITHIN GROUP (ORDER BY child_number) AS child_values

FROM   gv$sql_optimizer_env

WHERE  sql_id = :sql_id

GROUP  BY inst_id, name

HAVING COUNT(DISTINCT value) > 1

ORDER  BY inst_id, name;

 

 

================================================================================

 RT7 - SQL MONITOR (TUNING PACK) AND LONGOPS

================================================================================

 

-- RT7.1  Monitored SQL executing now

SELECT inst_id, sid, session_serial#, sql_id, sql_exec_id, sql_exec_start, status,

       username, module, px_servers_allocated,

       ROUND(elapsed_time/1e6)              AS ela_s,

       ROUND(cpu_time/1e6)                  AS cpu_s,

       ROUND(user_io_wait_time/1e6)         AS io_s,

       ROUND(application_wait_time/1e6)     AS app_s,

       ROUND(concurrency_wait_time/1e6)     AS conc_s,

       ROUND(cluster_wait_time/1e6)         AS cluster_s,

       buffer_gets, disk_reads, physical_read_requests

FROM   gv$sql_monitor

WHERE  status = 'EXECUTING'

ORDER  BY elapsed_time DESC;

 

 

-- RT7.2  Actual rows / starts / reads per plan line for one execution

-- Read : E-rows vs A-rows gaps = cardinality problem; huge starts on an

--        index line with few rows returned higher up = excessive probing.

SELECT plan_line_id,

       LPAD(' ', plan_depth) || plan_operation || ' ' || plan_options AS operation,

       plan_object_owner, plan_object_name,

       plan_cardinality                      AS e_rows,

       starts,

       output_rows                           AS a_rows,

       physical_read_requests,

       ROUND(physical_read_bytes/1048576)    AS read_mb,

       ROUND(workarea_mem/1048576)           AS wa_mem_mb,

       ROUND(workarea_tempseg/1048576)       AS wa_temp_mb

FROM   gv$sql_plan_monitor

WHERE  sql_id      = :sql_id

AND    sql_exec_id = :sql_exec_id

AND    inst_id     = :inst_id

ORDER  BY plan_line_id;

 

 

-- RT7.3  Where the time goes inside that execution (ASH by plan line)

SELECT sql_plan_line_id, sql_plan_operation, sql_plan_options,

       NVL(event,'ON CPU') AS event, COUNT(*) AS samples

FROM   gv$active_session_history

WHERE  sql_id      = :sql_id

AND    sql_exec_id = :sql_exec_id

GROUP  BY sql_plan_line_id, sql_plan_operation, sql_plan_options, event

ORDER  BY samples DESC;

 

 

-- RT7.4  Text SQL Monitor report for one execution

SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(sql_id       => :sql_id,

                                       sql_exec_id  => :sql_exec_id,

                                       type         => 'TEXT',

                                       report_level => 'ALL')

FROM   dual;

 

 

-- RT7.5  Long operations in progress (instrumented operations only)

-- Note : an empty result does NOT mean the session is idle.

SELECT l.inst_id, l.sid, l.serial#, s.module, l.opname, l.target, l.sofar, l.totalwork, l.units,

       ROUND(l.sofar/NULLIF(l.totalwork,0)*100,1) AS pct_done,

       l.elapsed_seconds, l.time_remaining, l.sql_id

FROM   gv$session_longops l

JOIN   gv$session s ON s.inst_id = l.inst_id AND s.sid = l.sid AND s.serial# = l.serial#

WHERE  l.totalwork > 0

AND    l.sofar < l.totalwork

ORDER  BY l.elapsed_seconds DESC;

 

 

================================================================================

 RT8 - I/O LATENCY NOW

================================================================================

 

-- RT8.1  Per-datafile read latency in the last metric interval

-- Read : average_read_time is in centiseconds -> *10 = ms. One file or

--        diskgroup much slower than the rest = storage hotspot.

SELECT f.inst_id, d.tablespace_name, d.file_name,

       f.physical_reads,

       ROUND(f.average_read_time * 10, 2)   AS avg_read_ms,

       f.physical_writes,

       ROUND(f.average_write_time * 10, 2)  AS avg_write_ms,

       TO_CHAR(f.begin_time,'HH24:MI:SS')   AS from_t,

       TO_CHAR(f.end_time,'HH24:MI:SS')     AS to_t

FROM   gv$filemetric f

JOIN   dba_data_files d ON d.file_id = f.file_id

WHERE  f.physical_reads > 0

ORDER  BY f.physical_reads DESC

FETCH FIRST 25 ROWS ONLY;

 

 

-- RT8.2  I/O wait profile by event, last 15 minutes (live ASH)

SELECT event, COUNT(*) AS samples,

       COUNT(DISTINCT inst_id || ':' || session_id || ',' || session_serial#) AS sessions,

       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100,1) AS pct

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 15/1440

AND    wait_class = 'User I/O'

GROUP  BY event

ORDER  BY samples DESC;

 

 

================================================================================

 RT9 - TEMP, PGA, WORKAREAS

================================================================================

 

-- RT9.1  TEMP consumers (real block size, not assumed 8 KB)

SELECT s.inst_id, s.sid, s.serial#, s.username, s.module, s.sql_id AS current_sql,

       u.sql_id AS temp_sql_id, u.tablespace, u.segtype,

       ROUND(u.blocks * t.block_size / 1048576) AS temp_mb

FROM   gv$tempseg_usage u

JOIN   gv$session s

       ON s.inst_id = u.inst_id AND s.saddr = u.session_addr

JOIN   dba_tablespaces t ON t.tablespace_name = u.tablespace

ORDER  BY u.blocks DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT9.2  TEMP tablespace headroom

SELECT tablespace_name,

       ROUND(tablespace_size/1048576)  AS size_mb,

       ROUND(allocated_space/1048576)  AS allocated_mb,

       ROUND(free_space/1048576)       AS free_mb

FROM   dba_temp_free_space;

 

 

-- RT9.3  Active sort/hash workareas (spills in flight)

-- Read : number_passes > 0 or large tempseg_mb = workarea spilling to TEMP.

SELECT inst_id, sid, sql_id, operation_type, policy,

       ROUND(actual_mem_used/1048576) AS mem_mb,

       ROUND(max_mem_used/1048576)    AS max_mem_mb,

       number_passes,

       ROUND(tempseg_size/1048576)    AS tempseg_mb

FROM   gv$sql_workarea_active

ORDER  BY tempseg_size DESC NULLS LAST, actual_mem_used DESC;

 

 

-- RT9.4  PGA overview per instance

SELECT inst_id, name, ROUND(value/1048576) AS mb_or_count

FROM   gv$pgastat

WHERE  name IN ('aggregate PGA target parameter','total PGA allocated',

                'total PGA inuse','maximum PGA allocated','over allocation count')

ORDER  BY inst_id, name;

 

 

================================================================================

 RT10 - UNDO AND OPEN TRANSACTIONS

================================================================================

 

-- RT10.1  Open transactions by undo used, with owning session

-- Read : large used_ublk on an INACTIVE session = long uncommitted transaction.

SELECT s.inst_id, s.sid, s.serial#, s.username, s.module, s.action, s.status,

       s.last_call_et, s.sql_id, s.prev_sql_id,

       t.start_time, t.used_ublk, t.used_urec,

       ROUND(t.used_ublk * TO_NUMBER(p.value) / 1048576) AS undo_mb,

       t.status AS txn_status

FROM   gv$transaction t

JOIN   gv$session s ON s.inst_id = t.inst_id AND s.taddr = t.addr

JOIN   gv$parameter p ON p.inst_id = t.inst_id AND p.name = 'db_block_size'

ORDER  BY t.used_ublk DESC;

 

 

-- RT10.2  Undo extent status

SELECT tablespace_name, status, ROUND(SUM(bytes)/1048576) AS mb

FROM   dba_undo_extents

GROUP  BY tablespace_name, status

ORDER  BY tablespace_name, status;

 

 

================================================================================

 RT11 - DRILL INTO ONE SESSION

================================================================================

 

-- RT11.1  Session identity and OS process

SELECT s.inst_id, s.sid, s.serial#, s.audsid, s.username, s.osuser, s.machine,

       s.program, s.module, s.action, s.client_identifier,

       p.spid AS os_pid, s.logon_time, s.status, s.last_call_et

FROM   gv$session s

JOIN   gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id

WHERE  s.inst_id = :inst_id

AND    s.sid     = :sid;

 

 

-- RT11.2  Session wait totals since logon

SELECT event, wait_class, total_waits,

       ROUND(time_waited_micro/1e6,1)                                   AS waited_s,

       ROUND(time_waited_micro/1e3/NULLIF(total_waits,0),2)             AS avg_ms

FROM   gv$session_event

WHERE  inst_id = :inst_id

AND    sid     = :sid

AND    wait_class <> 'Idle'

ORDER  BY time_waited_micro DESC;

 

 

-- RT11.3  Key session statistics since logon

SELECT n.name, st.value

FROM   gv$sesstat st

JOIN   gv$statname n ON n.inst_id = st.inst_id AND n.statistic# = st.statistic#

WHERE  st.inst_id = :inst_id

AND    st.sid     = :sid

AND    n.name IN ('CPU used by this session','session logical reads',

                  'physical reads','physical read IO requests',

                  'parse count (hard)','execute count','user commits',

                  'redo size','session pga memory','session pga memory max')

ORDER  BY n.name;

 

 

-- RT11.4  What this session did in the last 30 minutes (live ASH)

SELECT sql_id, NVL(event,'ON CPU') AS event, COUNT(*) AS samples,

       MIN(sample_time) AS first_seen, MAX(sample_time) AS last_seen

FROM   gv$active_session_history

WHERE  inst_id         = :inst_id

AND    session_id      = :sid

AND    session_serial# = :serial

AND    sample_time > SYSDATE - 30/1440

GROUP  BY sql_id, event

ORDER  BY samples DESC;

 

 

================================================================================

 RT12 - RAC AND RESOURCE LIMITS

================================================================================

 

-- RT12.1  Cluster waits in the last 15 minutes by event and object

SELECT h.inst_id, h.event, o.owner, o.object_name, COUNT(*) AS samples

FROM   gv$active_session_history h

LEFT JOIN dba_objects o ON o.object_id = h.current_obj#

WHERE  h.sample_time > SYSDATE - 15/1440

AND    h.wait_class = 'Cluster'

GROUP  BY h.inst_id, h.event, o.owner, o.object_name

ORDER  BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT12.2  Sessions / processes vs limits

SELECT inst_id, resource_name, current_utilization, max_utilization,

       initial_allocation, limit_value

FROM   gv$resource_limit

WHERE  resource_name IN ('processes','sessions','transactions','parallel_max_servers')

ORDER  BY inst_id, resource_name;

 

 

================================================================================

 QUICK TRIAGE ORDER

================================================================================

 1. RT1.1 / RT1.3   -> CPU, I/O, locks or RAC?  Where is the load?

 2. RT4.1           -> If anyone is blocked, the blocker is the RCA first.

 3. RT3.1 / RT3.3   -> Which EBS requests are running / stuck pending?

 4. RT2.1 / RT2.3   -> Which SQL and modules dominate the last 15 minutes?

 5. RT6.1 / RT7.2   -> Per-exec cost and actual rows for the top SQL.

 6. RT8.1           -> Is storage latency abnormal right now?

 7. RT9 / RT10      -> TEMP spills, long transactions, undo.

================================================================================

 END OF PACK

================================================================================

REAL-TIME PERFORMANCE QUERY PACK

================================================================================

 ORACLE EBS 12.2 / DATABASE 19c - REAL-TIME PERFORMANCE QUERY PACK (GV$ / V$)

 Read-only. Built from the "Performance Issue RCA - Deep Dive Query Workbook"

 view list, with the review fixes applied:

   - CPU vs wait separated using STATE (not WAIT_CLASS alone)

   - ASH object attribution only for waiting samples

   - INST_ID included in every distinct-session count (RAC)

   - Hot blocks from ROW_WAIT_* (no P1/P2 misreads for latches)

   - Real block size used for TEMP / undo MB

   - Pending requests limited to runnable ones; CM target vs actual

================================================================================

 

LICENSING

---------

- GV$ACTIVE_SESSION_HISTORY (live ASH) requires the Diagnostics Pack.

- GV$SQL_MONITOR, GV$SQL_PLAN_MONITOR and DBMS_SQLTUNE.REPORT_SQL_MONITOR

  require the Tuning Pack.

- Confirm metric-view licensing (GV$SYSMETRIC*, GV$FILEMETRIC) against your

  Oracle licensing guide.

 

USAGE

-----

- Run as a DBA user with SELECT on GV$ and DBA_ views; FND queries need

  access to the APPS objects (run as APPS or via synonyms/grants).

- Bind variables (set before running):

    :inst_id  :sid  :serial  :sql_id  :sql_exec_id  :child_no  :request_id

- Recommended SQL*Plus settings:

SET LINESIZE 400 PAGESIZE 5000 TRIMSPOOL ON LONG 1000000 LONGCHUNKSIZE 1000000

ALTER SESSION SET nls_date_format = 'YYYY-MM-DD HH24:MI:SS';

  (ALTER SESSION changes only your own session display format.)

 

 

================================================================================

 RT0 - CONTEXT: INSTANCES, CPU CAPACITY, ASH COVERAGE

================================================================================

 

-- RT0.1  Instances

SELECT inst_id, instance_name, host_name, status, startup_time, version

FROM   gv$instance

ORDER  BY inst_id;

 

-- RT0.2  CPU capacity (yardstick for "sessions on CPU")

SELECT inst_id, stat_name, value

FROM   gv$osstat

WHERE  stat_name IN ('NUM_CPUS','NUM_CPU_CORES','LOAD')

ORDER  BY inst_id, stat_name;

 

-- RT0.3  How far back live ASH reaches on each instance (varies with load)

SELECT inst_id, MIN(sample_time) AS oldest_sample, MAX(sample_time) AS newest_sample,

       ROUND((CAST(MAX(sample_time) AS DATE) - CAST(MIN(sample_time) AS DATE)) * 24, 1) AS hours_covered

FROM   gv$active_session_history

GROUP  BY inst_id

ORDER  BY inst_id;

 

 

================================================================================

 RT1 - WHAT IS THE DATABASE DOING RIGHT NOW

================================================================================

 

-- RT1.1  Current system metrics (last 60-second interval)

-- Read : CPU Usage Per Sec / 100 = CPU cores used by the DB.

--        Host CPU high but DB CPU low -> load is outside the database.

--        Single-block read latency is the live storage-latency check.

SELECT inst_id, metric_name, ROUND(value,2) AS value, metric_unit,

       TO_CHAR(begin_time,'HH24:MI:SS') AS from_t, TO_CHAR(end_time,'HH24:MI:SS') AS to_t

FROM   gv$sysmetric

WHERE  group_id = 2

AND    metric_name IN ('Host CPU Utilization (%)',

                       'CPU Usage Per Sec',

                       'Average Active Sessions',

                       'Database Time Per Sec',

                       'Average Synchronous Single-Block Read Latency',

                       'Physical Read Total IO Requests Per Sec',

                       'Physical Read Total Bytes Per Sec',

                       'Executions Per Sec',

                       'Hard Parse Count Per Sec',

                       'User Transaction Per Sec',

                       'Redo Generated Per Sec',

                       'Session Count',

                       'Global Cache Average CR Get Time',

                       'Global Cache Average Current Get Time')

ORDER  BY metric_name, inst_id;

 

 

-- RT1.2  Same metrics minute-by-minute for the last hour (trend)

SELECT inst_id, TO_CHAR(begin_time,'HH24:MI') AS minute, metric_name, ROUND(value,2) AS value

FROM   gv$sysmetric_history

WHERE  group_id = 2

AND    begin_time > SYSDATE - 1/24

AND    metric_name IN ('Host CPU Utilization (%)','CPU Usage Per Sec',

                       'Average Active Sessions',

                       'Average Synchronous Single-Block Read Latency')

ORDER  BY metric_name, inst_id, begin_time;

 

 

-- RT1.3  Active-session mix now: CPU vs each wait class (STATE-aware)

-- Read : on_cpu near NUM_CPUS = CPU saturation; large user_io = I/O bound;

--        application = locks; cluster = RAC gc; concurrency = latches/buffer busy.

SELECT inst_id,

       COUNT(*)                                                                         AS active_sess,

       SUM(CASE WHEN state <> 'WAITING'                                THEN 1 ELSE 0 END) AS on_cpu,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'User I/O'     THEN 1 ELSE 0 END) AS user_io,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Application'  THEN 1 ELSE 0 END) AS application,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Concurrency'  THEN 1 ELSE 0 END) AS concurrency,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Cluster'      THEN 1 ELSE 0 END) AS cluster_w,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Commit'       THEN 1 ELSE 0 END) AS commit_w,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Configuration' THEN 1 ELSE 0 END) AS configuration,

       SUM(CASE WHEN state = 'WAITING' AND wait_class = 'Scheduler'    THEN 1 ELSE 0 END) AS resmgr_sched,

       SUM(CASE WHEN state = 'WAITING' AND wait_class NOT IN

                ('User I/O','Application','Concurrency','Cluster','Commit','Configuration','Scheduler')

                                                                       THEN 1 ELSE 0 END) AS other_w

FROM   gv$session

WHERE  type = 'USER'

AND    status = 'ACTIVE'

AND    (state <> 'WAITING' OR wait_class <> 'Idle')

GROUP  BY inst_id

ORDER  BY inst_id;

 

 

-- RT1.4  Active sessions in detail (STATE-aware activity)

SELECT s.inst_id, s.sid, s.serial#, s.username, s.module, s.action, s.machine,

       s.sql_id, s.sql_child_number,

       CASE WHEN s.state = 'WAITING' THEN s.event      ELSE 'ON CPU' END                AS activity,

       CASE WHEN s.state = 'WAITING' THEN s.wait_class ELSE 'CPU'    END                AS act_class,

       CASE WHEN s.state = 'WAITING'

            THEN ROUND(s.wait_time_micro/1e6,1)

            ELSE ROUND(s.time_since_last_wait_micro/1e6,1) END                          AS secs_in_state,

       ROUND((SYSDATE - s.sql_exec_start) * 86400)                                      AS sql_exec_sec,

       s.last_call_et,

       s.blocking_instance, s.blocking_session,

       s.final_blocking_instance, s.final_blocking_session,

       s.row_wait_obj#

FROM   gv$session s

WHERE  s.type = 'USER'

AND    s.status = 'ACTIVE'

AND    (s.state <> 'WAITING' OR s.wait_class <> 'Idle')

ORDER  BY s.last_call_et DESC;

 

 

-- RT1.5  Average active sessions per minute, last 30 minutes (live ASH, 1-s samples)

-- Read : aas_cpu vs NUM_CPUS; aas_cpu_queued > 0 = Resource Manager CPU throttling.

--        The current (partial) minute will read low.

SELECT inst_id,

       TO_CHAR(TRUNC(sample_time,'MI'),'HH24:MI')                                        AS minute,

       ROUND(COUNT(*)/60,1)                                                              AS aas,

       ROUND(SUM(CASE WHEN session_state = 'ON CPU'         THEN 1 ELSE 0 END)/60,1)     AS aas_cpu,

       ROUND(SUM(CASE WHEN wait_class   = 'User I/O'        THEN 1 ELSE 0 END)/60,1)     AS aas_user_io,

       ROUND(SUM(CASE WHEN wait_class   = 'Application'     THEN 1 ELSE 0 END)/60,1)     AS aas_app,

       ROUND(SUM(CASE WHEN wait_class   = 'Cluster'         THEN 1 ELSE 0 END)/60,1)     AS aas_cluster,

       ROUND(SUM(CASE WHEN event = 'resmgr:cpu quantum'     THEN 1 ELSE 0 END)/60,1)     AS aas_cpu_queued

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 30/1440

GROUP  BY inst_id, TRUNC(sample_time,'MI')

ORDER  BY TRUNC(sample_time,'MI'), inst_id;

 

 

================================================================================

 RT2 - TOP CONSUMERS IN THE LAST 15 MINUTES (LIVE ASH)

================================================================================

 

-- RT2.1  Top SQL (aas = average active sessions over the 15-min window)

SELECT sql_id, sql_plan_hash_value,

       COUNT(*)                                                                  AS samples,

       ROUND(COUNT(*)/900,2)                                                     AS aas,

       SUM(CASE WHEN session_state = 'ON CPU' THEN 1 ELSE 0 END)                 AS cpu_samples,

       SUM(CASE WHEN wait_class   = 'User I/O' THEN 1 ELSE 0 END)                AS io_samples,

       COUNT(DISTINCT inst_id || ':' || session_id || ',' || session_serial#)     AS sessions,

       MAX(module)                                                               AS sample_module,

       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100,1)                          AS pct

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 15/1440

AND    sql_id IS NOT NULL

GROUP  BY sql_id, sql_plan_hash_value

ORDER  BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT2.2  Top wait events (and CPU)

SELECT NVL(wait_class,'CPU') AS wait_class, NVL(event,'ON CPU') AS event,

       COUNT(*)                                          AS samples,

       ROUND(COUNT(*)/900,2)                             AS aas,

       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100,1)  AS pct

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 15/1440

GROUP  BY wait_class, event

ORDER  BY samples DESC

FETCH FIRST 25 ROWS ONLY;

 

 

-- RT2.3  Top modules / programs on CPU

SELECT module, action, program, machine,

       COUNT(*)                                          AS cpu_samples,

       ROUND(COUNT(*)/900,2)                             AS aas_cpu,

       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100,1)  AS pct_of_cpu

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 15/1440

AND    session_state = 'ON CPU'

GROUP  BY module, action, program, machine

ORDER  BY cpu_samples DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT2.4  Top objects for I/O / contention waits (waiting samples only)

SELECT o.owner, o.object_name, o.object_type, o.subobject_name, h.event,

       COUNT(*)                                         AS samples,

       COUNT(DISTINCT h.current_block#)                 AS distinct_blocks

FROM   gv$active_session_history h

JOIN   dba_objects o ON o.object_id = h.current_obj#

WHERE  h.sample_time > SYSDATE - 15/1440

AND    h.session_state = 'WAITING'

AND    h.wait_class IN ('User I/O','Cluster','Concurrency','Application')

AND    h.current_obj# > 0

GROUP  BY o.owner, o.object_name, o.object_type, o.subobject_name, h.event

ORDER  BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

 

-- RT2.5  Top PL/SQL entry points (which package is driving the load)

SELECT p.owner, p.object_name, p.procedure_name,

       COUNT(*)                                                    AS samples,

       SUM(CASE WHEN h.session_state = 'ON CPU' THEN 1 ELSE 0 END) AS cpu_samples,

       ROUND(COUNT(*)/900,2)                                       AS aas

FROM   gv$active_session_history h

JOIN   dba_procedures p

       ON  p.object_id     = h.plsql_entry_object_id

       AND p.subprogram_id = h.plsql_entry_subprogram_id

WHERE  h.sample_time > SYSDATE - 15/1440

GROUP  BY p.owner, p.object_name, p.procedure_name

ORDER  BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

 

================================================================================

 RT3 - EBS CONCURRENT PROCESSING (LIVE)

================================================================================

 

-- RT3.1  Running requests -> DB session, current activity, CPU so far

SELECT fcr.request_id,

       fcp.concurrent_program_name,

       fcpt.user_concurrent_program_name,

       fu.user_name,

       ROUND((SYSDATE - fcr.actual_start_date) * 1440,1)              AS run_min,

       s.inst_id, s.sid, s.serial#, s.sql_id,

       CASE WHEN s.state = 'WAITING' THEN s.event ELSE 'ON CPU' END   AS activity,

       s.final_blocking_instance, s.final_blocking_session,

       ROUND(st.value/100)                                            AS cpu_sec_so_far,

       fcr.argument_text

FROM   apps.fnd_concurrent_requests     fcr

JOIN   apps.fnd_concurrent_programs     fcp

       ON  fcp.concurrent_program_id = fcr.concurrent_program_id

       AND fcp.application_id        = fcr.program_application_id

JOIN   apps.fnd_concurrent_programs_tl  fcpt

       ON  fcpt.concurrent_program_id = fcp.concurrent_program_id

       AND fcpt.application_id        = fcp.application_id

       AND fcpt.language              = USERENV('LANG')

LEFT JOIN apps.fnd_user fu ON fu.user_id = fcr.requested_by

LEFT JOIN gv$session s

       ON  s.audsid = fcr.oracle_session_id

       AND fcr.oracle_session_id > 0

LEFT JOIN gv$sesstat st

       ON  st.inst_id = s.inst_id

       AND st.sid     = s.sid

       AND st.statistic# = (SELECT statistic# FROM v$statname

                            WHERE name = 'CPU used by this session')

WHERE  fcr.phase_code = 'R'

ORDER  BY run_min DESC;

-- Note: a running request with no session row may be a Java / host / OPP

--       program that runs in other sessions - use RT3.2 or match by MODULE.

 

 

-- RT3.2  Fallback mapping via OS process id (for one request)

SELECT fcr.request_id, s.inst_id, s.sid, s.serial#, p.spid, s.sql_id, s.module, s.action,

       CASE WHEN s.state = 'WAITING' THEN s.event ELSE 'ON CPU' END AS activity

FROM   apps.fnd_concurrent_requests fcr

JOIN   gv$process p ON p.spid = fcr.oracle_process_id

JOIN   gv$session s ON s.paddr = p.addr AND s.inst_id = p.inst_id

WHERE  fcr.request_id = :request_id;

 

 

-- RT3.3  Runnable pending requests and how long they have waited

-- Read : long waiting_min with idle DB = manager capacity / incompatibility,

--        not database performance.

SELECT fcr.request_id,

       fcpt.user_concurrent_program_name,

       DECODE(fcr.status_code,'I','Normal','Q','Standby','R','Normal',

                              'Z','Waiting','F','Scheduled',fcr.status_code) AS pending_status,

       fcr.hold_flag,

       fcr.requested_start_date,

       ROUND((SYSDATE - GREATEST(fcr.request_date, fcr.requested_start_date)) * 1440,1) AS waiting_min,

       fu.user_name

FROM   apps.fnd_concurrent_requests     fcr

JOIN   apps.fnd_concurrent_programs_tl  fcpt

       ON  fcpt.concurrent_program_id = fcr.concurrent_program_id

       AND fcpt.application_id        = fcr.program_application_id

       AND fcpt.language              = USERENV('LANG')

LEFT JOIN apps.fnd_user fu ON fu.user_id = fcr.requested_by

WHERE  fcr.phase_code = 'P'

AND    fcr.hold_flag  = 'N'

AND    fcr.requested_start_date <= SYSDATE

ORDER  BY waiting_min DESC;

 

 

-- RT3.4  Concurrent managers: target vs actual processes vs running requests

-- Read : actual_active < target = manager processes missing;

--        running_reqs = actual_active with long pending queue = saturated.

SELECT q.concurrent_queue_name,

       q.target_node,

       q.max_processes                                     AS target_processes,

       q.running_processes                                 AS fnd_running_processes,

       (SELECT COUNT(*)

        FROM   apps.fnd_concurrent_processes p

        WHERE  p.queue_application_id = q.application_id

        AND    p.concurrent_queue_id  = q.concurrent_queue_id

        AND    p.process_status_code  = 'A')               AS actual_active,

       (SELECT COUNT(*)

        FROM   apps.fnd_concurrent_requests r

        JOIN   apps.fnd_concurrent_processes p

               ON p.concurrent_process_id = r.controlling_manager

        WHERE  r.phase_code = 'R'

        AND    p.queue_application_id = q.application_id

        AND    p.concurrent_queue_id  = q.concurrent_queue_id) AS running_reqs

FROM   apps.fnd_concurrent_queues q

WHERE  q.enabled_flag = 'Y'

AND    q.max_processes > 0

ORDER  BY running_reqs DESC, q.concurrent_queue_name;

 

 

================================================================================

 RT4 - BLOCKING, LOCKS AND WAIT CHAINS

================================================================================

 

-- RT4.1  Waiters with their FINAL blocker (and the blocker's EBS request, if any)

-- Read : blocker INACTIVE with high last_call_et = uncommitted session (often

--        an idle Forms session). Fix the holder, not the waiter.

SELECT w.inst_id, w.sid, w.serial#, w.username, w.module AS waiter_module, w.sql_id,

       w.event, ROUND(w.wait_time_micro/1e6) AS wait_s,

       o.owner || '.' || o.object_name                                AS waited_object,

       w.final_blocking_instance AS blk_inst, w.final_blocking_session AS blk_sid,

       b.serial# AS blk_serial, b.username AS blk_user, b.module AS blk_module,

       b.action AS blk_action, b.machine AS blk_machine,

       b.status AS blk_status, b.last_call_et AS blk_last_call_et,

       b.sql_id AS blk_sql_id, b.prev_sql_id AS blk_prev_sql_id,

       CASE WHEN b.state = 'WAITING' THEN b.event ELSE 'ON CPU' END   AS blk_activity,

       r.request_id                                                   AS blk_request_id

FROM   gv$session w

LEFT JOIN gv$session b

       ON  b.inst_id = w.final_blocking_instance

       AND b.sid     = w.final_blocking_session

LEFT JOIN dba_objects o ON o.object_id = w.row_wait_obj#

LEFT JOIN apps.fnd_concurrent_requests r

       ON  r.oracle_session_id = b.audsid

       AND r.phase_code = 'R'

       AND b.audsid > 0

WHERE  w.blocking_session IS NOT NULL

ORDER  BY wait_s DESC;

 

 

-- RT4.2  Full wait chains (cluster-wide)

SELECT chain_id, chain_is_cycle, instance, sid, sess_serial#,

       blocker_instance, blocker_sid, blocker_sess_serial#,

       wait_event_text, in_wait_secs, num_waiters, row_wait_obj#

FROM   v$wait_chains

ORDER  BY chain_id, num_waiters DESC;

 

 

-- RT4.3  Lock holders and requesters (TM locks decoded to the object)

SELECT l.inst_id, l.sid, s.serial#, s.username, s.module, l.type, l.id1, l.id2,

       DECODE(l.lmode,0,'None',1,'Null',2,'RowS',3,'RowX',4,'Share',5,'SRowX',6,'Excl',l.lmode)   AS held,

       DECODE(l.request,0,'None',1,'Null',2,'RowS',3,'RowX',4,'Share',5,'SRowX',6,'Excl',l.request) AS requested,

       l.ctime AS secs_in_mode, l.block,

       CASE WHEN l.type = 'TM'

            THEN (SELECT owner || '.' || object_name FROM dba_objects WHERE object_id = l.id1)

       END AS tm_object

FROM   gv$lock l

JOIN   gv$session s ON s.inst_id = l.inst_id AND s.sid = l.sid

WHERE  l.request > 0 OR l.block > 0

ORDER  BY l.block DESC, l.ctime DESC;

 

 

-- RT4.4  Locked objects with the owning session

SELECT lo.inst_id, lo.session_id AS sid, s.serial#, lo.oracle_username, s.module, s.status,

       s.last_call_et, o.owner, o.object_name, o.object_type,

       DECODE(lo.locked_mode,0,'None',1,'Null',2,'RowS',3,'RowX',4,'Share',5,'SRowX',6,'Excl',lo.locked_mode) AS locked_mode

FROM   gv$locked_object lo

JOIN   dba_objects o ON o.object_id = lo.object_id

JOIN   gv$session  s ON s.inst_id = lo.inst_id AND s.sid = lo.session_id

ORDER  BY s.last_call_et DESC;

 

 

================================================================================

 RT5 - HOT BLOCKS / BUFFER CONTENTION

================================================================================

 

-- RT5.1  Sessions waiting on the same object/block (uses ROW_WAIT_*)

SELECT s.inst_id, s.event, o.owner, o.object_name, o.object_type,

       s.row_wait_file#, s.row_wait_block#, COUNT(*) AS sessions

FROM   gv$session s

JOIN   dba_objects o ON o.object_id = s.row_wait_obj#

WHERE  s.state = 'WAITING'

AND    s.event IN ('buffer busy waits','read by other session',

                   'gc buffer busy acquire','gc buffer busy release',

                   'gc cr block busy','gc current block busy',

                   'enq: TX - row lock contention','enq: TX - index contention')

GROUP  BY s.inst_id, s.event, o.owner, o.object_name, o.object_type,

          s.row_wait_file#, s.row_wait_block#

ORDER  BY sessions DESC;

 

 

-- RT5.2  Most contended cache-buffers-chains latch children (cumulative since startup)

-- Note : mapping a latch child to blocks requires X$BH (SYS only). Use RT2.4 /

--        RT2.1 to find the SQL and object instead.

SELECT inst_id, addr, child#, gets, misses, sleeps

FROM   gv$latch_children

WHERE  name = 'cache buffers chains'

ORDER  BY sleeps DESC

FETCH FIRST 10 ROWS ONLY;


Friday, September 25, 2026

Performance Issue RCA — Deep Dive Query Workbook

 

ORACLE EBS 12.2  |  DATABASE 19c

Performance Issue RCA — Deep Dive Query Workbook

Production troubleshooting pack for slow concurrent requests, SQL regressions, blocking, waits, statistics, and space. Queries are generic (no SID, host, or client names). Prefer GV$ on RAC.

Classification: Internal DBA use  |  Diagnostics Pack required for DBA_HIST_* and historical ASH  |  26-Sep-2026

1. Purpose and rules of engagement

Use this workbook when a production incident needs evidence: which session, which SQL_ID, which wait class, whether the plan changed, and whether EBS concurrent processing is the entry point.

Always

•       Start live (GV$ / V$ / FND) before AWR. History cannot explain a session that is blocked right now.

•       Record request_id, audsid, inst_id, sid, serial#, sql_id, plan_hash_value, wait event, and the exact time window.

•       Treat elapsed time as a product of executions × time-per-exec × data volume. A higher total does not prove a regression.

•       On RAC query GV$ so you do not miss the instance that owns the session.

•       Do not change SQL, stats, or parameters until the wait class and plan_hash are documented.

Note: Most DBA_HIST_% views require the Oracle Diagnostics Pack. Confirm licensing before running historical ASH/AWR in production.

2. Incident decision tree

Symptom

First path

Concurrent running longer than normal

FND_CONCURRENT_REQUESTS → GV$SESSION (audsid) → GV$SQL → LONGOPS / SQL Monitor → ASH

SQL fast yesterday, slow today

DBA_HIST_SNAPSHOT → DBA_HIST_SQLSTAT → DBA_HIST_SQL_PLAN → DBA_HIST_ACTIVE_SESS_HISTORY

Sessions blocked or hung

GV$SESSION → V$WAIT_CHAINS → GV$LOCK → GV$LOCKED_OBJECT

High CPU or DB time

GV$ACTIVE_SESSION_HISTORY → DBA_HIST_SYS_TIME_MODEL → top SQL by elapsed

Suspected stale statistics

DBA_TAB_STATISTICS → DBA_TAB_MODIFICATIONS → DBA_OPTSTAT_OPERATIONS

Large GL archive / purge

FND request → SQL Monitor → GV$TRANSACTION → temp/undo → DBA_SEGMENTS

Temp or tablespace pressure

GV$TEMPSEG_USAGE → DBA_FREE_SPACE → DBA_HIST_TBSPC_SPACE_USAGE

3. Views used in production RCA

3.1 Historical AWR and ASH

View

Why it is used

DBA_HIST_SNAPSHOT

Snap IDs and interval times for the incident window

DBA_HIST_ACTIVE_SESS_HISTORY

SQL_ID, event, blocking, object# over time

DBA_HIST_SQLSTAT

Elapsed, CPU, gets, reads, rows, executions, plan_hash

DBA_HIST_SQLTEXT

Full SQL text from AWR

DBA_HIST_SQL_PLAN

Historical plans / access path change

DBA_HIST_SQLBIND

Captured binds for the window

DBA_HIST_SYSTEM_EVENT

Wait event deltas by snapshot

DBA_HIST_SYS_TIME_MODEL

DB time vs DB CPU vs parse time

DBA_HIST_SEG_STAT

Hot segment I/O and block contention

DBA_HIST_TBSPC_SPACE_USAGE

Tablespace growth during the incident

DBA_HIST_RESOURCE_LIMIT

Sessions / processes vs limit

DBA_HIST_DATABASE_INSTANCE

Instance startup and role context

3.2 Real-time SQL and sessions

View

Why it is used

GV$SESSION

Live sid, sql_id, event, blocking, module, audsid

GV$ACTIVE_SESSION_HISTORY

Recent samples (seconds to ~1 hour)

GV$SQL / GV$SQLAREA

Cursor-cache elapsed, gets, text, children

GV$SQL_PLAN / DISPLAY_CURSOR

Current plan + actuals if gathered

GV$SQL_MONITOR

Monitored long SQL (batch / PX)

GV$SQL_BIND_CAPTURE

Binds for the current child

GV$SESSION_LONGOPS

Instrumented long operations only

GV$PROCESS

OS PID for OS-level evidence

GV$TRANSACTION

Undo used by open transactions

GV$LOCK / GV$LOCKED_OBJECT

Enqueue and object holders

GV$TEMPSEG_USAGE

Who is consuming TEMP

GV$SESSTAT / GV$SYSSTAT

Parse, I/O, CPU counters

3.3 Locks, waiters, plans extras

View

Why it is used

V$SESSION_BLOCKERS

Direct blocker pairs

V$WAIT_CHAINS

Full wait chain (best on RAC)

V$SESSION_EVENT / V$SYSTEM_EVENT

Wait totals session / instance

DBA_BLOCKERS / DBA_WAITERS

Simple lock wait list

V$SQL_SHARED_CURSOR

Why a SQL_ID has many children

V$SQL_OPTIMIZER_ENV

Optimizer parameters of a child

V$SQL_WORKAREA_ACTIVE

PGA / TEMP spill in flight

3.4 Optimizer statistics and storage

View

Why it is used

DBA_TAB_STATISTICS

Stale / missing table stats

DBA_TAB_MODIFICATIONS

DML since last gather

DBA_IND_STATISTICS / DBA_INDEXES

Index stats and UNUSABLE

DBA_SEGMENTS / DBA_EXTENTS

Size and file#/block# map

DBA_TABLESPACES / DBA_DATA_FILES / DBA_FREE_SPACE

Space headroom

DBA_OPTSTAT_OPERATIONS

Last stats jobs

3.5 EBS concurrent processing (APPS)

Table / view

Why it is used

FND_CONCURRENT_REQUESTS

Request status, times, audsid, arguments

FND_CONCURRENT_PROGRAMS(_TL)

Program name and enabled flag

FND_EXECUTABLES

Executable / method

FND_CONCURRENT_QUEUES

Manager capacity vs running

FND_CONCURRENT_PROCESSES

Live manager OS/DB process

FND_CONCURRENT_PROGRAM_SERIAL

Incompatibility holding a request

4. Session setup (run once)

ALTER SESSION SET nls_date_format = 'YYYY-MM-DD HH24:MI:SS';

ALTER SESSION SET nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF';

 

-- Substitution variables used below

-- DEFINE SQL_ID     = 'xxxxxxxxxxxxxxxx'

-- DEFINE REQUEST_ID = 0

-- DEFINE BID        = 0

-- DEFINE EID        = 0

-- DEFINE SID        = 0

-- DEFINE INST_ID    = 1

-- DEFINE OWNER      = 'GL'

-- DEFINE TABLE_NAME = 'GL_JE_LINES'

 

Instance and AWR snap window

SELECT inst_id, instance_name, host_name, status, database_status,

       startup_time, version

FROM   gv$instance

ORDER BY inst_id;

 

SELECT name, dbid, created, log_mode, open_mode, database_role

FROM   v$database;

 

SELECT snap_id, instance_number, begin_interval_time, end_interval_time

FROM   dba_hist_snapshot

WHERE  begin_interval_time > SYSDATE - 2

ORDER BY snap_id, instance_number;

 

5. Scenario A — Concurrent request running long

Path: FND → session → SQL → longops / monitor → ASH. Run the FND queries as APPS.

A1. Request header

SELECT request_id, parent_request_id,

       phase_code, status_code, priority,

       requested_by, request_date, requested_start_date,

       actual_start_date, actual_completion_date,

       ROUND((NVL(actual_completion_date, SYSDATE) - actual_start_date) * 1440, 2) run_min,

       oracle_session_id, oracle_process_id,

       logfile_name, outfile_name, argument_text

FROM   fnd_concurrent_requests

WHERE  request_id = &REQUEST_ID;

 

A2. All running / pending requests

SELECT request_id, parent_request_id, phase_code, status_code,

       concurrent_program_id, requested_by,

       request_date, requested_start_date, actual_start_date,

       ROUND((SYSDATE - actual_start_date) * 1440, 1) run_min,

       oracle_session_id, argument_text

FROM   fnd_concurrent_requests

WHERE  phase_code IN ('R', 'P')

ORDER BY DECODE(phase_code, 'R', 1, 2), actual_start_date NULLS LAST;

 

A3. Map request AUDSID to GV$SESSION

SELECT r.request_id, r.phase_code, r.status_code,

       r.oracle_session_id,

       s.inst_id, s.sid, s.serial#,

       s.event, s.wait_class, s.seconds_in_wait,

       s.sql_id, s.sql_child_number, s.sql_exec_start,

       s.module, s.action,

       s.blocking_instance, s.blocking_session

FROM   fnd_concurrent_requests r

JOIN   gv$session s ON s.audsid = r.oracle_session_id

WHERE  r.request_id = &REQUEST_ID;

 

Note: Elapsed minutes from SQL_EXEC_START is the current cursor age, not necessarily the full request duration. Parent requests can sit on a different session than child workers.

A4. Program definition

SELECT p.concurrent_program_id, p.concurrent_program_name,

       t.user_concurrent_program_name,

       p.enabled_flag, p.execution_method_code,

       e.execution_file_name

FROM   fnd_concurrent_programs p

JOIN   fnd_concurrent_programs_tl t

       ON t.application_id = p.application_id

      AND t.concurrent_program_id = p.concurrent_program_id

      AND t.language = USERENV('LANG')

LEFT JOIN fnd_executables e

       ON e.application_id = p.executable_application_id

      AND e.executable_id  = p.executable_id

WHERE  p.concurrent_program_id = &CONC_PROG_ID;

 

A5. Manager capacity and incompatibilities

SELECT concurrent_queue_name, running_processes, max_processes,

       cache_size, target_node, enabled_flag

FROM   fnd_concurrent_queues

WHERE  enabled_flag = 'Y'

ORDER BY running_processes DESC;

 

SELECT *

FROM   fnd_concurrent_program_serial

WHERE  running_concurrent_program_id = &CONC_PROG_ID

    OR to_run_concurrent_program_id  = &CONC_PROG_ID;

 

6. Scenario B — Live session and current SQL

B1. Active user sessions

SELECT s.inst_id, s.sid, s.serial#, s.audsid, s.username,

       s.osuser, s.machine, s.program, s.module, s.action, s.status,

       s.sql_id, s.sql_child_number, s.sql_exec_start,

       ROUND((SYSDATE - s.sql_exec_start) * 1440, 2) sql_elapsed_min,

       s.event, s.wait_class, s.seconds_in_wait, s.state,

       s.blocking_instance, s.blocking_session,

       s.last_call_et

FROM   gv$session s

WHERE  s.type = 'USER'

AND    s.status = 'ACTIVE'

ORDER BY s.sql_exec_start NULLS LAST;

 

B2. Wait-class mix now

SELECT inst_id,

       COUNT(*) sessions,

       SUM(DECODE(status, 'ACTIVE', 1, 0)) active,

       SUM(DECODE(wait_class, 'Idle', 0, 1)) nonidle,

       SUM(DECODE(wait_class, 'User I/O', 1, 0)) user_io,

       SUM(DECODE(wait_class, 'Concurrency', 1, 0)) concurrency,

       SUM(DECODE(wait_class, 'Application', 1, 0)) application,

       SUM(DECODE(wait_class, 'Commit', 1, 0)) commit_w

FROM   gv$session

WHERE  type = 'USER'

GROUP BY inst_id

ORDER BY inst_id;

 

B3. Cursor-cache stats and text

SELECT inst_id, sql_id, child_number, plan_hash_value, executions,

       ROUND(elapsed_time / 1e6, 2) ela_s,

       ROUND(cpu_time / 1e6, 2) cpu_s,

       buffer_gets, disk_reads, rows_processed,

       ROUND(elapsed_time / NULLIF(executions, 0) / 1e6, 4) ela_per_exec_s,

       ROUND(buffer_gets / NULLIF(executions, 0)) gets_per_exec,

       last_active_time

FROM   gv$sql

WHERE  sql_id = '&SQL_ID';

 

SELECT * FROM TABLE(

  DBMS_XPLAN.DISPLAY_CURSOR('&SQL_ID', NULL,

    'ALLSTATS LAST +PEEKED_BINDS +OUTLINE +ADAPTIVE'));

 

B4. Longops and SQL Monitor

SELECT inst_id, sid, serial#, opname, target, sofar, totalwork, units,

       ROUND(sofar / NULLIF(totalwork, 0) * 100, 2) pct,

       elapsed_seconds, time_remaining, sql_id

FROM   gv$session_longops

WHERE  totalwork > 0

AND    sofar < totalwork

ORDER BY elapsed_seconds DESC;

 

SELECT inst_id, sid, session_serial#, sql_id, sql_exec_id, sql_exec_start,

       status, username, module, px_servers_allocated,

       ROUND(elapsed_time / 1e6, 2) ela_s,

       ROUND(cpu_time / 1e6, 2) cpu_s,

       buffer_gets, disk_reads

FROM   gv$sql_monitor

WHERE  status IN ('EXECUTING', 'DONE (ERROR)')

    OR sql_exec_start > SYSDATE - 1/24

ORDER BY sql_exec_start DESC

FETCH FIRST 30 ROWS ONLY;

 

Note: A missing LONGOPS row does not mean the request is idle. Only instrumented operations appear there. Use SQL Monitor and ASH when LONGOPS is empty.

B5. Binds and OS PID

SELECT inst_id, sql_id, child_number, name, position, datatype_string,

       last_captured, value_string

FROM   gv$sql_bind_capture

WHERE  sql_id = '&SQL_ID'

ORDER BY child_number, position;

 

SELECT s.inst_id, s.sid, s.serial#, p.spid os_pid, p.pid,

       s.username, s.sql_id, s.program

FROM   gv$session s

JOIN   gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id

WHERE  s.sid = &SID

AND    s.inst_id = &INST_ID;

 

7. Scenario C — Blocking, locks, wait chains

If blocking_session is populated, that is the RCA until the holder is released or explained. Do not tune the waiter first.

C1. Who blocks whom

SELECT s.inst_id, s.sid, s.serial#, s.username, s.program, s.module,

       s.sql_id, s.event, s.seconds_in_wait,

       s.blocking_instance, s.blocking_session,

       bs.username blk_user, bs.program blk_prog, bs.sql_id blk_sql,

       bs.event blk_event, bs.status blk_status

FROM   gv$session s

JOIN   gv$session bs

       ON bs.inst_id = s.blocking_instance

      AND bs.sid     = s.blocking_session

WHERE  s.blocking_session IS NOT NULL

ORDER BY s.seconds_in_wait DESC;

 

C2. Wait chains, locks, locked objects

SELECT * FROM v$wait_chains

ORDER BY num_waiters DESC, chain_signature;

 

SELECT inst_id, sid, type, id1, id2, lmode, request, ctime, block

FROM   gv$lock

WHERE  request > 0 OR block > 0

ORDER BY block DESC, ctime DESC;

 

SELECT lo.inst_id, lo.session_id sid, lo.oracle_username,

       o.owner, o.object_name, o.object_type, lo.locked_mode

FROM   gv$locked_object lo

JOIN   dba_objects o ON o.object_id = lo.object_id

ORDER BY lo.inst_id, lo.session_id;

 

8. Scenario D — Live ASH (last hour)

SELECT inst_id, wait_class, event, session_state, COUNT(*) samples

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 1/24

AND    session_type = 'FOREGROUND'

GROUP BY inst_id, wait_class, event, session_state

ORDER BY samples DESC

FETCH FIRST 40 ROWS ONLY;

 

SELECT sql_id, COUNT(*) samples,

       COUNT(DISTINCT session_id || ',' || session_serial#) sess,

       ROUND(100 * RATIO_TO_REPORT(COUNT(*)) OVER (), 1) pct

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 1/24

AND    sql_id IS NOT NULL

GROUP BY sql_id

ORDER BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

SELECT current_obj#, COUNT(*) samples

FROM   gv$active_session_history

WHERE  sample_time > SYSDATE - 1/24

AND    current_obj# > 0

GROUP BY current_obj#

ORDER BY samples DESC

FETCH FIRST 20 ROWS ONLY;

 

SELECT owner, object_name, object_type, subobject_name

FROM   dba_objects

WHERE  object_id = &OBJ_ID;

 

Hot block map

SELECT inst_id, event, p1 file#, p2 block#, COUNT(*) cnt

FROM   gv$session

WHERE  event IN ('latch: cache buffers chains', 'read by other session',

                 'buffer busy waits', 'gc cr block busy', 'gc current block busy')

GROUP BY inst_id, event, p1, p2

ORDER BY cnt DESC

FETCH FIRST 20 ROWS ONLY;

 

SELECT owner, segment_name, segment_type, partition_name, tablespace_name

FROM   dba_extents

WHERE  file_id = &FILE_ID

AND    &BLOCK_ID BETWEEN block_id AND block_id + blocks - 1;

 

9. Scenario E — Historical SQL (fast yesterday, slow today)

Set BID/EID from DBA_HIST_SNAPSHOT. Compare plan_hash_value, elapsed per execution, buffer gets per execution, and rows processed — not only total elapsed.

E1. Time model

SELECT sn.begin_interval_time, tm.instance_number, tm.stat_name,

       ROUND(tm.value / 1e6, 2) seconds

FROM   dba_hist_sys_time_model tm

JOIN   dba_hist_snapshot sn

       ON sn.snap_id = tm.snap_id

      AND sn.dbid = tm.dbid

      AND sn.instance_number = tm.instance_number

WHERE  tm.stat_name IN ('DB time', 'DB CPU', 'sql execute elapsed time',

                        'parse time elapsed', 'hard parse elapsed time')

AND    sn.begin_interval_time > SYSDATE - 2

ORDER BY sn.begin_interval_time, tm.instance_number, tm.stat_name;

 

E2. SQL_ID across snapshots

SELECT sn.begin_interval_time, st.instance_number, st.sql_id,

       st.plan_hash_value, st.executions_delta execs,

       ROUND(st.elapsed_time_delta / 1e6, 2) elapsed_s,

       ROUND(st.cpu_time_delta / 1e6, 2) cpu_s,

       st.buffer_gets_delta gets, st.disk_reads_delta reads,

       st.rows_processed_delta rows_proc,

       ROUND(st.elapsed_time_delta / NULLIF(st.executions_delta, 0) / 1e6, 4) ela_px_s,

       ROUND(st.buffer_gets_delta / NULLIF(st.executions_delta, 0)) gets_px

FROM   dba_hist_sqlstat st

JOIN   dba_hist_snapshot sn

       ON st.snap_id = sn.snap_id

      AND st.dbid = sn.dbid

      AND st.instance_number = sn.instance_number

WHERE  st.sql_id = '&SQL_ID'

ORDER BY sn.begin_interval_time DESC, st.instance_number;

 

E3. Plan-hash regression

SELECT sql_id, plan_hash_value,

       SUM(executions_delta) execs,

       ROUND(SUM(elapsed_time_delta) / 1e6, 2) ela_s,

       ROUND(SUM(elapsed_time_delta) / NULLIF(SUM(executions_delta), 0) / 1e6, 4) ela_px

FROM   dba_hist_sqlstat

WHERE  sql_id = '&SQL_ID'

GROUP BY sql_id, plan_hash_value

ORDER BY ela_s DESC;

 

SELECT sql_id, dbid, sql_text

FROM   dba_hist_sqltext

WHERE  sql_id = '&SQL_ID';

 

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&SQL_ID'));

 

E4. Historical ASH for one SQL_ID

SELECT event, wait_class, session_state, COUNT(*) sample_count

FROM   dba_hist_active_sess_history

WHERE  sql_id = '&SQL_ID'

AND    sample_time >= SYSDATE - 1

GROUP BY event, wait_class, session_state

ORDER BY sample_count DESC;

 

Note: ASH sample counts are not exact elapsed seconds. They show the distribution of time. Statements that never entered AWR will not appear in DBA_HIST_SQLSTAT.

E5. Top SQL in a snap window

SELECT sql_id,

       ROUND(SUM(elapsed_time_delta) / 1e6, 2) elapsed_s,

       ROUND(SUM(cpu_time_delta) / 1e6, 2) cpu_s,

       SUM(executions_delta) execs,

       ROUND(SUM(elapsed_time_delta) / NULLIF(SUM(executions_delta), 0) / 1e6, 4) ela_px,

       SUM(buffer_gets_delta) gets,

       SUM(disk_reads_delta) reads,

       SUM(rows_processed_delta) rows_proc

FROM   dba_hist_sqlstat

WHERE  snap_id > &BID AND snap_id <= &EID

GROUP BY sql_id

ORDER BY elapsed_s DESC

FETCH FIRST 20 ROWS ONLY;

 

10. Scenario F — Temp, undo, PGA, space

SELECT inst_id, tablespace, segtype,

       ROUND(SUM(blocks) * 8 / 1024) mb_approx_8k,

       COUNT(DISTINCT session_addr) sess

FROM   gv$tempseg_usage

GROUP BY inst_id, tablespace, segtype;

 

SELECT s.inst_id, s.sid, s.username, s.sql_id, t.tablespace, t.segtype,

       ROUND(t.blocks * 8 / 1024) mb_approx_8k

FROM   gv$tempseg_usage t

JOIN   gv$session s ON s.saddr = t.session_addr AND s.inst_id = t.inst_id

ORDER BY t.blocks DESC

FETCH FIRST 20 ROWS ONLY;

 

SELECT tablespace_name, status, ROUND(SUM(bytes) / 1024 / 1024) mb

FROM   dba_undo_extents

GROUP BY tablespace_name, status;

 

SELECT inst_id, ses_addr, start_time, used_ublk, used_urec, status

FROM   gv$transaction

ORDER BY used_ublk DESC;

 

SELECT name, ROUND(value / 1024 / 1024) mb

FROM   v$pgastat

WHERE  name IN ('aggregate PGA target parameter', 'total PGA allocated',

                'total PGA used for auto workareas', 'over allocation count');

 

SELECT tablespace_name, ROUND(SUM(bytes) / 1024 / 1024) free_mb

FROM   dba_free_space

GROUP BY tablespace_name

ORDER BY free_mb;

 

Note: MB math above assumes 8 KB blocks. Confirm DB_BLOCK_SIZE before quoting sizes in the RCA.

11. Scenario G — Cursor sharing and statistics

SELECT sql_id, COUNT(*) children, SUM(executions) execs,

       SUBSTR(MAX(sql_text), 1, 160) txt

FROM   gv$sql

GROUP BY sql_id

HAVING COUNT(*) > 20

ORDER BY children DESC

FETCH FIRST 20 ROWS ONLY;

 

SELECT * FROM v$sql_shared_cursor

WHERE  sql_id = '&SQL_ID'

AND    ROWNUM <= 10;

 

SELECT owner, table_name, num_rows, blocks, last_analyzed, stale_stats

FROM   dba_tab_statistics

WHERE  owner = UPPER('&OWNER')

AND    table_name = UPPER('&TABLE_NAME');

 

SELECT table_owner, table_name, inserts, updates, deletes, timestamp, truncated

FROM   dba_tab_modifications

WHERE  table_owner = UPPER('&OWNER')

AND    table_name = UPPER('&TABLE_NAME');

 

SELECT owner, index_name, table_name, uniqueness, status,

       clustering_factor, num_rows, last_analyzed, stale_stats

FROM   dba_ind_statistics

WHERE  owner = UPPER('&OWNER')

AND    table_name = UPPER('&TABLE_NAME');

 

12. Scenario H — RAC interconnect

SELECT inst_id, event, total_waits, time_waited

FROM   gv$system_event

WHERE  event LIKE 'gc %'

ORDER BY time_waited DESC;

 

SELECT inst_id, name, value

FROM   gv$sysstat

WHERE  name LIKE 'gc %'

ORDER BY inst_id, name;

 

13. Wait class to next check

Wait / class

Next check

db file sequential / scattered read

Hot segment, missing index, stale stats, multiblock I/O

log file sync / parallel write

Commit rate, log dest latency, LGWR

enq: TX / enq: TM

Blocker SQL, unindexed FK, ITL, uncommitted form

library cache / cursor: pin S wait on X

Hard parse, invalidations, literal SQL

cache buffers chains / read by other session

Hot block → DBA_EXTENTS

direct path read/write temp

PGA, HASH/SORT spill, workarea

undo / TX row lock

Long transaction, undo sizing, purge job

gc cr / gc current (RAC)

Interconnect, hot block, sequence

14. Evidence to attach to the ticket

•       Request_id, program name, phase/status, actual_start, argument_text

•       inst_id, sid, serial#, audsid, OS PID

•       sql_id, child_number, plan_hash_value (good window vs bad window)

•       Dominant wait class and top event from ASH

•       Elapsed per exec, gets per exec, rows processed (not only total elapsed)

•       Blocker sid and locked object if Application / Concurrency waits

•       TEMP / undo MB and whether workareas spilled

•       Stale_stats and last gather time on objects in the plan

Root-cause sentence format: Symptom → wait class → sql_id / object → change (plan, volume, blocker, space) → action.

15. Licensing and safety

•       DBA_HIST_* and DBA_HIST_ACTIVE_SESS_HISTORY: Diagnostics Pack.

•       SQL Monitor / DBMS_SQLTUNE.REPORT_SQL_MONITOR: Tuning Pack in many estates — confirm before use.

•       These queries are read-only. Do not embed ALTER SYSTEM, gather stats, or kill session in the same worksheet as the RCA capture.

•       Kill / disconnect only after blocker identity, module, and business owner are documented.