Tuesday, August 5, 2025

Title: How to Find the Tables Behind a SQL_ID in Oracle

 Title: How to Find the Tables Behind a SQL_ID in Oracle

As a database administrator or developer, you've likely encountered a situation where you need to troubleshoot a slow-running query. You have the SQL_ID from a performance report or a monitoring tool, but the SQL text itself is long, complex, or a mystery. The first step to understanding the query's behavior is to figure out which tables it's actually accessing.

Fortunately, in an Oracle database, this is a straightforward process. The execution plan—the set of steps the database uses to execute a SQL statement—holds all the answers. By examining the plan, we can quickly identify the tables, views, and indexes involved.

Let's dive into the two primary methods for doing this.

Method 1: Finding Tables for Active or Recent Queries

If the SQL statement is currently running or was executed very recently, its execution plan will still be in the shared pool. We can access this information using the V$SQL_PLAN view.

The V$ views are dynamic performance views, which means they show the current state of the database. V$SQL_PLAN specifically stores the execution plans for all SQL_IDs in the shared pool.

Here's the query you'll use:

SQL
SELECT
    t.object_owner,
    t.object_name,
    t.operation,
    t.options,
    s.sql_text
FROM
    v$sql_plan t,
    v$sql s
WHERE
    t.sql_id = s.sql_id
    AND t.sql_id = '&sql_id'
ORDER BY
    t.id;

How it works:

  • We join V$SQL_PLAN (t) with V$SQL (s) on sql_id.

  • V$SQL_PLAN gives us the execution plan details, including OBJECT_OWNER and OBJECT_NAME for each step.

  • V$SQL gives us the actual SQL_TEXT for context.

  • We filter the results by providing our specific SQL_ID.

  • The ORDER BY t.id ensures the plan steps are displayed in the correct sequence.

When you run this query, you'll be prompted to enter the sql_id. The output will be a list of operations, and any line with an OPERATION like 'TABLE ACCESS', 'INDEX FULL SCAN', or 'INDEX RANGE SCAN' will show you the corresponding table or index name under OBJECT_NAME.

Pro-Tip: If the query is on a view, the execution plan will typically show the underlying base tables, giving you a complete picture.

Method 2: Finding Tables for Historical Queries

What if the query was executed days or even weeks ago? The SQL_ID is no longer in the shared pool, so V$SQL_PLAN won't help. This is where the Automatic Workload Repository (AWR) comes in.

Oracle's AWR automatically collects, processes, and maintains performance statistics, including execution plans for popular or resource-intensive queries. This data is stored in historical views, which are prefixed with DBA_HIST_.

The view we're interested in is DBA_HIST_SQL_PLAN.

Here's the query to find historical tables for a SQL_ID:

SQL
SELECT
    t.object_owner,
    t.object_name,
    t.operation,
    t.options
FROM
    dba_hist_sql_plan t
WHERE
    t.sql_id = '&sql_id'
ORDER BY
    t.plan_hash_value,
    t.id;

How it works:

  • This query is very similar to the previous one, but we're now querying DBA_HIST_SQL_PLAN.

  • The plan_hash_value is included in the ORDER BY clause because a single SQL_ID can have multiple execution plans over time. This helps to group the steps for each plan together.

Permissions are Key! To run these queries, you'll need the necessary permissions. Typically, users with roles like SELECT_CATALOG_ROLE or DBA will have access to these views. If you encounter an "insufficient privileges" error, you'll need to contact your DBA to grant you the required permissions.

By using these simple but powerful queries, you can quickly demystify any SQL_ID and get a clear understanding of the tables and objects it's interacting with, putting you on the right path to performance tuning.

Happy troubleshooting!

Monday, August 4, 2025

Mastering Oracle EBS Concurrent Requests: Your Ultimate SQL Query Toolkit

 

Mastering Oracle EBS Concurrent Requests: Your Ultimate SQL Query Toolkit

As an Oracle E-Business Suite administrator or developer, managing concurrent requests is a fundamental part of your daily routine. From troubleshooting long-running programs to auditing user activity, having a powerful set of SQL queries can save you a significant amount of time and effort.

This post provides a comprehensive toolkit of essential SQL queries to help you monitor, troubleshoot, and analyze concurrent requests and other critical system data in your Oracle EBS environment.

1. Finding Concurrent Request Information

The queries below are your go-to tools for getting basic information about concurrent requests. You can use these to find out which manager ran a specific request, what program is associated with a request, or to see a summary of a request's status.

How to Determine Which Manager Ran a Specific Concurrent Request

This query helps you pinpoint the concurrent manager responsible for a particular request, which is useful for troubleshooting queue-related issues.

SQL
col USER_CONCURRENT_QUEUE_NAME for a100
select b.USER_CONCURRENT_QUEUE_NAME
from fnd_concurrent_processes a,
     fnd_concurrent_queues_vl b,
     fnd_concurrent_requests c
where a.CONCURRENT_QUEUE_ID = b.CONCURRENT_QUEUE_ID
and a.CONCURRENT_PROCESS_ID = c.controlling_manager
and c.request_id =&conc_reqid’;

To Find Concurrent Program Name, Phase, and Status for a Given Request ID

This is a quick way to get the essential details of a completed or running request, including the program name and its current phase and status.

SQL
SELECT request_id,
       user_concurrent_program_name,
       DECODE(phase_code, 'C', 'Completed', phase_code) phase_code,
       DECODE(status_code, 'D', 'Cancelled',
              'E', 'Error',
              'G', 'Warning',
              'H', 'On Hold',
              'T', 'Terminating',
              'M', 'No Manager',
              'X', 'Terminated',
              'C', 'Normal', status_code) status_code,
       to_char(actual_start_date, 'dd-mon-yy:hh24:mi:ss') Start_Date,
       to_char(actual_completion_date, 'dd-mon-yy:hh24:mi:ss'),
       completion_text
FROM apps.fnd_conc_req_summary_v
WHERE request_id = '&req_id'
ORDER BY 6 DESC;

To Find Child Requests

If a program spawns other requests (a parent-child relationship), this query helps you find all the requests submitted by a parent.

SQL
set lines 200
col USER_CONCURRENT_PROGRAM_NAME for a40
col PHASE_CODE for a10
col STATUS_CODE for a10
col COMPLETION_TEXT for a20
SELECT sum.request_id,
       req.PARENT_REQUEST_ID,
       sum.user_concurrent_program_name,
       DECODE (sum.phase_code, 'C', 'Completed', sum.phase_code) phase_code,
       DECODE(sum.status_code, 'D', 'Cancelled' ,
              'E', 'Error' ,
              'G', 'Warning',
              'H', 'On Hold' ,
              'T', 'Terminating',
              'M', 'No Manager' ,
              'X', 'Terminated',
              'C', 'Normal', sum.status_code) status_code,
       sum.actual_start_date,
       sum.actual_completion_date,
       sum.completion_text
FROM apps.fnd_conc_req_summary_v sum,
     apps.fnd_concurrent_requests req
where req.request_id=sum.request_id
and req.PARENT_REQUEST_ID = '&parent_concurrent_request_id';

2. Troubleshooting Long-Running and Errored Requests

These queries are essential for performance tuning and troubleshooting. They help you identify sessions that are taking too long, find the underlying SQL, and pinpoint requests that have failed.

Long-Running Concurrent Requests

This is a powerful query that provides a comprehensive overview of currently running requests, including session information (SPID), and their runtime duration.

SQL
SELECT a.request_id,
       a.oracle_process_id "SPID",
       frt.responsibility_name,
       c.concurrent_program_name || ': ' || ctl.user_concurrent_program_name,
       a.description,
       a.ARGUMENT_TEXT,
       b.node_name,
       b.db_instance,
       a.logfile_name,
       a.logfile_node_name,
       a.outfile_name,
       q.concurrent_queue_name,
       a.phase_code,
       a.status_code,
       a.completion_text,
       actual_start_date,
       actual_completion_date,
       fu.user_name,
       (nvl(actual_completion_date,sysdate)-actual_start_date)*1440 mins,
       (SELECT avg(nvl(a2.actual_completion_date-a2.actual_start_date,0))*1440 avg_run_time
        FROM APPLSYS.fnd_Concurrent_requests a2,
             APPLSYS.fnd_concurrent_programs c2
        WHERE c2.concurrent_program_id = c.concurrent_program_id
        AND a2.concurrent_program_id = c2.concurrent_program_id
        AND a2.program_application_id = c2.application_id
        AND a2.phase_code || '' = 'C') avg_mins,
       round((actual_completion_date - requested_start_date),2) * 24 duration_in_hours
FROM APPLSYS.fnd_Concurrent_requests a,
     APPLSYS.fnd_concurrent_processes b,
     applsys.fnd_concurrent_queues q,
     APPLSYS.fnd_concurrent_programs c,
     APPLSYS.fnd_concurrent_programs_tl ctl,
     apps.fnd_user fu,
     apps.FND_RESPONSIBILITY_TL frt
WHERE a.controlling_manager = b.concurrent_process_id
AND a.concurrent_program_id = c.concurrent_program_id
AND a.program_application_id = c.application_id
AND a.phase_code = 'R'
AND a.status_code = 'R'
AND b.queue_application_id = q.application_id
AND b.concurrent_queue_id = q.concurrent_queue_id
AND ctl.concurrent_program_id = c.concurrent_program_id
AND a.requested_by = fu.user_id
AND a.responsibility_id = frt.responsibility_id
ORDER BY a.actual_start_date DESC;

To Find the SQL Query for a Given Concurrent Request SID

Once you have the Session ID (SID) from the previous query, you can use this to find the specific SQL statement the request is currently executing.

SQL
select sid,sql_text
from gv$session ses, gv$sqlarea sql
where ses.sql_hash_value = sql.hash_value(+)
and ses.sql_address = sql.address(+)
and ses.sid='&oracle_sid'
/

Oracle Concurrent Request Error Script

This query quickly lists all concurrent requests that have errored out in the last two days, helping you identify and resolve issues.

SQL
SELECT a.request_id "Req Id",
       a.phase_code,
       a.status_code,
       actual_start_date,
       actual_completion_date,
       c.concurrent_program_name || ': ' || ctl.user_concurrent_program_name "program"
FROM APPLSYS.fnd_Concurrent_requests a,
     APPLSYS.fnd_concurrent_processes b,
     applsys.fnd_concurrent_queues q,
     APPLSYS.fnd_concurrent_programs c,
     APPLSYS.fnd_concurrent_programs_tl ctl
WHERE a.controlling_manager = b.concurrent_process_id
AND a.concurrent_program_id = c.concurrent_program_id
AND a.program_application_id = c.application_id
AND a.status_code = 'E'
AND a.phase_code = 'C'
AND actual_start_date > sysdate - 2
AND b.queue_application_id = q.application_id
AND b.concurrent_queue_id = q.concurrent_queue_id
AND ctl.concurrent_program_id = c.concurrent_program_id
AND ctl.LANGUAGE = 'US'
ORDER BY 5 DESC;

3. Monitoring and Performance Tuning

These queries help you monitor the overall health of your concurrent processing system by checking manager status, pending requests, and average run times.

Concurrent Manager Status for a Given SID

Use this to get detailed session information for a specific concurrent manager session.

SQL
col MODULE for a20
col OSUSER for a10
col USERNAME for a10
set num 10
col MACHINE for a20
set lines 200
col SCHEMANAME for a10
select s.sid,
       s.serial#,
       p.spid os_pid,
       s.status,
       s.osuser,
       s.username,
       s.MACHINE,
       s.MODULE,
       s.SCHEMANAME,
       s.action
from gv$session s, gv$process p
WHERE s.paddr = p.addr
and s.sid = '&oracle_sid';

Checking Which Manager Will Execute a Program

This query helps you understand the concurrent manager specialization rules by showing which manager is set to run a specific program.

SQL
SELECT user_concurrent_program_name,
       user_concurrent_queue_name
FROM apps.fnd_concurrent_programs_tl cp,
     apps.fnd_concurrent_queue_content cqc,
     apps.fnd_concurrent_queues_tl cq
WHERE cqc.type_application_id(+) = cp.application_id
AND cqc.type_id(+) = cp.concurrent_program_id
AND cqc.type_code(+) = 'P'
AND cqc.include_flag(+) = 'I'
AND cp.LANGUAGE = 'US'
AND cp.user_concurrent_program_name = '&USER_CONCURRENT_PROGRAM_NAME'
AND NVL (cqc.concurrent_queue_id, 0) = cq.concurrent_queue_id
AND NVL (cqc.queue_application_id, 0) = cq.application_id
AND cq.LANGUAGE = 'US'

To See All Pending / Running Requests per Manager

This gives you a quick snapshot of the workload for each concurrent manager.

SQL
SELECT request_id,
       phase_code,
       status_code,
       user_name,
       user_concurrent_queue_name
FROM apps.fnd_concurrent_worker_requests cwr,
     apps.fnd_concurrent_queues_tl cq,
     apps.fnd_user fu
WHERE (cwr.phase_code = 'P' OR cwr.phase_code = 'R')
AND cwr.hold_flag != 'Y'
AND cwr.requested_start_date <= SYSDATE
AND cwr.concurrent_queue_id = cq.concurrent_queue_id
AND cwr.queue_application_id = cq.application_id
AND cq.LANGUAGE = 'US'
AND cwr.requested_by = fu.user_id
ORDER BY 5

4. Administering Oracle EBS

These queries provide a broader view of your Oracle EBS environment, from user management to applied patches.

To Find Out If a Module is Installed

This query lists all installed Oracle EBS products along with their patch levels.

SQL
SELECT a.application_name,
       a.product_code,
       DECODE (b.status, 'I', 'Installed', 'S', 'Shared', 'N/A') status,
       patch_level
FROM apps.fnd_application_vl a,
     apps.fnd_product_installations b
WHERE a.application_id = b.application_id
and b.status='I'
order by product_code asc;

Script to Reset Oracle Apps Front-End User Password

This is an essential administrative script for resetting a user's password from the backend using PL/SQL.

SQL
DECLARE
  flag_value BOOLEAN;
BEGIN
  flag_value := fnd_user_pkg.changepassword(username=> 'NKUMAR2', newpassword => 'welcome1');
  IF flag_value THEN
    DBMS_OUTPUT.PUT_LINE('The password reset successfully');
  ELSE
    DBMS_OUTPUT.PUT_LINE('The password reset has failed');
  END IF;
END;
/
COMMIT;

How to Check the System Administrator Responsibility for Users

This query quickly identifies which users have been assigned the "System Administrator" responsibility and for what time frame.

SQL
set lines 2000
set pages 5000
col USER_NAME for a45
Select b.user_name,
       c.responsibility_name,
       a.START_DATE,
       a.END_DATE
from fnd_user_resp_groups_direct a,
     fnd_user b,
     fnd_responsibility_tl c
where a.user_id = b.user_id
and a.responsibility_id = c.responsibility_id
and c.responsibility_name='System Administrator';

This collection of queries should serve as a powerful resource for any Oracle EBS professional. Bookmark this page and refer to it whenever you need to dive into the details of your concurrent processing environment.

Unmasking Performance Bottlenecks: A DBA's Guide to Finding Slow Concurrent Programs

 

Unmasking Performance Bottlenecks: A DBA's Guide to Finding Slow Concurrent Programs

As an Oracle Apps DBA, one of the most critical tasks is ensuring the smooth and efficient operation of concurrent programs. A single long-running or poorly performing program can bring the entire system to a halt.

For proactive performance analysis, it's essential to regularly identify which concurrent programs are consuming the most time. This allows you to focus your tuning efforts where they will have the greatest impact.

Here is a powerful and straightforward SQL query that I use regularly to find the top 10 longest-running completed concurrent programs in Oracle E-Business Suite.

The Query

-- This query identifies the top 10 longest-running completed concurrent programs.
-- It joins two key tables to get the request details and program names.
select
    fcpt.user_concurrent_program_name as "Program Name",
    fcr.request_id as "Request ID",
    fcr.actual_start_date as "Start Time",
    fcr.actual_completion_date as "End Time",
    -- Calculate the run time in minutes
    round((fcr.actual_completion_date - fcr.actual_start_date) * 24 * 60, 2) as "Run Time (Minutes)"
from
    apps.fnd_concurrent_requests fcr,
    apps.fnd_concurrent_programs_tl fcpt
where
    -- Join condition to link requests to program names
    fcr.concurrent_program_id = fcpt.concurrent_program_id
    and fcr.program_application_id = fcpt.application_id
    and fcpt.language = userenv('LANG')
    -- Filter for completed requests with a start and end time
    and fcr.phase_code = 'C' -- 'C' for Complete
    and fcr.actual_start_date is not null
    and fcr.actual_completion_date is not null
order by
    "Run Time (Minutes)" desc
fetch first 10 rows only; -- Fetch only the top 10 longest-running programs

How It Works: An Expert's Breakdown

Let's dissect this query to understand its power:

  • Data Source: The query leverages two essential tables:

    • fnd_concurrent_requests: This is the master table for all concurrent requests. It contains the raw data, including the request_id, program_id, and, most importantly, the actual_start_date and actual_completion_date timestamps.

    • fnd_concurrent_programs_tl: This table provides the user-facing name of the program (user_concurrent_program_name), making the output immediately understandable without needing to cross-reference program IDs.

  • The Crucial Calculation: The heart of this query is the round((fcr.actual_completion_date - fcr.actual_start_date) * 24 * 60, 2) calculation. In Oracle, subtracting two DATE data types results in a value representing the number of days between them. By multiplying this by 24 (hours per day) and then 60 (minutes per hour), we get the total runtime in minutes.

  • Targeted Filtering: The WHERE clause is key to getting meaningful results.

    • fcr.phase_code = 'C': We only look at requests that have successfully completed.

    • fcr.actual_start_date is not null and fcr.actual_completion_date is not null: This ensures we're only calculating the runtime for programs that had a complete lifecycle recorded.

  • Ranking the Results: order by "Run Time (Minutes)" desc sorts the output to show the most time-consuming programs first. fetch first 10 rows only is an efficient, modern Oracle feature that limits the result set to the top 10, preventing an unwieldy output in a busy system.

Final Thoughts

This simple, yet effective, query is an invaluable tool for any Oracle Apps DBA. It helps to quickly pinpoint performance hogs, allowing you to prioritize your performance tuning efforts and keep your Oracle EBS environment running smoothly. Regularly running this query can be a game-changer for maintaining a healthy system.

How to Monitor LOB Storage in Oracle: A Simple SQL Query

 

How to Monitor LOB Storage in Oracle: A Simple SQL Query

Database administrators often need to monitor storage usage, and one of the most common culprits for large data growth is Large Objects (LOBs). LOBs can include anything from documents and images to video files stored directly in the database.

Knowing which programs are generating the most LOB data is a critical first step in managing your database's storage footprint. The following SQL query provides a simple and effective way to do just that, presenting the results in both megabytes (MB) and gigabytes (GB) for easy analysis.

The Query

Here is the SQL query you can run against your Oracle database:

-- This query calculates the total size of LOBs per program,
-- showing the results in both Megabytes (MB) and Gigabytes (GB).
select
  program_name,
  -- Calculate size in Megabytes (MB)
  round(sum(dbms_lob.getlength(FILE_DATA)) / 1024 / 1024, 0) as "Size(MB)",
  -- Calculate size in Gigabytes (GB)
  round(sum(dbms_lob.getlength(FILE_DATA)) / 1024 / 1024 / 1024, 2) as "Size(GB)"
from
  APPS.fnd_LOBS
where
  expiration_date is NULL
group by
  program_name
order by
  "Size(MB)" desc;

Breakdown of the Query

  • select program_name, ...: This selects the name of the program that owns the LOB.

  • dbms_lob.getlength(FILE_DATA): This is the core of the calculation. The dbms_lob.getlength function retrieves the size of the FILE_DATA LOB column in bytes.

  • round(sum(...) / 1024 / 1024, 0) as "Size(MB)": We take the sum of the byte lengths for each program, then divide by 1024 twice to convert bytes to megabytes. The round function ensures the number is a clean integer, and the as clause provides a clear column header.

  • round(sum(...) / 1024 / 1024 / 1024, 2) as "Size(GB)": Similar to the megabyte calculation, this line divides the total bytes by 1024 three times to get the size in gigabytes, rounded to two decimal places.

  • from APPS.fnd_LOBS: The query retrieves data from the fnd_LOBS table, which is a common table in Oracle E-Business Suite for storing LOB data.

  • where expiration_date is NULL: This is an important filter. It ensures that only LOBs that have not yet expired are included in the size calculation, giving you a picture of your current, active storage.

  • group by program_name: This groups all rows belonging to the same program_name together so that the sum function can calculate a total for each one.

  • order by "Size(MB)" desc: Finally, the results are sorted in descending order based on the total size in megabytes. This brings the programs consuming the most storage to the top of your list, allowing you to quickly identify your biggest storage users.

Conclusion

Running this simple query can provide valuable insights into your database's LOB storage, helping you proactively manage resources and identify areas for potential cleanup or optimization.

How to Identify and Manage the Largest Objects in Your Oracle EBS Database

 

Title: How to Identify and Manage the Largest Objects in Your Oracle EBS Database

Introduction

Database size can be a critical factor for performance and stability, especially in a complex and high-volume environment like Oracle E-Business Suite (EBS). A growing database can lead to slower queries, longer backup times, and increased storage costs. One of the most effective ways to manage database growth is to identify which tables and objects are consuming the most space.

In this blog post, we'll walk you through how to use simple yet powerful SQL queries to find the largest objects in your Oracle EBS database. We'll start with a basic query and then move on to a more comprehensive one that provides a detailed breakdown of space usage, which is essential for effective database management.


Step 1: The Quick and Simple Approach - Find the Top 10 Largest Objects

If you just need a quick overview of the biggest segments in your entire database, this query is your best friend. It lists the top 10 largest objects (tables, indexes, LOBs, etc.) in terms of raw size.

SQL
SELECT *
FROM (
  SELECT
    SEGMENT_NAME,
    SEGMENT_TYPE,
    BYTES / 1024 / 1024 / 1024 AS GB,
    TABLESPACE_NAME
  FROM DBA_SEGMENTS
  ORDER BY BYTES DESC
)
WHERE ROWNUM <= 10;

What this query does:

  • It queries the DBA_SEGMENTS view, which contains information about all segments in the database.

  • It sorts all segments by size (BYTES) in descending order.

  • The ROWNUM <= 10 clause limits the output to just the top 10 rows.

  • The BYTES column is converted to gigabytes (GB) for easy reading.

This is a great starting point, but it doesn't tell you the whole story, especially in an EBS database where a single table might have multiple indexes and LOB segments.


Step 2: The Detailed Approach - A Comprehensive Breakdown for EBS

In an EBS environment, it's more useful to know which tables (including all their associated indexes and LOBs) are taking up the most space. This next query provides a more comprehensive view by grouping all related segments under their parent table.

SQL
WITH tab_size AS (
  SELECT
    owner,
    table_name,
    SUM(total_bytes) total_bytes,
    SUM(tab_bytes) tab_bytes,
    SUM(ind_bytes) ind_bytes,
    SUM(lob_bytes) lob_bytes,
    SUM(lobind_bytes) lobind_bytes
  FROM (
    -- Table segments
    SELECT
      owner,
      segment_name table_name,
      bytes total_bytes,
      bytes tab_bytes,
      0 ind_bytes,
      0 lob_bytes,
      0 lobind_bytes
    FROM dba_segments
    WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
    UNION ALL
    -- Index segments
    SELECT
      i.table_owner owner,
      i.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      s.bytes ind_bytes,
      0 lob_bytes,
      0 lobind_bytes
    FROM dba_indexes i, dba_segments s
    WHERE s.segment_name = i.index_name
    AND s.owner = i.owner
    AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
    UNION ALL
    -- LOB segments
    SELECT
      l.owner,
      l.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      0 ind_bytes,
      s.bytes lob_bytes,
      0 lobind_bytes
    FROM dba_lobs l, dba_segments s
    WHERE s.segment_name = l.segment_name
    AND s.owner = l.owner
    AND s.segment_type IN ('LOBSEGMENT', 'LOB PARTITION')
    UNION ALL
    -- LOB index segments
    SELECT
      l.owner,
      l.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      0 ind_bytes,
      0 lob_bytes,
      s.bytes lobind_bytes
    FROM dba_lobs l, dba_segments s
    WHERE s.segment_name = l.index_name
    AND s.owner = l.owner
    AND s.segment_type = 'LOBINDEX'
  )
  GROUP BY owner, table_name
)
SELECT
  t.owner,
  t.table_name,
  ROUND(t.total_bytes / (1024 * 1024), 2) AS Total_MB,
  ROUND(t.tab_bytes / (1024 * 1024), 2) AS Table_MB,
  ROUND(t.ind_bytes / (1024 * 1024), 2) AS Indexes_MB,
  ROUND(t.lob_bytes / (1024 * 1024), 2) AS LOB_MB,
  ROUND(t.lobind_bytes / (1024 * 1024), 2) AS LOBIndexes_MB
FROM tab_size t
ORDER BY t.total_bytes DESC;

SQL

WITH tab_size AS (
  SELECT
    owner,
    table_name,
    SUM(total_bytes) total_bytes,
    SUM(tab_bytes) tab_bytes,
    SUM(ind_bytes) ind_bytes,
    SUM(lob_bytes) lob_bytes,
    SUM(lobind_bytes) lobind_bytes
  FROM (
    -- Table segments
    SELECT
      owner,
      segment_name table_name,
      bytes total_bytes,
      bytes tab_bytes,
      0 ind_bytes,
      0 lob_bytes,
      0 lobind_bytes
    FROM dba_segments
    WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
    UNION ALL
    -- Index segments
    SELECT
      i.table_owner owner,
      i.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      s.bytes ind_bytes,
      0 lob_bytes,
      0 lobind_bytes
    FROM dba_indexes i, dba_segments s
    WHERE s.segment_name = i.index_name
    AND s.owner = i.owner
    AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
    UNION ALL
    -- LOB segments
    SELECT
      l.owner,
      l.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      0 ind_bytes,
      s.bytes lob_bytes,
      0 lobind_bytes
    FROM dba_lobs l, dba_segments s
    WHERE s.segment_name = l.segment_name
    AND s.owner = l.owner
    AND s.segment_type IN ('LOBSEGMENT', 'LOB PARTITION')
    UNION ALL
    -- LOB index segments
    SELECT
      l.owner,
      l.table_name,
      s.bytes total_bytes,
      0 tab_bytes,
      0 ind_bytes,
      0 lob_bytes,
      s.bytes lobind_bytes
    FROM dba_lobs l, dba_segments s
    WHERE s.segment_name = l.index_name
    AND s.owner = l.owner
    AND s.segment_type = 'LOBINDEX'
  )
  GROUP BY owner, table_name
)
SELECT
  t.owner,
  t.table_name,
  ROUND(t.total_bytes / (1024 * 1024 * 1024), 2) AS Total_GB,
  ROUND(t.tab_bytes / (1024 * 1024 * 1024), 2) AS Table_GB,
  ROUND(t.ind_bytes / (1024 * 1024 * 1024), 2) AS Indexes_GB,
  ROUND(t.lob_bytes / (1024 * 1024 * 1024), 2) AS LOB_GB,
  ROUND(t.lobind_bytes / (1024 * 1024 * 1024), 2) AS LOBIndexes_GB
FROM tab_size t
ORDER BY t.total_bytes DESC;

Why this query is so powerful for EBS:

  • It aggregates the size of a table and all its related indexes and LOB segments into a single, easy-to-read row.

  • You can see the breakdown of space usage (e.g., "This table is large because of its indexes, not the table data itself").

  • This helps you identify which schemas (e.g., APPLSYS, AP, GL) are growing the most.

Note: To run these queries, you must have the necessary privileges, typically as a DBA or a user with permissions to access the DBA_ views.


Conclusion and Next Steps

By regularly running these queries, you can stay on top of your Oracle EBS database growth. Pinpointing the largest objects is the first step toward effective database management. Once you've identified the culprits, you can investigate potential actions like:

  • Archiving old data: Many large tables contain historical data that can be moved to an archive tablespace or a separate database.

  • Purging data: Some tables, especially log or temporary tables, can have their data purged after a certain period.

  • Analyzing indexes: For large indexes, you might need to analyze them for fragmentation and consider rebuilding or reorganizing them.

  • Compressing tables/indexes: In some cases, Oracle's compression features can significantly reduce space usage.

Proactive monitoring and management of your database's largest objects will ensure your Oracle EBS system remains performant and stable for the long term.

long

 


set markup html on

set heading off

set linesize 1000

set pagesize 50000


-- Spool the output to an HTML file

spool long_running_requests.html


-- Add a title to the HTML file

SELECT '<h2>Monitoring Long-Running Concurrent Requests</h2>' FROM DUAL;


-- Add the HTML table header

SELECT

    '<table>' ||

    '<tr>' ||

    '<th>Concurrent Program</th>' ||

    '<th>Run Time (Minutes)</th>' ||

    '<th>Request ID</th>' ||

    '<th>Parent ID</th>' ||

    '<th>Request Date</th>' ||

    '<th>Start Date</th>' ||

    '<th>Completion Date</th>' ||

    '<th>End-to-End Mins</th>' ||

    '<th>Lag Time (Seconds)</th>' ||

    '<th>Requested By</th>' ||

    '<th>Phase</th>' ||

    '<th>Status</th>' ||

    '<th>Arguments</th>' ||

    '<th>Priority</th>' ||

    '</tr>' AS HTML_HEADER

FROM DUAL;


-- The actual query to generate the table rows

SELECT

    '<tr>' ||

    '<td>' || c.user_concurrent_program_name || '</td>' ||

    '<td>' || round(((sysdate-a.actual_start_date)*24*60),2) || '</td>' ||

    '<td>' || a.request_id || '</td>' ||

    '<td>' || NVL(TO_CHAR(a.parent_request_id), 'N/A') || '</td>' ||

    '<td>' || TO_CHAR(a.request_date, 'DD-MON-YYYY HH24:MI:SS') || '</td>' ||

    '<td>' || TO_CHAR(a.actual_start_date, 'DD-MON-YYYY HH24:MI:SS') || '</td>' ||

    '<td>' || TO_CHAR(a.actual_completion_date, 'DD-MON-YYYY HH24:MI:SS') || '</td>' ||

    '<td>' || round((a.actual_completion_date-a.request_date)*24*60, 2) || '</td>' ||

    '<td>' || round((a.actual_start_date-a.request_date)*24*60*60, 2) || '</td>' ||

    '<td>' || d.user_name || '</td>' ||

    '<td>' || a.phase_code || '</td>' ||

    '<td>' || a.status_code || '</td>' ||

    '<td>' || a.argument_text || '</td>' ||

    '<td>' || a.priority || '</td>' ||

    '</tr>'

FROM

    apps.fnd_concurrent_requests a,

    apps.fnd_concurrent_programs b,

    apps.FND_CONCURRENT_PROGRAMS_TL c,

    apps.fnd_user d

WHERE

    a.concurrent_program_id = b.concurrent_program_id

    AND b.concurrent_program_id = c.concurrent_program_id

    AND a.requested_by = d.user_id

    AND a.status_code = 'R'

ORDER BY

    run_time_minutes DESC;


-- Close the table and stop spooling

SELECT '</table>' FROM DUAL;


spool off

exit


#!/bin/bash


# Database connection string

DB_CONN="username/password@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=your_host)(PORT=your_port))(CONNECT_DATA=(SERVICE_NAME=your_service_name)))"


# The email recipient and subject

EMAIL_TO="recipient@example.com"

EMAIL_SUBJECT="Monitoring: Long-Running Concurrent Requests Report"


# Run the SQL script to generate the HTML file

sqlplus -s $DB_CONN @long_running_requests.sql


# Send the email with the HTML file as the body

cat long_running_requests.html | mailx -s "$EMAIL_SUBJECT" -a "Content-Type: text/html" "$EMAIL_TO"


# Clean up the generated file

rm long_running_requests.html


echo "Monitoring report sent successfully."