最近有朋友問到了DBMS_SPACE包的使用,也看了一下,大部分是關(guān)于dbms_space.space_usage的使用,space_usage這個(gè)過程的例子已經(jīng)很多了,我也就不再多說了,除了這個(gè)過程外,另外還有兩個(gè)過程也有著特殊的用處,但使用的人不多,我們也來看看這兩個(gè)過程有什么用。 這兩個(gè)過程為:CREATE_INDEX_COST和CREATE_TABLE_COST,分別用戶評(píng)估創(chuàng)建索引和創(chuàng)建表的存儲(chǔ)開銷(空間占用情況)。
CREATE_INDEX_COST的語法如下: DBMS_SPACE.CREATE_INDEX_COST ( ddl IN VARCHAR2, used_bytes OUT NUMBER, alloc_bytes OUT NUMBER, plan_table IN VARCHAR2 DEFAULT NULL);
1、準(zhǔn)備相關(guān)表和數(shù)據(jù) SQL> set serveroutput on SQL> create table t(c char(100),d varchar2(200)); 表已創(chuàng)建。
SQL> begin 2 for i in 1..5000 loop 3 insert into t values(i,i); 4 end loop; 5 commit; 6 end; 7 / PL/SQL過程已成功完成。
2、分析表,注意:沒有統(tǒng)計(jì)信息,CREATE_INDEX_COST將無法計(jì)算索引的存儲(chǔ)開銷 SQL> analyze table t compute statistics; 表已分析。
SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(c)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 488.28125 640 --計(jì)算出的索引將占用488K字節(jié)空間,為該索引需要分配640k存儲(chǔ)空間 PL/SQL過程已成功完成。
3、創(chuàng)建實(shí)際索引,確定索引存儲(chǔ)空間是否與計(jì)算的結(jié)果相符 SQL> create index i on t(c); 索引已創(chuàng)建。
SQL> select count(*) from user_extents where segment_name='I'; COUNT(*) ---------- 11 已選擇1行。 --11個(gè)64k的區(qū),比計(jì)算出的大1個(gè)區(qū)
4、再次裝載數(shù)據(jù) SQL> begin 2 for i in 1..5000 loop 3 insert into t values(i,i); 4 end loop; 5 commit; 6 end; 7 / PL/SQL過程已成功完成。
SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(c)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 488.28125 640 --沒有分析之前,獲得得仍然是根據(jù)以前分析結(jié)果計(jì)算的值 PL/SQL過程已成功完成。
SQL> analyze table t compute statistics; 表已分析。
SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(c)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 976.5625 2048 --分析之后,得到新的結(jié)果 PL/SQL過程已成功完成。
5、再次驗(yàn)證,16個(gè)64k的區(qū)和1個(gè)1024k的區(qū),2048k,與估計(jì)值相同 SQL> select count(*) from user_extents where segment_name='I'; COUNT(*) ---------- 17
------------------------------------------------------------- 6、換了一個(gè)字段進(jìn)行測(cè)試 SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(d)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 39.0625 192 --計(jì)算出的索引將占用39K字節(jié)空間,為該索引需要分配192k存儲(chǔ)空間 PL/SQL過程已成功完成。
7、創(chuàng)建索引,新建的索引比估算的值大1個(gè)區(qū) SQL> create index i on t(d); 索引已創(chuàng)建。
SQL> select count(*) from user_extents where segment_name='I'; COUNT(*) ---------- 4
SQL> drop index i;
8、再次裝載數(shù)據(jù)并分析表 SQL> begin 2 for i in 1..10000 loop 3 insert into t values(i,i); 4 end loop; 5 commit; 6 end; 7 / PL/SQL過程已成功完成。
SQL> analyze table t compute statistics; 表已分析。
9、重新計(jì)算,得到新的估算值 SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(d)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 78.125 320
PL/SQL過程已成功完成。
10、創(chuàng)建索引,新建的索引比估計(jì)的大2個(gè)區(qū) SQL> create index i on t(d); 索引已創(chuàng)建。
SQL> select count(*) from user_extents where segment_name='I'; COUNT(*) ---------- 7
11、順便測(cè)試shink space的效果 SQL> select count(*) from t; COUNT(*) ---------- 20000
SQL> delete t where rownum<=15000; 已刪除15000行。
SQL> commit; 提交完成。
SQL> alter table t enable row movement; 表已更改。
12、在刪掉15000行數(shù)據(jù)后,沒有整理空間之前進(jìn)行統(tǒng)計(jì)信息收集 SQL> analyze table t compute statistics; 表已分析。
SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(d)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 24.4140625 128 --基于新收集的統(tǒng)計(jì)信息計(jì)算,估算的索引需要分配128k存儲(chǔ)空間 PL/SQL過程已成功完成。
13、收縮表,釋放占用的存儲(chǔ)空間 SQL> alter table t shrink space; 表已更改。
SQL> analyze table t compute statistics; 表已分析。
SQL> declare 2 v1 number; 3 v2 number; 4 begin 5 DBMS_SPACE.CREATE_INDEX_COST('create index i on t(d)',v1,v2); 6 dbms_output.put_line(v1/1024||' '||v2/1024); 7 end; 8 / 24.4140625 128 --收縮后重新收集統(tǒng)計(jì)信息,與原統(tǒng)計(jì)信息一樣,因此計(jì)算出的大小一樣 PL/SQL過程已成功完成。
SQL> select count(*) from user_extents where segment_name='I'; COUNT(*) ---------- 7 --現(xiàn)有索引并沒有收縮,僅僅是表空間進(jìn)行了收縮,因此現(xiàn)有索引仍保持原大小
14、重建索引,對(duì)比新的索引大小與計(jì)算出的索引大小一樣大 SQL> alter index i rebuild; 索引已更改。
SQL> select count(*) from user_extents where segment_name='I';
COUNT(*) ---------- 2 --重建索引后新的索引占用空間與計(jì)算出的空間一樣大 CREATE_TABLE_COST有兩種用法,因此包內(nèi)進(jìn)行了overload,具體的語法如下: DBMS_SPACE.CREATE_TABLE_COST ( tablespace_name IN VARCHAR2, avg_row_size IN NUMBER, row_count IN NUMBER, pct_free IN NUMBER, used_bytes OUT NUMBER, alloc_bytes OUT NUMBER);
DBMS_SPACE.CREATE_TABLE_COST ( tablespace_name IN VARCHAR2, colinfos IN CREATE_TABLE_COST_COLUMNS, row_count IN NUMBER, pct_free IN NUMBER, used_bytes OUT NUMBER, alloc_bytes OUT NUMBER);
CREATE TYPE create_table_cost_colinfo IS OBJECT ( COL_TYPE VARCHAR(200), COL_SIZE NUMBER);
下面是關(guān)于CREATE_TABLE_COST的測(cè)試代碼: 1、測(cè)試創(chuàng)建一個(gè)表所需的存儲(chǔ)大小,預(yù)計(jì)該表平均行長(zhǎng)度為100字節(jié),10000行數(shù)據(jù) SQL> DECLARE 2 V1 NUMBER; 3 V2 NUMBER; 4 BEGIN 5 DBMS_SPACE.CREATE_TABLE_COST('USERS', 100, 10000, 10, V1, V2); 6 DBMS_OUTPUT.PUT_LINE('V1: '||V1/1024/8||' V2: '||V2/1024/8); 7 END; 8 / V1: 143 V2: 256 --估算出該表需要存儲(chǔ)空間143塊,所需分配空間256塊 PL/SQL過程已成功完成。
2、創(chuàng)建該表,并插入10000行數(shù)據(jù) SQL> CREATE TABLE T1(C CHAR(96)); --96字節(jié)的char字段平均行長(zhǎng)度為100字節(jié) 表已創(chuàng)建。
SQL> BEGIN 2 FOR I IN 1..10000 LOOP 3 INSERT INTO T1 VALUES(I); 4 ENDLOOP; 5 COMMIT; 6 END; 7 / PL/SQL過程已成功完成。
3、分析表統(tǒng)計(jì)信息 SQL> ANALYZE TABLE T1 COMPUTE STATISTICS; 表已分析。
SQL> SELECT BLOCKS,EMPTY_BLOCKS,AVG_ROW_LEN FROM USER_TABLES WHERE TABLE_NAME='T1'; BLOCKS EMPTY_BLOCKS AVG_ROW_LEN ---------- ------------ ----------- 180 76 100 --經(jīng)檢查,高水平線之前的塊數(shù)180塊,高水平線之后的空塊數(shù)76塊,總存儲(chǔ)空間為256塊,與DBMS_SPACE.CREATE_INDEX_COST計(jì)算出的總需要存儲(chǔ)空間大小相符。
4、通過dbms_space.space_usage過程,可以進(jìn)一步看到表中各個(gè)塊的使用情況 declare unf number; unfb number; fs1 number; fs1b number; fs2 number; fs2b number; fs3 number; fs3b number; fs4 number; fs4b number; full number; fullb number; own dba_tables.owner%type; tab dba_tables.table_name%type; yesno varchar2(3); type parts is table of dba_tab_partitions%rowtype; partlist parts; type cursor_ref is ref cursor; c_cur cursor_ref; begin own:=upper('&owner'); tab:=upper('&table_name'); dbms_output.put_line('--------------------------------------------------------------------------------'); open c_cur for select partitioned from dba_tables where wner=own and table_name=tab; fetch c_cur into yesno; close c_cur; dbms_output.put_line('Owner: '||own); dbms_output.put_line('Table: '||tab); dbms_output.put_line('------------------------------------------------'); if yesno='NO' then dbms_space.space_usage(own,tab,'TABLE',unf,unfb,fs1,fs1b,fs2,fs2b,fs3,fs3b,fs4,fs4b,full,fullb); dbms_output.put_line('unf: '||unf||' fs1: '||fs1||' fs2: '||fs2||' fs3: '||fs3||' fs4: '||fs4||' full: '||full); else open c_cur for select * from dba_tab_partitions where table_owner=own and table_name=tab; fetch c_cur bulk collect into partlist; close c_cur; for i in partlist.first .. partlist.last loop dbms_space.space_usage(partlist(i).table_owner,partlist(i).table_name,'TABLE PARTITION',unf,unfb,fs1,fs1b,fs2,fs2b,fs3,fs3b,fs4,fs4b,full,fullb,partlist(i).partition_name); dbms_output.put_line('Partition: '||partlist(i).partition_name); dbms_output.put_line('unf: '||unf||' fs1: '||fs1||' fs2: '||fs2||' fs3: '||fs3||' fs4: '||fs4||' full: '||full); end loop; end if; dbms_output.put_line('--------------------------------------------------------------------------------'); end; /
輸入owner的值: HR 原值 22: own:=upper('&owner'); 新值 22: own:=upper('HR'); 輸入table_name的值: T1 原值 23: tab:=upper('&table_name'); 新值 23: tab:=upper('T1'); -------------------------------------------------------------------------------- Owner: HR Table: T1 ------------------------------------------------ unf: 0 fs1: 1 fs2: 0 fs3: 0 fs4: 39 full: 140 -------------------------------------------------------------------------------- PL/SQL過程已成功完成。 --經(jīng)查看,發(fā)現(xiàn)該表寫滿數(shù)據(jù)的塊有140塊,3/4滿的塊有39塊,1/4滿的塊有1塊,該表存儲(chǔ)空間沒有有效利用,可以看到140+39+1=180,這些均為高水平線之下的塊。但與DBMS_SPACE.CREATE_INDEX_COST計(jì)算出的數(shù)據(jù)需要143塊不符。
5、對(duì)表進(jìn)行空間整理并重新分析 SQL> ALTER TABLE T1 MOVE; 表已更改。
SQL> ANALYZE TABLE T1 COMPUTE STATISTICS; 表已分析。
SQL> SELECT BLOCKS,EMPTY_BLOCKS,AVG_ROW_LEN FROM USER_TABLES WHERE TABLE_NAME='T1'; BLOCKS EMPTY_BLOCKS AVG_ROW_LEN ---------- ------------ ----------- 155 101 100 --經(jīng)檢查,高水平線之前的塊數(shù)155,高水平線之后的空塊數(shù)101,平均行長(zhǎng)度100字節(jié)
6、通過dbms_space.space_usage過程,可以進(jìn)一步看到表中各個(gè)塊的使用情況 declare unf number; unfb number; fs1 number; fs1b number; fs2 number; fs2b number; fs3 number; fs3b number; fs4 number; fs4b number; full number; fullb number; own dba_tables.owner%type; tab dba_tables.table_name%type; yesno varchar2(3); type parts is table of dba_tab_partitions%rowtype; partlist parts; type cursor_ref is ref cursor; c_cur cursor_ref; begin own:=upper('&owner'); tab:=upper('&table_name'); dbms_output.put_line('--------------------------------------------------------------------------------'); open c_cur for select partitioned from dba_tables where wner=own and table_name=tab; fetch c_cur into yesno; close c_cur; dbms_output.put_line('Owner: '||own); dbms_output.put_line('Table: '||tab); dbms_output.put_line('------------------------------------------------'); if yesno='NO' then dbms_space.space_usage(own,tab,'TABLE',unf,unfb,fs1,fs1b,fs2,fs2b,fs3,fs3b,fs4,fs4b,full,fullb); dbms_output.put_line('unf: '||unf||' fs1: '||fs1||' fs2: '||fs2||' fs3: '||fs3||' fs4: '||fs4||' full: '||full); else open c_cur for select * from dba_tab_partitions where table_owner=own and table_name=tab; fetch c_cur bulk collect into partlist; close c_cur; for i in partlist.first .. partlist.last loop dbms_space.space_usage(partlist(i).table_owner,partlist(i).table_name,'TABLE PARTITION',unf,unfb,fs1,fs1b,fs2,fs2b,fs3,fs3b,fs4,fs4b,full,fullb,partlist(i).partition_name); dbms_output.put_line('Partition: '||partlist(i).partition_name); dbms_output.put_line('unf: '||unf||' fs1: '||fs1||' fs2: '||fs2||' fs3: '||fs3||' fs4: '||fs4||' full: '||full); end loop; end if; dbms_output.put_line('--------------------------------------------------------------------------------'); end; / 輸入owner的值: HR 原值 22: own:=upper('&owner'); 新值 22: own:=upper('HR'); 輸入table_name的值: T1 原值 23: tab:=upper('&table_name'); 新值 23: tab:=upper('T1'); -------------------------------------------------------------------------------- Owner: HR Table: T1 ------------------------------------------------ unf: 0 fs1: 0 fs2: 0 fs3: 0 fs4: 0 full: 143 -------------------------------------------------------------------------------- PL/SQL過程已成功完成。 --經(jīng)查看,發(fā)現(xiàn)該表寫滿數(shù)據(jù)的塊有143塊,與DBMS_SPACE.CREATE_INDEX_COST計(jì)算出的數(shù)據(jù)需要塊數(shù)完全相同
-- review the parameters SELECT argument_name, data_type, type_owner, type_name FROM all_arguments WHERE object_name = 'CREATE_TABLE_COST' AND verload = 2
-- examine the input parameter type SELECT text FROM dba_source WHERE name = 'CREATE_TABLE_COST_COLUMNS';
-- drill down further into the input parameter type SELECT text FROM dba_source WHERE name = 'create_table_cost_colinfo';
set serveroutput on DECLARE ub NUMBER; ab NUMBER; cl sys.create_table_cost_columns; BEGIN cl := sys.create_table_cost_columns( sys.create_table_cost_colinfo('NUMBER',10), sys.create_table_cost_colinfo('VARCHAR2',30), sys.create_table_cost_colinfo('VARCHAR2',30), sys.create_table_cost_colinfo('DATE',NULL)); DBMS_SPACE.CREATE_TABLE_COST('SYSTEM',cl,100000,0,ub,ab); DBMS_OUTPUT.PUT_LINE('Used Bytes: ' || TO_CHAR(ub)); DBMS_OUTPUT.PUT_LINE('Alloc Bytes: ' || TO_CHAR(ab)); END; / |
|