Published: 4/14/2026 Oracle APEX 24.2 Internal Table Defragmentation APEX PL/SQL SQL Oracle Application Express 24.2 stores its metadata in a versioned engine schema (typically APEX_240200) along with companion schemas APEX_PUBLIC_USER, FLOWS_FILES, and APEX_LISTENER / APEX_REST_PUBLIC_USER. Over time — particularly after application imports/exports, workspace provisioning, developer activity logging, and APEX upgrades — the WWV_FLOW_* internal tables accumulate significant fragmentation. This guide provides the SQL and PL/SQL needed to: Discover the actual APEX engine schema dynamically in each PDB Identify which APEX internal tables are fragmented and worth reclaiming Defragment those tables using either an online method (no outage) or an outage method (faster, but requires APEX downtime) Warning: Never modify data in APEX internal tables directly. The procedures in this guide only reorganize physical storage — they do not alter any rows or metadata. SampleBEGIN apex_defrag.defrag_apex_online( p_execute => TRUE, p_frag_threshold => 10, p_min_reclaimable => 5 ); END; / ==================================================== APEX DEFRAg ENGINE (WITH LOGGING) RUN ID: 20260414122432 MODE : EXECUTE ==================================================== 1. APEX_240200.WWV_FLOW_DATA | FRAG: 25.8 | MB: 3.309597015380859375 2. APEX_240200.WWV_FLOW_SESSIONS$ | FRAG: 40.8 | MB: 2.4488983154296875 ERROR: ORA-10631: SHRINK clause should not be specified for this object 3. APEX_240200.WWV_FLOW_PAGE_PLUGS | FRAG: 29.2 | MB: 2.3692760467529296875 4. APEX_240200.WWV_FLOW_STEP_ITEMS | FRAG: 22.5 | MB: 1.310394287109375 ==================================================== TOTAL OBJECTS: 4 ERRORS : 1 ==================================================== OWNER TABLE NAME ACTION FRAG % RECLAIM MB STATUS END TIME --------------- ------------------------------ --------------- ------- --------------- ------------ ------------------- APEX_240200 WWV_FLOW_STEP_ITEMS SHRINK 22.5 1.31 SUCCESS 2026-04-14 12:24:32 APEX_240200 WWV_FLOW_PAGE_PLUGS SHRINK 29.2 2.37 SUCCESS 2026-04-14 12:24:32 APEX_240200 WWV_FLOW_SESSIONS$ SHRINK 40.8 2.45 SUCCESS 2026-04-14 12:24:32 APEX_240200 WWV_FLOW_DATA SHRINK 25.8 3.31 SUCCESS 2026-04-14 12:24:32 APEX_240200 WWV_FLOW_STEP_ITEMS SHRINK 23.4 1.37 SUCCESS 2026-04-14 11:59:44Create UserCREATE USER apex_defrag IDENTIFIED BY "__PASSWORD__" DEFAULT TABLESPACE sysaux TEMPORARY TABLESPACE temp QUOTA UNLIMITED ON sysaux;Assign Privileges-- Basic connection privileges GRANT CREATE SESSION TO apex_defrag; GRANT CREATE PROCEDURE TO apex_defrag; GRANT CREATE TABLE TO apex_defrag; -- Dictionary access (required for defrag logic) GRANT SELECT ANY DICTIONARY TO apex_defrag; -- Object-level dictionary views (optional but commonly needed) GRANT SELECT ON all_synonyms TO apex_defrag; -- DBA views (required for your APEX defrag queries) GRANT SELECT ON dba_tables TO apex_defrag; GRANT SELECT ON dba_segments TO apex_defrag; GRANT SELECT ON dba_tablespaces TO apex_defrag; -- DML/DDL operations on target tables GRANT ALTER ANY TABLE TO apex_defrag; GRANT SELECT ANY TABLE TO apex_defrag;Create Objects CREATE TABLE apex_defrag.defrag_apex_log ( run_id NUMBER, owner VARCHAR2(128), table_name VARCHAR2(128), action_taken VARCHAR2(50), frag_pct_before NUMBER, reclaimable_mb NUMBER, status VARCHAR2(30), error_msg VARCHAR2(4000), start_time DATE, end_time DATE ); /apex_defrag.defrag_apex_onlineCREATE OR REPLACE PROCEDURE apex_defrag.defrag_apex_online ( p_execute IN BOOLEAN DEFAULT FALSE, p_frag_threshold IN NUMBER DEFAULT 20, p_min_reclaimable IN NUMBER DEFAULT 10 ) AUTHID DEFINER AS v_run_id NUMBER := TO_NUMBER(TO_CHAR(SYSDATE,'YYYYMMDDHH24MISS')); v_count NUMBER := 0; v_errors NUMBER := 0; ------------------------------------------------------------------ CURSOR c_tables IS SELECT t.owner, t.table_name, t.iot_type, t.temporary, t.nested, s.bytes / 1024 / 1024 AS allocated_mb, (NVL(t.num_rows,0) * NVL(t.avg_row_len,0)) / 1024 / 1024 AS actual_mb, GREATEST( (s.bytes - NVL(t.num_rows,0) * NVL(t.avg_row_len,0)) / 1024 / 1024, 0 ) AS reclaimable_mb, ROUND( (s.bytes - NVL(t.num_rows,0) * NVL(t.avg_row_len,0)) / NULLIF(s.bytes,0) * 100, 1 ) AS frag_pct FROM dba_tables t JOIN dba_segments s ON s.owner = t.owner AND s.segment_name = t.table_name AND s.segment_type = 'TABLE' AND t.owner IN ( SELECT DISTINCT table_owner FROM all_synonyms WHERE synonym_name = 'WWV_FLOW' AND owner = 'PUBLIC' ) WHERE t.num_rows > 0 AND s.bytes > p_min_reclaimable * 1024 * 1024 AND ROUND( (s.bytes - NVL(t.num_rows,0) * NVL(t.avg_row_len,0)) / NULLIF(s.bytes,0) * 100, 1 ) >= p_frag_threshold ORDER BY reclaimable_mb DESC; ------------------------------------------------------------------ FUNCTION can_shrink( p_iot_type VARCHAR2, p_temp VARCHAR2, p_nested VARCHAR2 ) RETURN BOOLEAN IS BEGIN IF p_iot_type IS NOT NULL THEN RETURN FALSE; END IF; IF p_temp = 'Y' THEN RETURN FALSE; END IF; IF p_nested = 'YES' THEN RETURN FALSE; END IF; RETURN TRUE; END; ------------------------------------------------------------------ PROCEDURE log_result( p_owner VARCHAR2, p_table VARCHAR2, p_action VARCHAR2, p_frag NUMBER, p_mb NUMBER, p_status VARCHAR2, p_error VARCHAR2 ) IS BEGIN INSERT INTO apex_defrag.defrag_apex_log ( run_id, owner, table_name, action_taken, frag_pct_before, reclaimable_mb, status, error_msg, start_time, end_time ) VALUES ( v_run_id, p_owner, p_table, p_action, p_frag, p_mb, p_status, p_error, SYSDATE, SYSDATE ); END; ------------------------------------------------------------------ PROCEDURE exec_sql(p_sql VARCHAR2) IS BEGIN EXECUTE IMMEDIATE p_sql; EXCEPTION WHEN OTHERS THEN v_errors := v_errors + 1; DBMS_OUTPUT.PUT_LINE('ERROR: ' || SQLERRM); END; BEGIN DBMS_OUTPUT.PUT_LINE('===================================================='); DBMS_OUTPUT.PUT_LINE(' APEX DEFRAg ENGINE (WITH LOGGING)'); DBMS_OUTPUT.PUT_LINE(' RUN ID: ' || v_run_id); DBMS_OUTPUT.PUT_LINE(' MODE : ' || CASE WHEN p_execute THEN 'EXECUTE' ELSE 'REPORT' END); DBMS_OUTPUT.PUT_LINE('===================================================='); ------------------------------------------------------------------ FOR r IN c_tables LOOP v_count := v_count + 1; DBMS_OUTPUT.PUT_LINE( v_count || '. ' || r.owner || '.' || r.table_name || ' | FRAG: ' || r.frag_pct || ' | MB: ' || r.reclaimable_mb ); ------------------------------------------------------------------ -- NOT SHRINKABLE ------------------------------------------------------------------ IF NOT can_shrink(r.iot_type, r.temporary, r.nested) THEN log_result( r.owner, r.table_name, 'SKIP', r.frag_pct, r.reclaimable_mb, 'NOT_ELIGIBLE', NULL ); CONTINUE; END IF; ------------------------------------------------------------------ -- EXECUTE MODE ------------------------------------------------------------------ IF p_execute THEN BEGIN exec_sql( 'ALTER TABLE "'||r.owner||'"."'||r.table_name||'" ENABLE ROW MOVEMENT' ); exec_sql( 'ALTER TABLE "'||r.owner||'"."'||r.table_name||'" SHRINK SPACE CASCADE' ); log_result( r.owner, r.table_name, 'SHRINK', r.frag_pct, r.reclaimable_mb, 'SUCCESS', NULL ); EXCEPTION WHEN OTHERS THEN v_errors := v_errors + 1; log_result( r.owner, r.table_name, 'SHRINK', r.frag_pct, r.reclaimable_mb, 'FAILED', SQLERRM ); END; ELSE log_result( r.owner, r.table_name, 'REPORT', r.frag_pct, r.reclaimable_mb, 'SKIPPED', NULL ); END IF; END LOOP; ------------------------------------------------------------------ DBMS_OUTPUT.PUT_LINE('===================================================='); DBMS_OUTPUT.PUT_LINE(' TOTAL OBJECTS: ' || v_count); DBMS_OUTPUT.PUT_LINE(' ERRORS : ' || v_errors); DBMS_OUTPUT.PUT_LINE('===================================================='); COMMIT ; END; / Compile ALTER PROCEDURE apex_defrag.defrag_apex_online COMPILE; SHOW ERRORS;Run in REPORT mode SET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 BEGIN apex_defrag.defrag_apex_online( p_execute => FALSE, p_frag_threshold => 20, p_min_reclaimable => 10 ); END; /Run in EXECUTE mode (real defrag)SET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 BEGIN apex_defrag.defrag_apex_online( p_execute => TRUE, p_frag_threshold => 20, p_min_reclaimable => 10 ); END; /Example tuned execution (production-safe)SET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 BEGIN apex_defrag.defrag_apex_online( p_execute => TRUE, p_frag_threshold => 30, -- only heavily fragmented tables p_min_reclaimable => 50 -- only large wins ); END; /Example aggressive cleanup runSET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 BEGIN apex_defrag.defrag_apex_online( p_execute => TRUE, p_frag_threshold => 15, p_min_reclaimable => 5 ); END; /Monitor Processing table COLUMN run_id FORMAT 9999999999 HEADING 'RUN ID' COLUMN owner FORMAT A15 HEADING 'OWNER' COLUMN table_name FORMAT A30 HEADING 'TABLE NAME' COLUMN action_taken FORMAT A15 HEADING 'ACTION' COLUMN frag_pct_before FORMAT 9990.0 HEADING 'FRAG %' COLUMN reclaimable_mb FORMAT 999,999,990.00 HEADING 'RECLAIM MB' COLUMN status FORMAT A12 HEADING 'STATUS' COLUMN error_msg FORMAT A40 HEADING 'ERROR MESSAGE' COLUMN start_time FORMAT A19 HEADING 'START TIME' COLUMN end_time FORMAT A19 HEADING 'END TIME' SET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 SELECT -- run_id, owner, table_name, action_taken, frag_pct_before, reclaimable_mb, status, -- SUBSTR(error_msg,1,100) AS error_msg, -- TO_CHAR(start_time,'YYYY-MM-DD HH24:MI:SS') AS start_time, TO_CHAR(end_time,'YYYY-MM-DD HH24:MI:SS') AS end_time FROM apex_defrag.defrag_apex_log ORDER BY run_id DESC;– Track impact via segmentsCOLUMN owner FORMAT A15 HEADING 'OWNER' COLUMN segment_name FORMAT A40 HEADING 'SEGMENT NAME' COLUMN mb FORMAT 999,999,990.00 HEADING 'SIZE (MB)' COLUMN segment_type FORMAT A15 HEADING 'TYPE' SET SERVEROUTPUT ON LINESIZE 200 PAGESIZE 100 WITH data AS ( SELECT owner, segment_type, segment_name, ROUND(bytes / 1024 / 1024,0 ) AS mb FROM dba_segments WHERE owner = 'APEX_240200' ORDER BY bytes DESC ) SELECT owner, segment_type, segment_name, mb FROM DATA WHERE ROWNUM <= 20 ;