← PDB & Multitenant
Oracle · PDB & Multitenant

How to project tablespace growth per PDB in Oracle Multitenant

PL/SQL report that walks every PDB in a container database and prints per-tablespace growth history plus 30/60/90 day projections from AWR.

Tested on12c18c19c21c23ai26ai

Multitenant version: iterate V$CONTAINERS (skip PDB$SEED), then loop every PERMANENT tablespace inside each PDB, and print a growth history and projection for each. Uses CDB_TABLESPACES and DBA_HIST_TBSPC_SPACE_USAGE with con_id filters.

Requires Oracle Multitenant (12c and later). DBA_HIST_TBSPC_SPACE_USAGE and DBA_HIST_SNAPSHOT are part of Oracle Diagnostic Pack and require that license on Enterprise Edition. Run from the CDB root as a common DBA user.

SET SERVEROUT ON
SET VERIFY OFF
SET LINES 200 PAGES 2000

DECLARE
  v_ts_id                    NUMBER;
  v_ts_block_size            NUMBER;
  v_begin_snap_id            NUMBER;
  v_end_snap_id              NUMBER;
  v_begin_snap_date          DATE;
  v_end_snap_date            DATE;
  v_numdays                  NUMBER;
  v_ts_begin_size            NUMBER;
  v_ts_end_size              NUMBER;
  v_ts_growth                NUMBER;
  v_count                    NUMBER;
  v_ts_begin_allocated_space NUMBER;
  v_ts_end_allocated_space   NUMBER;
  not_in_awr                 EXCEPTION;

  CURSOR v_con_cur IS
    SELECT con_id, name FROM v$containers WHERE name <> 'PDB$SEED';

  CURSOR v_ts_cur (par_con_id NUMBER) IS
    SELECT tablespace_name
    FROM   cdb_tablespaces
    WHERE  contents = 'PERMANENT'
    AND    con_id   = par_con_id;
BEGIN
  FOR v_con_rec IN v_con_cur LOOP
    DBMS_OUTPUT.PUT_LINE(CHR(10) || 'Container = ' || v_con_rec.name);
    DBMS_OUTPUT.PUT_LINE('====================');

    FOR v_ts_rec IN v_ts_cur(v_con_rec.con_id) LOOP
      BEGIN
        SELECT ts# INTO v_ts_id
        FROM   v$tablespace
        WHERE  name   = v_ts_rec.tablespace_name
        AND    con_id = v_con_rec.con_id;

        SELECT COUNT(*) INTO v_count
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id
        AND    con_id        = v_con_rec.con_id;
        IF v_count = 0 THEN RAISE not_in_awr; END IF;

        SELECT block_size INTO v_ts_block_size
        FROM   cdb_tablespaces
        WHERE  tablespace_name = v_ts_rec.tablespace_name
        AND    con_id          = v_con_rec.con_id;

        SELECT MIN(snap_id), MAX(snap_id),
               MIN(TRUNC(TO_DATE(rtime, 'MM/DD/YYYY HH24:MI:SS'))),
               MAX(TRUNC(TO_DATE(rtime, 'MM/DD/YYYY HH24:MI:SS')))
        INTO   v_begin_snap_id, v_end_snap_id, v_begin_snap_date, v_end_snap_date
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id
        AND    con_id        = v_con_rec.con_id;

        v_numdays := v_end_snap_date - v_begin_snap_date;

        SELECT ROUND(MAX(tablespace_size) * v_ts_block_size / 1024 / 1024, 2)
        INTO   v_ts_begin_allocated_space
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id AND con_id = v_con_rec.con_id AND snap_id = v_begin_snap_id;

        SELECT ROUND(MAX(tablespace_size) * v_ts_block_size / 1024 / 1024, 2)
        INTO   v_ts_end_allocated_space
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id AND con_id = v_con_rec.con_id AND snap_id = v_end_snap_id;

        SELECT ROUND(MAX(tablespace_usedsize) * v_ts_block_size / 1024 / 1024, 2)
        INTO   v_ts_begin_size
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id AND con_id = v_con_rec.con_id AND snap_id = v_begin_snap_id;

        SELECT ROUND(MAX(tablespace_usedsize) * v_ts_block_size / 1024 / 1024, 2)
        INTO   v_ts_end_size
        FROM   dba_hist_tbspc_space_usage
        WHERE  tablespace_id = v_ts_id AND con_id = v_con_rec.con_id AND snap_id = v_end_snap_id;

        v_ts_growth := v_ts_end_size - v_ts_begin_size;

        DBMS_OUTPUT.PUT_LINE(CHR(10) || v_ts_rec.tablespace_name);
        DBMS_OUTPUT.PUT_LINE('  Block size: ' || v_ts_block_size);
        DBMS_OUTPUT.PUT_LINE('  Allocated: '  || v_ts_end_allocated_space || ' MB');
        DBMS_OUTPUT.PUT_LINE('  Used:      '  || v_ts_end_size || ' MB');
        DBMS_OUTPUT.PUT_LINE('  Used %:    '  || ROUND(v_ts_end_size/v_ts_end_allocated_space*100,2) || ' %');
        DBMS_OUTPUT.PUT_LINE('  Growth in ' || v_numdays || ' days: ' || v_ts_growth || ' MB');

        IF (v_ts_growth <= 0 OR v_numdays <= 0) THEN
          DBMS_OUTPUT.PUT_LINE('  !!! NO GROWTH FOR ' || v_ts_rec.tablespace_name || ' !!!');
        ELSE
          DBMS_OUTPUT.PUT_LINE('  Per day growth: ' || ROUND(v_ts_growth/v_numdays,2) || ' MB');
          DBMS_OUTPUT.PUT_LINE('  Projected 30d:  ' || ROUND((v_ts_growth/v_numdays)*30,2) || ' MB');
          DBMS_OUTPUT.PUT_LINE('  Projected 60d:  ' || ROUND((v_ts_growth/v_numdays)*60,2) || ' MB');
          DBMS_OUTPUT.PUT_LINE('  Projected 90d:  ' || ROUND((v_ts_growth/v_numdays)*90,2) || ' MB');
        END IF;
      EXCEPTION
        WHEN not_in_awr THEN
          DBMS_OUTPUT.PUT_LINE(CHR(10) || v_ts_rec.tablespace_name || ': NOT FOUND IN AWR');
      END;
    END LOOP;
  END LOOP;
END;
/
pdbmultitenantspacegrowthawrplsql