Tuesday, July 22, 2025

PL/SQL - Tablespace Utilization Report

/*
 * Comprehensive PL/SQL script for Oracle Database storage and space utilization reporting.
 * Output is formatted for SQL*Plus/Toad using DBMS_OUTPUT with meticulously aligned columns.
 * Enhancements include:
 *   - Database name and total occupied/free space as header.
 *   - Segment-based used space in overall summary for accuracy comparison.
 *   - Simplified temporary tablespace utilization using DBA_TEMP_FREE_SPACE (Oracle 11g+).
 *   - Free space fragmentation report.
 *   - Segment level report limited to top 20 largest objects (using ROWNUM for Oracle 11g+ compatibility).
 *   - Partition_name included in segment report for granularity.
 *   - UNDO tablespaces included in overall reports.
 *   - Improved column formatting with totals where feasible.
 *   - Fixed ORA-00979 in Section 1 using CTE for cleaner aggregation.
 *   - Added Section 6 for real-time temporary tablespace usage via GV$TEMP_SPACE_HEADER.
 *   - Removed ASM Diskgroup Utilization (ASM not used).
 *   - Fixed ORA-00937 (Database Summary), ORA-00900 (Section 4), and ORA-00979 (Section 1).
 *   - Adjusted DBMS_OUTPUT padding for perfect alignment.
 *
 * Ensure SELECT privileges on:
 *   DBA_TABLESPACES, DBA_DATA_FILES, DBA_FREE_SPACE, DBA_SEGMENTS,
 *   DBA_TEMP_FILES, DBA_TEMP_FREE_SPACE, GV$TEMP_SPACE_HEADER, V$DATABASE.
 * Compatible with Oracle 11g and later.
 * To ensure output visibility:
 *   - SQL*Plus: Run `SET SERVEROUTPUT ON SIZE 1000000; SET LINESIZE 200;` before executing.
 *   - Toad: Enable DBMS Output pane and set display width to at least 200 characters.
 * If no output or misalignment occurs, check:
 *   - DBMS_OUTPUT enabled.
 *   - Sufficient privileges.
 *   - Data in queried views (e.g., non-temporary tablespaces, segments).
 */

BEGIN
    -- Enable DBMS_OUTPUT with a large buffer
    DBMS_OUTPUT.ENABLE(1000000);

    -- Declare cursors for each report section
    DECLARE
        -- Cursor for Database Summary (Section 0)
        CURSOR db_summary_cur IS
            SELECT
                d.name AS db_name,
                ROUND(SUM(df.bytes) / (1024 * 1024 * 1024), 2) AS total_allocated_gb,
                ROUND(SUM(df.bytes - NVL(fs.bytes, 0)) / (1024 * 1024 * 1024), 2) AS total_used_gb,
                ROUND(SUM(NVL(fs.bytes, 0)) / (1024 * 1024 * 1024), 2) AS total_free_gb,
                ROUND((SUM(df.bytes - NVL(fs.bytes, 0)) / SUM(df.bytes)) * 100, 2) AS total_used_pct
            FROM
                dba_data_files df
            LEFT JOIN (
                SELECT file_id, SUM(bytes) AS bytes
                FROM dba_free_space
                GROUP BY file_id
            ) fs ON df.file_id = fs.file_id
            CROSS JOIN (
                SELECT name FROM v$database
            ) d
            WHERE
                df.tablespace_name IN (SELECT tablespace_name FROM dba_tablespaces WHERE contents != 'TEMPORARY')
            GROUP BY
                d.name;

        -- Cursor for Overall Tablespace Utilization (Section 1)
        CURSOR overall_tablespace_cur IS
            WITH segment_space AS (
                SELECT tablespace_name, ROUND(SUM(bytes) / (1024 * 1024 * 1024), 2) AS segment_used_gb
                FROM dba_segments
                GROUP BY tablespace_name
            ),
            file_space AS (
                SELECT
                    t.tablespace_name,
                    t.status,
                    SUM(f.bytes) / (1024 * 1024 * 1024) AS total_size_gb,
                    SUM(f.bytes - NVL(s.bytes, 0)) / (1024 * 1024 * 1024) AS used_space_gb,
                    SUM(NVL(s.bytes, 0)) / (1024 * 1024 * 1024) AS free_space_gb,
                    SUM(DECODE(f.autoextensible, 'YES', f.maxbytes, f.bytes)) / (1024 * 1024 * 1024) AS max_size_gb
                FROM
                    dba_tablespaces t
                JOIN
                    dba_data_files f ON t.tablespace_name = f.tablespace_name
                LEFT JOIN (
                    SELECT file_id, SUM(bytes) AS bytes
                    FROM dba_free_space
                    GROUP BY file_id
                ) s ON f.file_id = s.file_id
                WHERE
                    t.contents != 'TEMPORARY'
                GROUP BY
                    t.tablespace_name, t.status
            )
            SELECT
                fs.tablespace_name,
                fs.status,
                ROUND(fs.total_size_gb, 2) AS total_size_gb,
                ROUND(fs.used_space_gb, 2) AS used_space_gb,
                NVL(ss.segment_used_gb, 0) AS segments_used_gb,
                ROUND(fs.free_space_gb, 2) AS free_space_gb,
                ROUND((fs.used_space_gb / fs.total_size_gb) * 100, 2) AS allocated_pct_used,
                ROUND(fs.max_size_gb, 2) AS max_size_gb,
                ROUND((fs.used_space_gb / fs.max_size_gb) * 100, 2) AS max_pct_used
            FROM
                file_space fs
            LEFT JOIN
                segment_space ss ON fs.tablespace_name = ss.tablespace_name
            ORDER BY
                allocated_pct_used DESC;

        -- Cursor for Tablespace Utilization by Datafile (Section 2)
        CURSOR datafile_util_cur IS
            SELECT
                df.tablespace_name,
                df.file_name,
                ROUND(df.bytes / (1024 * 1024 * 1024), 2) AS current_size_gb,
                ROUND((df.bytes - NVL(fs.bytes, 0)) / (1024 * 1024 * 1024), 2) AS used_space_gb,
                ROUND(NVL(fs.bytes, 0) / (1024 * 1024 * 1024), 2) AS free_space_gb,
                ROUND(((df.bytes - NVL(fs.bytes, 0)) / df.bytes) * 100, 2) AS used_pct,
                df.autoextensible,
                CASE
                    WHEN df.autoextensible = 'YES' THEN ROUND(df.maxbytes / (1024 * 1024 * 1024), 2)
                    ELSE NULL
                END AS max_size_gb
            FROM
                dba_data_files df
            LEFT JOIN (
                SELECT file_id, SUM(bytes) AS bytes
                FROM dba_free_space
                GROUP BY file_id
            ) fs ON df.file_id = fs.file_id
            ORDER BY
                df.tablespace_name, df.file_id;

        -- Cursor for Segment Level Utilization (Top 20 Largest Objects, Section 3)
        CURSOR segment_util_cur IS
            SELECT
                owner,
                segment_name,
                partition_name,
                segment_type,
                tablespace_name,
                ROUND(bytes / (1024 * 1024 * 1024), 2) AS size_gb
            FROM (
                SELECT
                    owner,
                    segment_name,
                    partition_name,
                    segment_type,
                    tablespace_name,
                    bytes
                FROM
                    dba_segments
                WHERE
                    tablespace_name IN (SELECT tablespace_name FROM dba_tablespaces WHERE contents != 'TEMPORARY')
                    AND owner NOT IN ('SYS', 'SYSTEM', 'SYSMAN', 'DBSNMP', 'OUTLN', 'APPQOSSYS', 'GSMADMIN_INTERNAL',
                                      'LBACSYS', 'DIP', 'ORACLE_OCM', 'AUDSYS', 'XDB', 'MDSYS', 'OLAPSYS', 'ORDDATA',
                                      'ORDPLUGINS', 'ORDSYS', 'WMSYS', 'EXFSYS', 'CTXSYS', 'ANONYMOUS', 'APEX_%',
                                      'FLOWS_FILES', 'MGMT_VIEW', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
                                      'SI_INFORMTN_SCHEMA', 'XS$NULL', 'MDDATA', 'OJVMSYS', 'APEX_PUBLIC_USER',
                                      'AUDSYS', 'GGSYS', 'DVF', 'DVSYS', 'LBAC_SYS', 'CDB_ONLY_SYS')
                ORDER BY
                    bytes DESC
            )
            WHERE ROWNUM <= 20; -- Compatible with Oracle 11g+

        -- Cursor for Temporary Tablespace Utilization (Section 4)
        CURSOR temp_tablespace_cur IS
            SELECT
                fs.tablespace_name,
                ROUND(fs.allocated_space / (1024 * 1024 * 1024), 2) AS allocated_size_gb,
                ROUND((fs.allocated_space - fs.free_space) / (1024 * 1024 * 1024), 2) AS used_space_gb,
                ROUND(fs.free_space / (1024 * 1024 * 1024), 2) AS free_space_gb,
                ROUND(((fs.allocated_space - fs.free_space) / fs.allocated_space) * 100, 2) AS used_pct,
                ROUND(SUM(DECODE(tf.autoextensible, 'YES', tf.maxbytes, tf.bytes)) / (1024 * 1024 * 1024), 2) AS max_size_gb
            FROM
                dba_temp_free_space fs
            JOIN
                dba_temp_files tf ON fs.tablespace_name = tf.tablespace_name
            GROUP BY
                fs.tablespace_name, fs.allocated_space, fs.free_space
            ORDER BY
                used_pct DESC;

        -- Cursor for Free Space Fragmentation (Section 5)
        CURSOR fragmentation_cur IS
            SELECT
                tablespace_name,
                COUNT(*) AS fragments,
                ROUND(MAX(bytes) / (1024 * 1024 * 1024), 2) AS largest_chunk_gb,
                ROUND(SUM(bytes) / (1024 * 1024 * 1024), 2) AS total_free_gb
            FROM
                dba_free_space
            GROUP BY
                tablespace_name
            ORDER BY
                fragments DESC;

        -- Cursor for Real-Time Temporary Tablespace Usage (Section 6)
        CURSOR realtime_temp_cur IS
            SELECT
                ts.tablespace_name,
                ROUND(SUM(tf.bytes) / (1024 * 1024 * 1024), 2) AS total_temp_size_gb,
                ROUND(SUM(tu.used_space) / (1024 * 1024 * 1024), 2) AS used_temp_space_gb,
                ROUND(SUM(tf.bytes - tu.used_space) / (1024 * 1024 * 1024), 2) AS free_temp_space_gb,
                ROUND((SUM(tu.used_space) / SUM(tf.bytes)) * 100, 2) AS used_pct
            FROM
                dba_temp_files tf
            JOIN
                dba_tablespaces ts ON tf.tablespace_name = ts.tablespace_name
            LEFT JOIN (
                SELECT tablespace_name, SUM(bytes_used) AS used_space
                FROM gv$temp_space_header
                GROUP BY tablespace_name
            ) tu ON ts.tablespace_name = tu.tablespace_name
            WHERE
                ts.contents = 'TEMPORARY'
            GROUP BY
                ts.tablespace_name
            ORDER BY
                ts.tablespace_name;

        -- Variable to track if data was found for each section
        v_data_found BOOLEAN;
    BEGIN
        -- Section 0: Database Summary
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- Database Summary                                                           --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('DB_NAME', 30) || RPAD('Total Allocated(GB)', 20) || 
                             RPAD('Total Used(GB)', 18) || RPAD('Total Free(GB)', 18) || 
                             RPAD('Used Pct(%)', 12));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 30, '-') || ' ' || RPAD('-', 18, '-') || ' ' || 
                             RPAD('-', 16, '-') || ' ' || RPAD('-', 16, '-') || ' ' || 
                             RPAD('-', 10, '-')); -- Total: 96 chars

        v_data_found := FALSE;
        FOR rec IN db_summary_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.db_name, 30) || 
                                 RPAD(TO_CHAR(rec.total_allocated_gb, '999,999,999.99'), 20) || 
                                 RPAD(TO_CHAR(rec.total_used_gb, '999,999,999.99'), 18) || 
                                 RPAD(TO_CHAR(rec.total_free_gb, '999,999,999.99'), 18) || 
                                 RPAD(TO_CHAR(rec.total_used_pct, '999.99'), 12));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Database Summary. Check privileges or data in DBA_DATA_FILES/DBA_TABLESPACES.');
        END IF;

        -- Section 1: Overall Tablespace Utilization
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 1. Overall Tablespace Utilization (Summary with Autoextend and Segments)   --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('TABLESPACE_NAME', 25) || RPAD('STATUS', 10) || 
                             RPAD('Allocated(GB)', 15) || RPAD('Used(GB)', 15) || 
                             RPAD('Segments(GB)', 15) || RPAD('Free(GB)', 15) || 
                             RPAD('Used Pct(%)', 12) || RPAD('Max Size(GB)', 15) || 
                             RPAD('Max Pct(%)', 12));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 25, '-') || ' ' || RPAD('-', 8, '-') || ' ' || 
                             RPAD('-', 13, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 13, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 10, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 10, '-')); -- Total: 124 chars

        v_data_found := FALSE;
        FOR rec IN overall_tablespace_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.tablespace_name, 25) || 
                                 RPAD(rec.status, 10) || 
                                 RPAD(TO_CHAR(rec.total_size_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.segments_used_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.free_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.allocated_pct_used, '999.99'), 12) || 
                                 RPAD(TO_CHAR(rec.max_size_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.max_pct_used, '999.99'), 12));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Overall Tablespace Utilization. Check privileges or data in DBA_TABLESPACES/DBA_DATA_FILES.');
        END IF;

        -- Section 2: Tablespace Utilization by Datafile
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 2. Tablespace Utilization by Datafile (Granular)                           --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('TABLESPACE_NAME', 25) || RPAD('FILE_NAME', 50) || 
                             RPAD('Current Size(GB)', 18) || RPAD('Used(GB)', 15) || 
                             RPAD('Free(GB)', 15) || RPAD('Used Pct(%)', 12) || 
                             RPAD('Autoextend?', 12) || RPAD('Max Size(GB)', 15));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 25, '-') || ' ' || RPAD('-', 48, '-') || ' ' || 
                             RPAD('-', 16, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 13, '-') || ' ' || RPAD('-', 10, '-') || ' ' || 
                             RPAD('-', 10, '-') || ' ' || RPAD('-', 13, '-')); -- Total: 152 chars

        v_data_found := FALSE;
        FOR rec IN datafile_util_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.tablespace_name, 25) || 
                                 RPAD(SUBSTR(rec.file_name, 1, 50), 50) || 
                                 RPAD(TO_CHAR(rec.current_size_gb, '999,999.99'), 18) || 
                                 RPAD(TO_CHAR(rec.used_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.free_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_pct, '999.99'), 12) || 
                                 RPAD(rec.autoextensible, 12) || 
                                 RPAD(NVL(TO_CHAR(rec.max_size_gb, '999,999.99'), 'N/A'), 15));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Tablespace Utilization by Datafile. Check privileges or data in DBA_DATA_FILES.');
        END IF;

        -- Section 3: Segment Level Utilization (Top 20 Largest Objects)
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 3. Segment Level Utilization (Top 20 Largest Objects)                      --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('OWNER', 20) || RPAD('SEGMENT_NAME', 30) || 
                             RPAD('PARTITION_NAME', 30) || RPAD('SEGMENT_TYPE', 15) || 
                             RPAD('TABLESPACE_NAME', 25) || RPAD('Size(GB)', 15));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 20, '-') || ' ' || RPAD('-', 28, '-') || ' ' || 
                             RPAD('-', 28, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 23, '-') || ' ' || RPAD('-', 13, '-')); -- Total: 133 chars

        v_data_found := FALSE;
        FOR rec IN segment_util_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.owner, 20) || 
                                 RPAD(rec.segment_name, 30) || 
                                 RPAD(NVL(rec.partition_name, 'NONE'), 30) || 
                                 RPAD(rec.segment_type, 15) || 
                                 RPAD(rec.tablespace_name, 25) || 
                                 RPAD(TO_CHAR(rec.size_gb, '999,999.99'), 15));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Segment Level Utilization. Check privileges or data in DBA_SEGMENTS.');
        END IF;

        -- Section 4: Temporary Tablespace Utilization
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 4. Temporary Tablespace Utilization (Using DBA_TEMP_FREE_SPACE)            --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('TABLESPACE_NAME', 25) || RPAD('Allocated(GB)', 15) || 
                             RPAD('Used(GB)', 15) || RPAD('Free(GB)', 15) || 
                             RPAD('Used Pct(%)', 12) || RPAD('Max Size(GB)', 15));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 25, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 13, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 10, '-') || ' ' || RPAD('-', 13, '-')); -- Total: 99 chars

        v_data_found := FALSE;
        FOR rec IN temp_tablespace_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.tablespace_name, 25) || 
                                 RPAD(TO_CHAR(rec.allocated_size_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.free_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_pct, '999.99'), 12) || 
                                 RPAD(TO_CHAR(rec.max_size_gb, '999,999.99'), 15));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Temporary Tablespace Utilization. Check privileges or data in DBA_TEMP_FREE_SPACE.');
        END IF;

        -- Section 5: Free Space Fragmentation
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 5. Free Space Fragmentation by Tablespace                                  --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('TABLESPACE_NAME', 25) || RPAD('Fragments', 12) || 
                             RPAD('Largest Chunk(GB)', 18) || RPAD('Total Free(GB)', 15));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 25, '-') || ' ' || RPAD('-', 10, '-') || ' ' || 
                             RPAD('-', 16, '-') || ' ' || RPAD('-', 13, '-')); -- Total: 70 chars

        v_data_found := FALSE;
        FOR rec IN fragmentation_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.tablespace_name, 25) || 
                                 RPAD(TO_CHAR(rec.fragments, '999,999'), 12) || 
                                 RPAD(TO_CHAR(rec.largest_chunk_gb, '999,999.99'), 18) || 
                                 RPAD(TO_CHAR(rec.total_free_gb, '999,999.99'), 15));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Free Space Fragmentation. Check privileges or data in DBA_FREE_SPACE.');
        END IF;

        -- Section 6: Real-Time Temporary Tablespace Usage
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- 6. Real-Time Temporary Tablespace Usage (Using GV$TEMP_SPACE_HEADER)       --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE(RPAD('TABLESPACE_NAME', 25) || RPAD('Total Size(GB)', 15) || 
                             RPAD('Used(GB)', 15) || RPAD('Free(GB)', 15) || 
                             RPAD('Used Pct(%)', 12));
        DBMS_OUTPUT.PUT_LINE(RPAD('-', 25, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 13, '-') || ' ' || RPAD('-', 13, '-') || ' ' || 
                             RPAD('-', 10, '-')); -- Total: 84 chars

        v_data_found := FALSE;
        FOR rec IN realtime_temp_cur LOOP
            v_data_found := TRUE;
            DBMS_OUTPUT.PUT_LINE(RPAD(rec.tablespace_name, 25) || 
                                 RPAD(TO_CHAR(rec.total_temp_size_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_temp_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.free_temp_space_gb, '999,999.99'), 15) || 
                                 RPAD(TO_CHAR(rec.used_pct, '999.99'), 12));
        END LOOP;
        IF NOT v_data_found THEN
            DBMS_OUTPUT.PUT_LINE('No data found for Real-Time Temporary Tablespace Usage. Check privileges or data in DBA_TEMP_FILES/GV$TEMP_SPACE_HEADER.');
        END IF;

        -- Final message
        DBMS_OUTPUT.PUT_LINE(' ');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE('-- Comprehensive Oracle Database Storage and Space Utilization Report completed. --');
        DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------');
        DBMS_OUTPUT.PUT_LINE(' ');
    EXCEPTION
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('Error during report generation: ' || SQLERRM);
            RAISE;
    END;
END;
/

No comments: