Table of contents
Preamble
After a first blog post on when to rebuild or shrink indexes I have naturally decided to write a post on table fragmentation and how to remove it. Maybe I should have started with this one but when we implemented Oracle Disk Manager (ODM) I have got the feedback from applicative team they have multiple times experience a good performance improvement when rebuilding indexes.
It is equally important to defragment tables as it reduces the number of physical reads to put table data blocks in memory. It also decreases the number of blocks to handle in each query (logical reads) as you will also reduce what is called High Water Mark. You increase rows density per blocks as more table figures (rows) will be clubbed in one block.
I have tried to draw few pictures (tolerance requested, done by myself) to visually explain the concept, you will see them in many other blog posts but those two pictures perfectly sum up what we want to achieve.
As a reminder a tablespace is made of multiple datafiles, the object logical storage is called a segment that is made of multiple extents and each extents is made of multiple blocks. In worst case situation you have deleted multiple rows from your table and remaining rows got sparse is a lot of blocks with a low percentage of completion. High Water Mark (HWM) is the last block containing a row of your table. When doing a Full Table Scan (FTS) all blocks till this HWM will be read. If your table blocks are almost empty you easily understand the over-I/O you will do:
The ideal target is to move to a situation where all rows have been condensed (defragmented) in a minimum number of blocks, each being almost full:
Of course if the deleted rows will be soon reinserted then no need to do anything as Oracle will start to insert new rows in not already full blocks before starting to allocate new ones.
This blog post as been written using Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 – 64bit Production running on Red Hat Enterprise Linux Server release 6.5 (Santiago).
Legacy situation
The old school approach is to work with DBA_TABLES and estimate how much space the table is taking and how much optimal space it could take. Current size is number of used blocks multiply by block size to have a size in bytes. Theoretical smaller size is number of rows multiply by average row length. The gain you might get is a simple computation from this two values. Of course you must take into account the value of PCTFREE that is percentage of space reserved in blocks for future updates (mainly to avoid what is called row chaining when a row is spread on more than one block).
Of course if you want to estimate the size of a non yet existing table it does not apply !
The query could looks like:
SELECT a.blocks*b.block_size AS current_size, a.num_rows*a.avg_row_len AS theorical_size, (a.blocks*b.block_size)-(a.num_rows*a.avg_row_len) AS gain, (((a.blocks*b.block_size)-(a.num_rows*a.avg_row_len))*100/(a.blocks*b.block_size)) - a.pct_free AS percentage_gain FROM dba_tables a, dba_tablespaces b WHERE a.tablespace_name=b.tablespace_name AND owner = UPPER('<owner>') AND table_name = UPPER('<table_name>'); |
If you want a better display it can even be put in a PL/SQL block like (inspect_table.sql):
SET linesize 200 pages 1000 SET serveroutput ON SIZE 999999 SET verify off SET feedback off DECLARE vcurrent_size NUMBER; vtheorical_size NUMBER; vgain NUMBER; vpercentage_gain NUMBER; FUNCTION format_size(value1 IN NUMBER) RETURN VARCHAR2 AS BEGIN CASE WHEN (value1>1024*1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024*1024),'999,999.999') || 'GB'); WHEN (value1>1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024),'999,999.999') || 'MB'); WHEN (value1>1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024),'999,999.999') || 'KB'); ELSE RETURN LTRIM(TO_CHAR(value1,'999,999.999') || 'B'); END CASE; END format_size; BEGIN SELECT a.blocks*b.block_size, a.num_rows*a.avg_row_len, (a.blocks*b.block_size)-(a.num_rows*a.avg_row_len), (((a.blocks*b.block_size)-(a.num_rows*a.avg_row_len))*100/(a.blocks*b.block_size)) - a.pct_free INTO vcurrent_size, vtheorical_size, vgain, vpercentage_gain FROM dba_tables a, dba_tablespaces b WHERE a.tablespace_name=b.tablespace_name AND owner = UPPER('&1.') AND table_name = UPPER('&2.'); dbms_output.put_line('For table ' || UPPER('&1.') || '.' || UPPER('&2.')); dbms_output.put_line('Current table size: ' || format_size(vcurrent_size)); dbms_output.put_line('Theoretical table size: ' || format_size(vtheorical_size)); dbms_output.put_line('Potential saving: ' || format_size(vgain)); dbms_output.put_line('Potential saving percentage: ' || ROUND(vpercentage_gain, 2) || '%'); END; / SET feedback ON |
For one of my test tables it gives:
SQL> @inspect_table <owner> <table_name> FOR TABLE owner.table_name CURRENT TABLE SIZE: 57.031MB Theoretical TABLE SIZE: 179.000B Potential saving: 57.031MB Potential saving percentage: 90% |
So my table is currently using around 57MB and I could ideally make it fitting in 179 bytes so one block at the end (that’s why here the computation is not accurate). But here we do not take into account extent management of tablespaces and so obviously the gain will not be that big !
Newest methods to estimate tables size
Not like for indexes here you cannot use EXPLAIN PLAN for create table statement because mainly Oracle cannot guess how many rows you will insert. Since Oracle 10gR1, same as for indexes, you can now DBMS_SPACE.CREATE_TABLE_COST procedures (because two version exist). When calling them you specify target number of row for future tables or real number of rows for existing tables.
As just written there are two versions of DBMS_SPACE.CREATE_TABLE_COST. One where you specify average row length (so for existing table) and one where you give the data type and length of every columns (so apply for soon to be created tables). I have tried both on an existing table and it provided same result, the second form of the procedure is a bit more complex to handle as you must build a variable with a special type (CREATE_TABLE_COST_COLUMNS) which describe all columns. Here is the small PL/SQL block I have written (create_table_cost.sql):
SET linesize 200 pages 1000 SET serveroutput ON SIZE 999999 SET verify off SET feedback off DECLARE vtablespace_name dba_tables.tablespace_name%TYPE; vavg_row_len dba_tables.avg_row_len%TYPE; vnum_rows dba_tables.num_rows%TYPE; vpct_free dba_tables.pct_free%TYPE; used_bytes NUMBER; alloc_bytes NUMBER; CURSOR cursor1 IS SELECT data_type, data_length FROM dba_tab_columns WHERE owner = UPPER('&1.') AND table_name = UPPER('&2.') ORDER BY column_id; columns1 sys.create_table_cost_columns:=sys.create_table_cost_columns(); i NUMBER:=0; TYPE collection1 IS TABLE OF cursor1%ROWTYPE INDEX BY PLS_INTEGER; item1 collection1; FUNCTION format_size(value1 IN NUMBER) RETURN VARCHAR2 AS BEGIN CASE WHEN (value1>1024*1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024*1024),'999,999.999') || 'GB'); WHEN (value1>1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024),'999,999.999') || 'MB'); WHEN (value1>1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024),'999,999.999') || 'KB'); ELSE RETURN LTRIM(TO_CHAR(value1,'999,999.999') || 'B'); END CASE; END format_size; BEGIN SELECT tablespace_name, avg_row_len, num_rows, pct_free INTO vtablespace_name, vavg_row_len, vnum_rows, vpct_free FROM dba_tables WHERE owner = UPPER('&1.') AND table_name = UPPER('&2.'); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('------------ DBMS_SPACE.CREATE_TABLE_COST version 1 ------------'); dbms_output.put_line('----------------------------------------------------------------'); dbms_space.create_table_cost(vtablespace_name, vavg_row_len, vnum_rows, vpct_free, used_bytes, alloc_bytes); dbms_output.put_line('Used: ' || format_size(used_bytes)); dbms_output.put_line('Allocated: ' || format_size(alloc_bytes)); OPEN cursor1; FETCH cursor1 BULK COLLECT INTO item1; FOR i IN item1.FIRST..item1.LAST LOOP columns1.extend; columns1(i):=sys.create_table_cost_colinfo(item1(i).data_type, item1(i).data_length); END LOOP; CLOSE cursor1; dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('------------ DBMS_SPACE.CREATE_TABLE_COST version 2 ------------'); dbms_output.put_line('----------------------------------------------------------------'); dbms_space.create_table_cost(vtablespace_name, columns1, vnum_rows, vpct_free, used_bytes, alloc_bytes); dbms_output.put_line('Used: ' || format_size(used_bytes)); dbms_output.put_line('Allocated: ' || format_size(alloc_bytes)); END; / SET feedback ON |
With my test table it gives:
SQL> @create_table_cost <owner> <tavle name> ---------------------------------------------------------------- ------------ DBMS_SPACE.CREATE_TABLE_COST version 1 ------------ ---------------------------------------------------------------- Used: 8.000KB Allocated: 64.000KB ---------------------------------------------------------------- ------------ DBMS_SPACE.CREATE_TABLE_COST version 2 ------------ ---------------------------------------------------------------- Used: 8.000KB Allocated: 64.000KB |
So the procedure handle minimum first extent size of 64KB of my 8KB block size and EXTENT MANAGEMENT LOCAL AUTOALLOCATE and SEGMENT SPACE MANAGEMENT AUTO. Despite what Oracle is claiming I see no difference between the two versions of the procedure. If we check current used space (DBA_EXTENTS):
SQL> SELECT bytes, blocks,COUNT(*) 2 FROM dba_extents 3 WHERE owner = UPPER('<owner>') 4 AND segment_name = UPPER('<table_name>') 5 GROUP BY bytes, blocks 6 ORDER BY blocks; BYTES BLOCKS COUNT(*) ---------- ---------- ---------- 65536 8 16 1048576 128 57 2 ROWS selected. |
Table fragmentation identification
Once we have the estimated size (whatever the method) of the table we can compare it with its actual size and see how much we might gain. To compute the current size of an existing table (of course) we have multiple methods:
- DBMS_SPACE.SPACE_USAGE procedure
- DBA_SEGMENTS view
- DBA_TABLES view
From my testing DBMS_SPACE.SPACE_USAGE is giving exact same result as the query we have seen on DBA_TABLES with a bit more insight on block completion. So the small PL/SQL blocks I have written is not using DBA_TABLES (table_saving.sql):
SET linesize 200 pages 1000 SET serveroutput ON SIZE 999999 SET verify off SET feedback off DECLARE unformatted_blocks NUMBER; unformatted_bytes NUMBER; fs1_blocks NUMBER; fs1_bytes NUMBER; fs2_blocks NUMBER; fs2_bytes NUMBER; fs3_blocks NUMBER; fs3_bytes NUMBER; fs4_blocks NUMBER; fs4_bytes NUMBER; full_blocks NUMBER; full_bytes NUMBER; dbms_space_bytes NUMBER; bytes_dba_segments NUMBER; vtablespace_name dba_tables.tablespace_name%TYPE; vavg_row_len dba_tables.avg_row_len%TYPE; vnum_rows dba_tables.num_rows%TYPE; vpct_free dba_tables.pct_free%TYPE; used_bytes NUMBER; alloc_bytes NUMBER; FUNCTION format_size(value1 IN NUMBER) RETURN VARCHAR2 AS BEGIN CASE WHEN (value1>1024*1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024*1024),'999,999.999') || 'GB'); WHEN (value1>1024*1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024*1024),'999,999.999') || 'MB'); WHEN (value1>1024) THEN RETURN LTRIM(TO_CHAR(value1/(1024),'999,999.999') || 'KB'); ELSE RETURN LTRIM(TO_CHAR(value1,'999,999.999') || 'B'); END CASE; END format_size; BEGIN SELECT tablespace_name, avg_row_len, num_rows, pct_free INTO vtablespace_name, vavg_row_len, vnum_rows, vpct_free FROM dba_tables WHERE owner = UPPER('&1.') AND table_name = UPPER('&2.'); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('Analyzing table &1..&2.'); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('-------------------- DBMS_SPACE.SPACE_USAGE --------------------'); dbms_output.put_line('----------------------------------------------------------------'); dbms_space.space_usage(UPPER('&1.'), UPPER('&2.'), 'TABLE', unformatted_blocks, unformatted_bytes, fs1_blocks, fs1_bytes, fs2_blocks, fs2_bytes, fs3_blocks, fs3_bytes, fs4_blocks, fs4_bytes, full_blocks, full_bytes); dbms_output.put_line('Total number of blocks unformatted :' || unformatted_blocks); --dbms_output.put_line('Total number of bytes unformatted: ' || unformatted_bytes); dbms_output.put_line('Number of blocks having at least 0 to 25% free space: ' || fs1_blocks); --dbms_output.put_line('Number of bytes having at least 0 to 25% free space: ' || fs1_bytes); dbms_output.put_line('Number of blocks having at least 25 to 50% free space: ' || fs2_blocks); --dbms_output.put_line('Number of bytes having at least 25 to 50% free space: ' || fs2_bytes); dbms_output.put_line('Number of blocks having at least 50 to 75% free space: ' || fs3_blocks); --dbms_output.put_line('Number of bytes having at least 50 to 75% free space: ' || fs3_bytes); dbms_output.put_line('Number of blocks having at least 75 to 100% free space: ' || fs4_blocks); --dbms_output.put_line('Number of bytes having at least 75 to 100% free space: ' || fs4_bytes); dbms_output.put_line('The number of blocks full in the segment: ' || full_blocks); --dbms_output.put_line('Total number of bytes full in the segment: ' || format_size(full_bytes)); dbms_space_bytes:=unformatted_bytes+fs1_bytes+fs2_bytes+fs3_bytes+fs4_bytes+full_bytes; dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('------------------------- DBA_SEGMENTS -------------------------'); dbms_output.put_line('----------------------------------------------------------------'); SELECT bytes INTO bytes_dba_segments FROM dba_segments WHERE owner=UPPER('&1.') AND segment_name=UPPER('&2.'); dbms_output.put_line('Size of the segment: ' || format_size(bytes_dba_segments)); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('----------------- DBMS_SPACE.CREATE_TABLE_COST -----------------'); dbms_output.put_line('----------------------------------------------------------------'); dbms_space.create_table_cost(vtablespace_name, vavg_row_len, vnum_rows, vpct_free, used_bytes, alloc_bytes); dbms_output.put_line('Used: ' || format_size(used_bytes)); dbms_output.put_line('Allocated: ' || format_size(alloc_bytes)); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('---------------------------- Results ---------------------------'); dbms_output.put_line('----------------------------------------------------------------'); dbms_output.put_line('Potential percentage gain (DBMS_SPACE): ' || ROUND(100 * (dbms_space_bytes - alloc_bytes) / dbms_space_bytes) || '%'); dbms_output.put_line('Potential percentage gain (DBA_SEGMENTS): ' || ROUND(100 * (bytes_dba_segments - alloc_bytes) / bytes_dba_segments) || '%'); END; / SET feedback ON |
On my test table it gives:
SQL> @table_saving <owner> <table_name> ---------------------------------------------------------------- Analyzing TABLE <owner>.<table_name> ---------------------------------------------------------------- -------------------- DBMS_SPACE.SPACE_USAGE -------------------- ---------------------------------------------------------------- Total NUMBER OF blocks unformatted :0 NUMBER OF blocks HAVING AT LEAST 0 TO 25% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 25 TO 50% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 50 TO 75% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 75 TO 100% free SPACE: 7300 The NUMBER OF blocks full IN the SEGMENT: 0 ---------------------------------------------------------------- ------------------------- DBA_SEGMENTS ------------------------- ---------------------------------------------------------------- SIZE OF the SEGMENT: 58.000MB ---------------------------------------------------------------- ----------------- DBMS_SPACE.CREATE_TABLE_COST ----------------- ---------------------------------------------------------------- Used: 8.000KB Allocated: 64.000KB ---------------------------------------------------------------- ---------------------------- Results --------------------------- ---------------------------------------------------------------- Potential percentage gain (DBMS_SPACE): 100% Potential percentage gain (DBA_SEGMENTS): 100% |
Only with DBMS_SPACE.SPACE_USAGE you already know that the potential for storage saving is huge because my table is made of 7300 blocks which are all not more than 25% full…
You can even create a procedure based on above PL/SQL block, I have chosen to use DBMS_SPACE.SPACE_USAGE (table_saving_function.sql):
CREATE OR REPLACE FUNCTION table_saving_function(vtable_owner IN VARCHAR2, vtable_name IN VARCHAR2) RETURN NUMBER AUTHID current_user AS vtablespace_name dba_tables.tablespace_name%TYPE; vavg_row_len dba_tables.avg_row_len%TYPE; vnum_rows dba_tables.num_rows%TYPE; vpct_free dba_tables.pct_free%TYPE; unformatted_blocks NUMBER; unformatted_bytes NUMBER; fs1_blocks NUMBER; fs1_bytes NUMBER; fs2_blocks NUMBER; fs2_bytes NUMBER; fs3_blocks NUMBER; fs3_bytes NUMBER; fs4_blocks NUMBER; fs4_bytes NUMBER; full_blocks NUMBER; full_bytes NUMBER; dbms_space_bytes NUMBER; used_bytes NUMBER; alloc_bytes NUMBER; BEGIN SELECT tablespace_name, avg_row_len, num_rows, pct_free INTO vtablespace_name, vavg_row_len, vnum_rows, vpct_free FROM dba_tables WHERE owner = UPPER(vtable_owner) AND table_name = UPPER(vtable_name); dbms_space.space_usage(UPPER(vtable_owner), UPPER(vtable_name), 'TABLE', unformatted_blocks, unformatted_bytes, fs1_blocks, fs1_bytes, fs2_blocks, fs2_bytes, fs3_blocks, fs3_bytes, fs4_blocks, fs4_bytes, full_blocks, full_bytes); dbms_space_bytes:=unformatted_bytes+fs1_bytes+fs2_bytes+fs3_bytes+fs4_bytes+full_bytes; IF (vavg_row_len > 0 AND vnum_rows > 0) THEN dbms_space.create_table_cost(vtablespace_name, vavg_row_len, vnum_rows, vpct_free, used_bytes, alloc_bytes); IF (dbms_space_bytes <> 0) THEN RETURN (100 * (dbms_space_bytes - alloc_bytes) / dbms_space_bytes); ELSE RETURN 0; END IF; ELSE RETURN 0; END IF; END; / |
Then with a query like this you can find the best candidates to work on (this is by the way how I have found the example of this blog post):
SELECT a.owner,a.table_name,table_saving_function(a.owner,a.table_name) AS percentage_gain FROM dba_tables a WHERE a.owner='<owner>' AND a.status='VALID' --In valid state AND a.iot_type IS NULL -- IOT tables not supported by dbms_space --and external='no' starting from 12cr2 AND NOT EXISTS (SELECT 'x' FROM dba_external_tables b WHERE b.owner=a.owner AND b.table_name=a.table_name) AND TEMPORARY='N' --Temporary segment not supported AND a.last_analyzed IS NOT NULL --Recently analyzed ORDER BY 3 DESC; |
Move, shrink or export/import ?
We have three options in our hands to defragment tables:
- Alter table move (to another tablespace, or same tablespace) and rebuild indexes. You obviously need extra space in tablespace to use it. Using ONLINE keyword in Enterprise edition you have no lock and DML are still possible.
- Export and import the table. Needless to say the downtime is big and is difficult to get on a production database. Not the option I would choose…
- Shrink command available starting with Oracle 10gR1. Usable on segments in tablespaces with automatic segment management and when row movement has been activated.
So the method to target is ALTER TABLE
Same as for indexes COMPACT option has poor interest:
If you specify COMPACT, then Oracle Database only defragments the segment space and compacts the table rows for subsequent release. The database does not readjust the high water mark and does not release the space immediately. You must issue another ALTER TABLE … SHRINK SPACE statement later to complete the operation. This clause is useful if you want to accomplish the shrink operation in two shorter steps rather than one longer step.
Let’s try with my test table:
SQL> ALTER TABLE <owner>.<table_name> shrink SPACE; Error starting AT line : 1 IN command - ALTER TABLE <owner>.<table_name> shrink SPACE Error report - ORA-10636: ROW MOVEMENT IS NOT enabled SQL> ALTER TABLE <owner>.<table_name> enable ROW movement; TABLE <owner>.<table_name> altered. SQL> ALTER TABLE <owner>.<table_name> shrink SPACE; TABLE <owner>.<table_name> altered. SQL> EXEC dbms_stats.gather_table_stats('<owner>','<table_name>'); PL/SQL PROCEDURE successfully completed. SQL> @table_saving <owner> <table_name> ---------------------------------------------------------------- Analyzing TABLE <owner>.<table_name> ---------------------------------------------------------------- -------------------- DBMS_SPACE.SPACE_USAGE -------------------- ---------------------------------------------------------------- Total NUMBER OF blocks unformatted :0 NUMBER OF blocks HAVING AT LEAST 0 TO 25% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 25 TO 50% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 50 TO 75% free SPACE: 0 NUMBER OF blocks HAVING AT LEAST 75 TO 100% free SPACE: 1 The NUMBER OF blocks full IN the SEGMENT: 0 ---------------------------------------------------------------- ------------------------- DBA_SEGMENTS ------------------------- ---------------------------------------------------------------- SIZE OF the SEGMENT: 64.000KB ---------------------------------------------------------------- ----------------- DBMS_SPACE.CREATE_TABLE_COST ----------------- ---------------------------------------------------------------- Used: 8.000KB Allocated: 64.000KB ---------------------------------------------------------------- ---------------------------- Results --------------------------- ---------------------------------------------------------------- Potential percentage gain (DBMS_SPACE): -700% Potential percentage gain (DBA_SEGMENTS): 0% SQL> @create_table_cost <owner> <table_name> ---------------------------------------------------------------- ------------ DBMS_SPACE.CREATE_TABLE_COST version 1 ------------ ---------------------------------------------------------------- Used: 8.000KB Allocated: 64.000KB ---------------------------------------------------------------- ------------ DBMS_SPACE.CREATE_TABLE_COST version 2 ------------ ---------------------------------------------------------------- Used: 16.000KB Allocated: 64.000KB SQL> @inspect_table <owner> <table_name> FOR TABLE <owner>.<table_name> CURRENT TABLE SIZE: 8.000KB Theoretical TABLE SIZE: 1.090KB Potential saving: 6.910KB Potential saving percentage: 76.38% SQL> SELECT bytes, blocks,COUNT(*) 2 FROM dba_extents 3 WHERE owner = UPPER('<owner>') 4 AND segment_name = UPPER('<table_name>') 5 GROUP BY bytes, blocks 6 ORDER BY blocks; BYTES BLOCKS COUNT(*) ---------- ---------- ---------- 65536 8 1 1 ROW selected. |
As expected the table is now fitting in one block. But still one extent of 64KB has been allocated to store it. HWM has been reduced so no real need to find something better. Maybe my PL/SQL should be modified to avoid reporting negative percentage gain and just report 0 for no gain…
References
- How to Estimate the Size of Tables and Indexes Before Being Created and Populated in the Database? (Doc ID 1585326.1)
- Managing Fragmentation and Table Shrink in Oracle Database
- Fragmentation 3
- Reorganizing Tables in Oracle – is it worth the effort?
venkat says:
Queries are not working. Getting error like below.
DECLARE
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 17
And does this work for partitioned table.
Yannick Jaquier says:
As already commented the script is not (yet) working for partitioned tables…
Tony says:
Hi,
Thank you for this article. Really helps me to understand better how to calculate the real size of one table. No I am monitoring the ALTER TABLE SHRINK COMPACT of one huge table by 1350 Gb after my customer has removed rows, since I couldn’t take another method to release the space on tablespace. Started 29 hours ago is still running, the script based on dbms_space.space_usage (”, ”, ‘TABLE’, v_unformatted_blocks,v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes); returns unchanged results for last 2 hours but the session is still active. I do not know how could I monitor what is doing.
Regards,
Tony
Yannick Jaquier says:
Hi,
Thanks for comment !
Wow 1.3 TB !! I would check in v$session_longops and v$active_session_history…
Rupdipt says:
Hi Yannick
The script for table fragmentation identification is not working for any partitioned table. Getting the below error.
ERROR at line 1:
ORA-14107: partition specification is required for a partitioned object
ORA-06512: at “SYS.DBMS_SPACE”, line 214
ORA-06512: at line 43
Can you check and advice.
Mike says:
*Cause: This procedure can be used only on segments in tablespaces with AUTO SEGMENT SPACE MANAGEMENT
@Yannick can we used function on tablespaces without AUTO SEGMENT SPACE MANAGEMENT?
Yannick Jaquier says:
@Rupdipt: Yes partitioned table are not (yet) taken into account. Will try to free a bit of time to work on it…
@Mike: Yes should work with MANUAL segment management but why would you still use this obsolete approach ?
Mamta Chawla says:
Hi Yannick,
Thanks for the script. I am not able to run it, as value1 is empty. what is value1 here??
Regards
Mamta Chawla
Yannick Jaquier says:
Hi Mamta,
Welcome! I’m not getting your question: value1 is the parameter of the function to convert in GB, MB and so on…
Thanks,
Yannick.
Mamta Chawla says:
Hi Yannick,
SELECT bytes INTO bytes_dba_segments FROM dba_segments WHERE owner=UPPER(‘&1.’) AND segment_name=UPPER(‘&2.’);
was giving me the error…I corrected it…as
SELECT bytes INTO bytes_dba_segments FROM dba_segments WHERE owner=UPPER(‘&1.’) AND segment_name=UPPER(‘&2.’) and tablespace_name=’DATA’
Regards
Mamta Chawla
Yannick Jaquier says:
Hi Mamta,
If you do this then you do not take partitioned objects into consideration…
Thanks,
Yannick.