T
The space on free OCI can sometimes be a bit limiting, especially if you're using table partitions. Even an empty partition consumes 8 MB, and if you have partitions per tenant in every table — and over 100 tables — it can quickly grow with each new tenant.
So here is the script which helps you to remove empty partitions and also rename the partitions so you can easily identify specific tenants:
DECLARE in_table_like CONSTANT VARCHAR2(128) := '%'; in_partition_prefix CONSTANT VARCHAR2(128) := '__P'; -- v_found PLS_INTEGER; v_high_value VARCHAR2(4000); v_new_name VARCHAR2(128); BEGIN FOR c IN ( SELECT t.table_name, t.partition_name, t.high_value FROM user_tab_partitions t WHERE t.table_name LIKE in_table_like ORDER BY 1, 2 ) LOOP v_found := NULL; v_high_value := c.high_value; -- LONG conversion -- IF v_high_value IN ('DEFAULT', 'MAXVALUE') THEN CONTINUE; END IF; -- calculate number of rows in partition EXECUTE IMMEDIATE 'SELECT MAX(1) FROM ' || c.table_name || ' PARTITION (' || c.partition_name || ') WHERE ROWNUM = 1' INTO v_found; -- delete empty partition IF v_found IS NULL THEN EXECUTE IMMEDIATE 'ALTER TABLE ' || c.table_name || ' DROP PARTITION ' || c.partition_name || ' UPDATE INDEXES'; -- CONTINUE; END IF; -- rename partition v_new_name := c.table_name || in_partition_prefix || v_high_value; -- IF c.partition_name != v_new_name THEN EXECUTE IMMEDIATE 'ALTER TABLE ' || c.table_name || ' RENAME PARTITION ' || c.partition_name || ' TO ' || v_new_name; END IF; END LOOP; END; /
Here is a simple query to see your state:
SELECT t.table_name, t.partition_name, t.high_value FROM user_tab_partitions t WHERE t.table_name LIKE '%' ORDER BY 1, 2;
And here is a more complex query where you can see more details (and number of rows in each partition):
WITH p AS ( SELECT p.*, -- 'SELECT p.high_value' || CHR(10) || 'FROM user_tab_partitions p' || CHR(10) || 'WHERE p.table_name = ''' || p.table_name || '''' || CHR(10) || ' AND p.partition_name = ''' || p.partition_name || '''' AS query_ FROM user_tab_partitions p ) SELECT p.table_name, p.partition_position AS position, p.partition_name, p.high_value, -- LTRIM(REGEXP_SUBSTR(r.high_value, '[^,' || ']+', 1, 1)) AS header_1, LTRIM(REGEXP_SUBSTR(r.high_value, '[^,' || ']+', 1, 2)) AS header_2, LTRIM(REGEXP_SUBSTR(r.high_value, '[^,' || ']+', 1, 3)) AS header_3, LTRIM(REGEXP_SUBSTR(r.high_value, '[^,' || ']+', 1, 4)) AS header_4, -- TO_NUMBER(EXTRACTVALUE(XMLTYPE(DBMS_XMLGEN.GETXML( 'SELECT /*+ PARALLEL(p,4) */ COUNT(*) AS r ' || 'FROM ' || p.table_name || ' PARTITION (' || p.partition_name || ') p' )), '/ROWSET/ROW/R')) AS count_rows, -- p.subpartition_count AS subpartitions, p.read_only -- FROM p JOIN ( SELECT p.table_name, p.partition_name, LTRIM(RTRIM(h.high_value, ' )'), '( ') AS high_value FROM p, -- trick to convert LONG to VARCHAR2 on the fly XMLTABLE('/ROWSET/ROW' PASSING (DBMS_XMLGEN.GETXMLTYPE(p.query_)) COLUMNS high_value VARCHAR2(4000) PATH 'HIGH_VALUE' ) h ) r ON r.table_name = p.table_name AND r.partition_name = p.partition_name ORDER BY 1, 2;
Comments
Post a Comment