-- 用于打印数据库段的空间利用率信息
-- 
-- P_SEGNAME：段名（例如，表或者索引名称）
-- P_OWNER 默认为当前用户，不过也可以使用这个存储过程来查看另外某个用户
-- P_TYPE 默认为TABLE，这个参数表示查看哪种类型的对象
-- P_PARTITION 显示分区对象的空间时所用的分区名，SHOW_SPACE一次只显示一个分区的空间利用率


--

-- 此脚本执行完之后可能会遇到ORA-00942: table or view does not exist 的错误，这个错误就是因为部分表没有权限的缘故

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
)
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;

    -- 内部存储过程，用于格式化数字
    -- 
    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 
    begin 
        execute immediate 
        -- 此部分执行出现表不存在的错误时可以是因为没有权限造成的，赋予select权限即可
        '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 partitiond table,use p_partition =>' );
                return;
            -- 解决没有数据时报错的问题
            WHEN NO_DATA_FOUND THEN  
                dbms_output.put_line( 'no data found =>' );
                return;
    end;

    -- api

    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( 'FS1 Blocks (25-50)',l_fs2_blocks );
        p( 'FS1 Blocks (50-75)',l_fs3_blocks );
        p( 'FS1 Blocks (75-100)',l_fs4_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;

    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 Blocks',l_LastUsedExtBlockId );
    p( 'Last Used Block',l_LAST_USED_BLOCK );
end;
/
