The short answer is No. Unfortunately the way to do this in Oracle does require the "big stack of boring queries". The articles you linked to are some of the best information available on the subject. The datafile does indeed become fragmented, so that even if free space exists below the highest segment, Oracle will not automatically consolidate it when a RESIZE
is done.
To "defragment" the tablespace you need to move these segments to the start of the datafile rather than at the end. For tables this is an offline process meaning the table will be unavailable while the move is taking place. Indexes can be moved either offline or with Enterprise Edition they can be moved online. Since you have an outage window I recommend you follow these steps.
A. Shrink datafiles with free space beyond the high water mark. This can be done as follows (the query is similar to Frosty Z's procedure):
SELECT ceil( blocks*(a.BlockSize)/1024/1024) "Current Size",
ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Smallest Poss.",
ceil( blocks*(a.BlockSize)/1024/1024) -
ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Savings",
'alter database datafile '''|| file_name || ''' resize ' ||
ceil((nvl(hwm,1)*(a.BlockSize))/1024/1024/100)*100 || 'm;' "Command"
FROM (SELECT a.*, p.value BlockSize FROM dba_data_files a
JOIN v$parameter p ON p.Name='db_block_size') a
LEFT JOIN (SELECT file_id, max(block_id+blocks-1) hwm FROM dba_extents GROUP BY file_id ) b
ON a.file_id = b.file_id
WHERE ceil( blocks*(a.BlockSize)/1024/1024) - ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 )
> 100 /* Minimum MB it must shrink by to be considered. */
ORDER BY "Savings" Desc;
B. After shrinking things above the high water mark, find out what tablespaces would still benefit from having segments moved.
SELECT DISTINCT tablespace_name FROM
(
SELECT tablespace_name, block_id + blocks LastBlock,
lead(block_id) OVER (PARTITION BY File_ID
ORDER BY tablespace_name, file_id, block_id) NextBlock
FROM dba_free_space
) WHERE LastBlock <> NextBlock AND NextBlock IS NOT NULL;
C. For each of these tablespaces determine which segments need to be moved. (Replace USERS with the name of your tablespace or join it with the previous query)
SELECT distinct de.segment_name
FROM dba_extents de
JOIN
(
SELECT tablespace_name, file_id, MIN(block_id) LowestFreeBlock
FROM dba_free_space
WHERE tablespace_name = 'USERS'
GROUP BY tablespace_name, file_id
) dfs ON dfs.tablespace_name = de.tablespace_name AND dfs.file_id = de.file_id
WHERE de.tablespace_name = 'USERS'
AND de.block_id > dfs.LowestFreeBlock;
D. Move each table and rebuild the indexes and statistics.
E. Repeat step A.
I just built most of these queries, so you will want to thoroughly test them before use. I suppose you could create a procedure that would use EXECUTE IMMEDIATE
to create the actual statements to run dynamically, but because queries will receive ORA-08103: Object no longer exists while the move is in progress, I think it is best to control that process manually even if it does mean a bit more time/effort.
In general, extents are allocated in a round-robin fashion among all data files in a tablespace so long as those data files have enough free space to allocate the extent. So if you add a new data file, you would generally expect that half of the new extents would be allocated in the existing data file and half of the new extents would be allocated in the new data file. Assuming the two data files are the same size, you would generally expect that by the time the first data file got to 100% full, the second data file would be roughly 50% full.
I'm not sure, though, how this helps you deal with increasing tablespace size without wasting physical space. I'm hard-pressed to imagine in what circumstances knowing how extents are going to be allocated is going to help you use less physical space. It shouldn't matter from that perspective whether you double the size of the existing data file, add a new data file, or add multiple data files and it shouldn't matter whether one data file is filled up before the next one starts being used. In any case, you've allocated the same amount of space at the operating system level, you've allocated the same number of extents in the tablespace, and your segments are all the same size.
You would decrease the amount of space allocated at the operating system level by letting the data files autoextend rather than allocating fixed size data files but then you have to monitor the free space at the operating system level to ensure that the data files all have enough space to grow and you may complicate your ability to move data files to different mountpoints. You may also decrease the amount of parallelism you can get in your backups forcing the backup job to run a bit longer.
Best Answer
Extents will spread evenly: