they only question that I can answer definitively is q3)
q3) of course not. one of three things will happen after any change. 1) it will not affect performance at all. 2) things will go faster. 3) things will go slower.
ok, maybe two of these
q2) no, it does not. It might make somethings GROW in size. Yes, you read that right - you could take a segment - reorganize it and discover that it is larger afterwards. Why? Because you might take a very densely packed table or index - which is very full - reorganize it and re-introduce the pctfree - making the segment larger than it used to be.
q3) one thing you might do is search this site for maxshrink and see if you can shrink existing datafiles (if you are on 10g, you might want to purge your recycle bin first! but make sure you know what that means...)
q1) there is no best way
if there were a single best way, we would not have invented or implemented the others.
you would have to go segment by segment in these tablespaces to see the 'percentage of free space' to see if they are candidates for 'shrinking' or not.
You could use your knowledge - why do you think you will get lots of free space back? If you mostly insert and update - you won't. If you insert just a little more than you delete - you will not.
You can either use the segment advisor in Enterprise manager to find segments that might be candidates for a 'reorg'
Or you can use show_space to see how "compact" your segments are:
set define off
create or replace procedure show_space
( p_segname in varchar2,
p_owner in varchar2 default user,
p_type in varchar2 default 'TABLE',
p_partition in varchar2 default NULL )
-- this procedure uses authid current user so it can query DBA_*
-- views using privileges from a ROLE and so it can be installed
-- once per database, instead of once per user that wanted to use it
authid current_user
as
l_free_blks number;
l_total_blocks number;
l_total_bytes number;
l_unused_blocks number;
l_unused_bytes number;
l_LastUsedExtFileId number;
l_LastUsedExtBlockId number;
l_LAST_USED_BLOCK number;
l_segment_space_mgmt varchar2(255);
l_unformatted_blocks number;
l_unformatted_bytes number;
l_fs1_blocks number; l_fs1_bytes number;
l_fs2_blocks number; l_fs2_bytes number;
l_fs3_blocks number; l_fs3_bytes number;
l_fs4_blocks number; l_fs4_bytes number;
l_full_blocks number; l_full_bytes number;
-- inline procedure to print out numbers nicely formatted
-- with a simple label
procedure p( p_label in varchar2, p_num in number )
is
begin
dbms_output.put_line( rpad(p_label,40,'.') ||
to_char(p_num,'999,999,999,999') );
end;
begin
-- this query is executed dynamically in order to allow this procedure
-- to be created by a user who has access to DBA_SEGMENTS/TABLESPACES
-- via a role as is customary.
-- NOTE: at runtime, the invoker MUST have access to these two
-- views!
-- this query determines if the object is a ASSM object or not
begin
execute immediate
'select ts.segment_space_management
from dba_segments seg, dba_tablespaces ts
where seg.segment_name = :p_segname
and (:p_partition is null or
seg.partition_name = :p_partition)
and seg.owner = :p_owner
and seg.tablespace_name = ts.tablespace_name'
into l_segment_space_mgmt
using p_segname, p_partition, p_partition, p_owner;
exception
when too_many_rows then
dbms_output.put_line
( 'This must be a partitioned table, use p_partition => ');
return;
end;
-- if the object is in an ASSM tablespace, we must use this API
-- call to get space information, else we use the FREE_BLOCKS
-- API for the user managed segments
if l_segment_space_mgmt = 'AUTO'
then
dbms_space.space_usage
( p_owner, p_segname, p_type, l_unformatted_blocks,
l_unformatted_bytes, l_fs1_blocks, l_fs1_bytes,
l_fs2_blocks, l_fs2_bytes, l_fs3_blocks, l_fs3_bytes,
l_fs4_blocks, l_fs4_bytes, l_full_blocks, l_full_bytes, p_partition);
p( 'Unformatted Blocks ', l_unformatted_blocks );
p( 'FS1 Blocks (0-25) ', l_fs1_blocks );
p( 'FS2 Blocks (25-50) ', l_fs2_blocks );
p( 'FS3 Blocks (50-75) ', l_fs3_blocks );
p( 'FS4 Blocks (75-100)', l_fs4_blocks );
p( 'Full Blocks ', l_full_blocks );
else
dbms_space.free_blocks(
segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
freelist_group_id => 0,
free_blks => l_free_blks);
p( 'Free Blocks', l_free_blks );
end if;
-- and then the unused space API call to get the rest of the
-- information
dbms_space.unused_space
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
partition_name => p_partition,
total_blocks => l_total_blocks,
total_bytes => l_total_bytes,
unused_blocks => l_unused_blocks,
unused_bytes => l_unused_bytes,
LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,
LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,
LAST_USED_BLOCK => l_LAST_USED_BLOCK );
p( 'Total Blocks', l_total_blocks );
p( 'Total Bytes', l_total_bytes );
p( 'Total MBytes', trunc(l_total_bytes/1024/1024) );
p( 'Unused Blocks', l_unused_blocks );
p( 'Unused Bytes', l_unused_bytes );
p( 'Last Used Ext FileId', l_LastUsedExtFileId );
p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
p( 'Last Used Block', l_LAST_USED_BLOCK );
end;
/
set define on
now if the goal is to ultimately shrink datafiles - there is no 'best way' to do that in place.
If you used alter table t shrink space/shrink space compact - you'll reorganize the tables, but you'll not free up space at the end of the datafile, rather you'll take a datafile that looked like this (each 'word' is an extent in the following string - from the first extent of a datafile to the last)
t1,t1,t2,t3,t2,t3,t1,t4,t4,t4,t5,t6,f,f,f
so, that file has extents for tables T1-T6 and three free extents at the end (assuming locally managed uniform for ease of example) If you resized the file, you could make it three extents smaller. Now, lets assume you shrank each table - and each table fit in one extent, then:
t1,f,t2,t3,f,f,f,t4,f,f,t5,t6,f,f,f
You now have nine free extents in the file, but if you shrank the file, you would only still get three - since t6 is "at the end"
So, what you would need to do to reclaim "as much as you can" would be to MOVE the segements to a new, empty tablespace - that would pack them densely and allow you to DROP the old tablespace.
The odds that this exercise is even worth considering (the entire "lets get disk space back") are so so so so very close to 0% that I would strongly encourage you to find something else to do :)
Think about it - for how many hours or days would this space be 'reclaimed' and freed up before the database just wanted it back again.