我們的技術專家回答關於游標范圍(extent)和間隔的問題 是不是從Oracle
第
版以後的版本
隱式游標得到了優化
不會兩次取數據?還有
為什麼當表T在列X上有一個索引時下面的隱式游標比顯式游標運行得更快
而沒有索引時是顯式游標運行得較快呢?
Implicit Cursor:
Select x
into y
from T
where x = j;
Explicit Cursor:
cursor c(p number) is
select x from blah where x = p;
open c(j);
fetch c into y;
close c;
為了讓每個人都了解顯式游標和隱式游標是什麼
我先簡單介紹一下它們的定義
通常
隱式游標是指程序員並不
顯式
聲明
打開
從中取數據和關閉的那些游標
這些操作都是隱式的
因此
在上面的例子中
SELECT X INTO Y查詢就是一個隱式游標
對於它來說並沒有
cursor cursor_name is
這樣的定義語句
相反
第二個例子是典型的顯式關標
程序員顯式地聲明
打開
取數據和關閉它
在PL/SQL中隱式游標比顯式游標運行得更快是一個事實
在Oracle
版之前的版本中就是這樣
事實上
我在Oracle
版中就測試過這樣的情況並得到了同樣的結論(這些測試請參見/~tkyte/l)
隱式游標運行得更快的原因(FOR LOOP隱式游標和SELECT INTO隱式游標)是PL/SQL引擎只需要解釋和執行很少的代碼
一般來說
PL/SQL引擎在後台做的越多
程序就運行地越快
上面的隱式游標只使用了一行PL/SQL代碼
顯式游標至少使用了三行代碼
如果要
正確地
運行
實際上要使用
行代碼
你的顯式代碼並不像隱式游標那樣運行
它要確保你得到一條且只得到一條記錄
你的顯式代碼缺少了許多你要做的工作
為了精確地比較你的兩個游標例子
你的顯式代碼應該被擴展出以下幾行
open c(j);
fetch c into y;
if ( c%notfound ) then raise NO_DATA_FOUND;
end if;
fetch c into y;
if ( c%found ) then raise TOO_MANY_ROWS;
end if;
close c;
如果這就是你的顯式游標
你會發現在所有情況下顯式游標都運行得比較慢
甚至於無論你的例子中有沒有索引都是這樣
那麼
你的問題的症結所在是
為什麼在你的例子中沒有索引時
隱式游標好像運行地非常慢
然而當存在一個索引的時候
隱式游標卻運行得較快呢?答案在於全表掃描
事實上在得到一條記錄後
你的顯式測試就停止了
我將給出一個例子來向你展示它們之間的不同之處
SQL> create table t ( x int )
pctfree
pctused
;
Table created
SQL> insert into t
select rownum
from all_objects;
rows created
SQL> analyze table t compute statistics;
Table analyzed
SQL> select blocks
empty_blocks
num_rows
from user_tables
where table_name =
T
;
BLOCKS EMPTY_BLOCKS NUM_ROWS
我創建了一個有許多數據塊的表
值pctfree
為隨後更新數據保留了
%的塊作為
空閒空間
因此
即使表中的數據量很小
表本身也相當大
接著
我通過INSERT把值
一直到
嚴格按順序插入到表中
因此
X=
在該表的
第一個
塊中而X=
在表中相當接近表的最後一個塊
接下來
我將運行一個小PL/SQL塊
它會顯示各種隱式和顯式游標對數據進行一致讀的次數
因為沒有索引
查詢將對整個表進行全面掃描
一旦我運行這個程序然後評審查詢結果
將很容易對性能的差異進行量化
SQL> declare
l_last_cgets number default
;
l_x number;
cursor c( p_x in number ) is
select x
from t
where x = p_x;
procedure cgets( p_msg in varchar
)
is
l_value number;
begin
select b
value into l_value
from v$statname a
v$mystat b
where a
statistic# = b
statistic#
and a
name =
consistent gets
;
dbms_output
put_line( p_msg );
dbms_output
put_line
(
Incremental cgets:
||
to_char(l_value
l_last_cgets
) );
l_last_cgets := l_value;
end;
begin
cgets(
Starting
);
open c(
);
fetch c into l_x;
close c;
cgets(
Explicit to find X=
||
stop at first hit
);
open c(
);
fetch c into l_x;
fetch c into l_x;
close c;
cgets(
Explicit to find X=
||
check for dups
);
select x into l_x
from t
where x =
AND rownum =
;
cgets(
Implicit to find X=
||
stop at first hit
);
select x into l_x
from t
where x =
;
cgets(
Implicit to find X=
||
check for dups
);
open c(
);
fetch c into l_x;
close c;
cgets(
Explicit to find X=
);
select x into l_x
from t
where x =
;
cgets(
Implicit to find X=
);
end;
/
Starting
Incremental cgets:
Explicit to find X=
stop at first hit
Incremental cgets: &nb
sp;
Explicit to find X=
check for dups
Incremental cgets:
Implicit to find X=
stop at first hit
Incremental cgets:
Implicit to find X=
check for dups
Incremental cgets:
Explicit to find X=
Incremental cgets:
Implicit to find X=
Incremental cgets:
PL/SQL procedure successfully completed
現在你就可以明白在你的例子中為什麼顯式游標好像比隱式游標運行得更快了
當我使用顯式游標進行測試的時候
只取一次數據X=
為了找到答案
查詢只需要掃描非常少的塊(很少的一致的讀次數)
然而
只要我使顯式游標來進行隱式游標的工作
檢查確保沒有其他記錄滿足同一條件
你就會看到顯式游標檢查表中的每一個塊
現在
我接著說隱式游標
通過使用ROWNUM=
看看它是否也會在找到第一條符合條件的記錄時停下來
它和顯式游標做相同的工作量
當它檢查表中的第二行是否符合條件時
你會看到它同顯式游標一樣進行相同次數的一致讀
它也不得不對表進行全面掃描以核定只有一行X=
最有趣的是當我查詢X=
的時候
因為那行接近表的
結尾
所以無論我采用什麼方法
兩個查詢的工作量都差不多
為了找到滿足條件的第一行它們都必須掃描幾乎整個表
現在
如果在X上有一個索引
兩個查詢都會使用索引范圍掃描
而且兩個查詢都不必對表進行全面掃描
便能快速地發現只有一行滿足條件
這就解釋了你的游標行為
SELECT INTO檢查第二行
但顯式游標卻不這麼做
如果你對應地進行比較
第二次顯式地取數據或者把
rownum =
添加到SELECT INTO語句中--你就會發現兩個游標的工作量相同
簡而言之
隱式游標更好
它們比使用顯式游標的相同代碼運行地更快
更容易編碼(需要鍵入的代碼更少)
而且我個人認為使用隱士游標的代碼更容易讀也更容易理解
小中和大 在我們的新應用程序中
我們設計了數據庫並創建了數據模型
甚至還估計了表的大小並為每個標指定了存儲參數
但現在我們的數據庫管理員告訴我們將給我們三個表空間
范圍大小統一為
K的TS_small表空間
范圍大小統一為
MB的TS_med表空間和范圍大小統一為
MB的TS_large表空間
他們告訴我們在TS_small中創建小於
MB的表
在TS_med中創建小於
MB的表
在TS_large中創建大於
MB的表
另外
他們不希望我們對表使用任何存儲參數
對索引也是這樣
這好像並不合理
因為對於一個預計大小為
MB的表
我們應把它放在TS_med中
接下來如果我們在那個表空間中創建它
它會占
個范圍!數據庫管理員聲稱許多測試已經證明這種設計提供了最佳的性能並可以防止碎片
我的問題是
他們說的對嗎?我擔心對象會有太多的范圍
看來他們已經讀過asktom Web站點()和互聯網討論組的相關內容
並發現了好的建議
從他們的數字看
我注意到他們允許一個表占用的最大空間是
GB
可以有
個或更少的范圍
假設上百個(或者上千個)范圍不會影響運行時數據操縱語言(DML)的性能
我會說他們做得非常好
他們的前提都是正確的
From:http://tw.wingwit.com/Article/program/Oracle/201311/16936.html