課程目標
完成本課程的學習后,您應該能夠:
子游標 child cursor ---后續運行的SQL 。
父游標的關鍵信息是SQL語句的文本。
子游標的關鍵是執行計划和執行環境。

sqlplus test/test drop table t_cursor purge; create table t_cursor as select * from dba_objects; alter system flush shared_pool;

select count(*) from t_cursor; select count(*) from T_cursor; col sql_text format a40; select sql_id, sql_text, executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%'; alter system flush shared_pool; select sql_id, sql_text, executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%';

select count(*) from t_cursor; select count(*) from t_cursor; select sql_id, sql_text, executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%'; alter system flush shared_pool;

--父子游標 alter session set optimizer_mode = all_rows; select count(*) from t_cursor; select sql_id, sql_text,child_number,executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%'; alter session set optimizer_mode = first_rows_10; select count(*) from t_cursor; select sql_id, sql_text,child_number,executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%'; grant select any dictionary to scott; sqlplus scott/tiger drop table t_cursor purge; create table t_cursor as select * from user_objects; col sql_text format a40; select count(*) from t_cursor; select sql_id, sql_text,child_number, executions from v$sql where sql_text like '%select count(*) from%_cursor%' and sql_text not like '%sql_text%';
Desc V$SQL_SHARED_CURSOR:
字段意思參考幫助文檔:Reference/V$SQL_SHARED_CURSOR
Select * from V$SQL_SHARED_CURSOR;
邏輯優化:這個階段,通過應用各種不同的轉換技巧,會生成語義上等同的新的SQL語句。
物理優化:首先會生成與每個邏輯優化產生的SQL語句有關的執行計划。接着,根據數據字典找到的統計信息或動態取樣收集的統計信息,計算出一個與各個執行計划相關的開銷。最后,擁有最低開銷的執行計划會被選中。
1.3硬解析步驟
1.) 對SQL語句進行語法檢查,看是否有語法錯誤。如果存在語法錯誤,則退出解析過程;
2.) 通過數據字典(row cache),檢查SQL語句中涉及的對象和列是否存在。如果不存在,則退出解析過程;
3.)檢查SQL語句的用戶是否對涉及到的對象是否有權限。沒有則退出解析;
4.)通過優化器創建一個最優的執行計划。這個過程會根據數據字典中的對象的統計信息,來計算多個執行計划的cost,從而得到一個最優的執行計划。
這一步涉及到大量的數據運算,從而會消耗大量的CPU資源;(library cache最主要的目的就是通過軟解析來減少這個步驟);
5.)將該游標所產生的執行計划,SQL文本等裝載進library cache中的heap中。
1.4軟解析、軟軟解析
軟解析: 所謂軟解析,就是因為相同文本的SQL語句存在於library cache中,所以本次SQL語句的解析就可以去掉硬解析中的多個步驟。從而節省大量的資源的 耗費。
軟軟解析:所謂的軟軟解析,就是沒有任何解析過程。當設置了session_cached_cursors參數時,當某個session第三次執行相同的SQL語句時,則會把該SQL語句的游標緩存到PGA中。這樣,當該session在執行該SQL語句時,會直接從PGA中取出執行計划,從而跳過解析的所有步驟。

drop table test purge; alter system flush shared_pool; create table test as select * from dba_objects where 1<>1; exec dbms_stats.gather_table_stats(user,'test'); select * from test where object_id=20; select * from test where object_id=30; select * from test where object_id=40; select * from test where object_id=50; var oid number; exec :oid:=20; select * from test where object_id=:oid; exec :oid:=30; select * from test where object_id=:oid; exec :oid:=40; select * from test where object_id=:oid; exec :oid:=50; select * from test where object_id=:oid; begin for i in 1..4 loop execute immediate 'select * from test where object_id=:i' using i; end loop; end; / select sql_text,s.PARSE_CALLS,loads,executions from v$sql s where sql_text like 'select * from test where object_id%' order by 1,2,3,4;
2.綁定變量
2.1變量綁定的目的
變量綁定的目的是把硬解析變成軟解析:
a.減少SQL硬解析的次數。
b.減少系統資源開銷。
c.減少latch爭用。
綁定變量也有冬天:bind peeking
bind peeking的由來:在執行含有綁定變量的查詢語句時,完成解析和最優化操作之后才能對綁定變量進行綁定,這意味着在實現最優化操作時無法使用綁定變 量列的統計信息。
為了解決這個問題,數據庫使用了窺探技術,在第一次解析SQL時,按照窺探變量的值生成執行計划,以后這樣的SQL都按照這個執行。
隱藏參數_optim_peek_user_binds=true則啟用綁定變量窺探,否則CBO認為統計列是均勻的。
由綁定窺探你想到了什么?

drop table test purge; create table test as select * from dba_objects; create index ind_object_id on TEST (object_id); exec dbms_stats.gather_table_stats(user,'test',cascade=>true); set autotrace traceonly select count(object_name) from test where object_id < 10; select count(object_name) from test where object_id < 1000000; set autotrace off alter session set statistics_level=all; alter system flush shared_pool; var ccc number; exec :ccc:=10; select count(object_name) from test where object_id < :ccc; select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); exec :ccc:=1000000; select count(object_name) from test where object_id < :ccc; select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); alter system flush shared_pool; exec :ccc:=1000000; select count(object_name) from test where object_id < :ccc; select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); exec :ccc:=10; select count(object_name) from test where object_id < :ccc; select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
2.2變量綁定的應用場景
OLTP環境應該使用綁定變量:

---OLTP環境模擬 drop table t purge; create table t (name varchar2(20),department_id number(10)); begin for i in 1 .. 10000 loop insert into t values ('test'||i,i); end loop; commit; end; / select * from t; ------------以上為創造實驗環境-------------- ---如果我們要將test1、test2兩個用戶轉部門,那么產生的sql應該是以下兩條內容 select department_id from t where name = 'test1' update t set department_id=1000 where name ='test1'; commit; select department_id from t where name = 'test2' update t set department_id=1000 where name ='test2'; commit; ---如果使用綁定變量,那么sql應該是以下內容 select department_id from t where name = :x; update t set department_id=1000 where name =:x; ---通過綁定變量,我們將兩次硬解析變成了一次硬解析 ---接下來我們看綁定變量與不綁定變量的性能區別 alter session set tracefile_identifier = 'Look_For_Me'; alter session set events '10046 trace name context forever,level 12'; begin ---不綁定 for x in 1..10000 loop execute immediate 'select * from t where name ='||x; end loop; end; / begin ---綁定 for x in 1..10000 loop execute immediate 'select * from t where name =:x' using x; end loop; end; / alter session set events '10046 trace name context off';
OLAP環境不應該使用綁定變量:
OLAP環境大多時候是報表處理,操作的結果集龐大,並且數據往往不均勻,在這種情況下,謂詞條件不同,獲取到的數據量也不一定相同,執行計划就不一定相同,這個時候,變量就不那么合適了
在OLAP系統中,根據不同謂詞條件選擇不同的執行計划是至關重要的,這時候,解析的代價和大查詢的代價比較起來,sql解析消耗的資源是可以忽略不計的
用戶量較少、執行次數少。
2.3游標共享(cursor_sharing)
cursor sharing用來告訴oracle什么情況下可以共享游標,即SQL重用。oracle默認cursor_sharing 是exact 指的是SQL語句必須絕對一樣的情況下才能共享游標,否則作為新的SQL語句處理。
適合場景:使用exact 高效的前提是應用代碼中使用了綁定變量,這也是oracle推薦的。

Drop table t purge; show parameter cursor_sharing; create table t as select * from dba_objects; alter system flush shared_pool; select /*+test_exact*/count(1) from t where object_id=100; select /*+test_exact*/count(1) from t where object_id=200; col sql_text format a80; select sql_text from v$sql where sql_text like '%/*+test_exact*/%' and sql_text not like '%sql_text%'; alter system flush shared_pool; var x number; exec :x:=100; select /*+test_exact*/count(1) from t where object_id=:x; exec :x:=200; select /*+test_exact*/count(1) from t where object_id=:x; select sql_text from v$sql where sql_text like '%/*+test_exact*/%' and sql_text not like '%sql_text%';
cursor sharing=force,oracle將2條類似的SQL的謂詞用一個變量代替,同時將它們看做同一條SQL語句處理
適用場景:在無法將應用的代碼修改為綁定變量情況下,oracle提供的一種解決方法。
注意:游標共享特性有個不太好的名聲,就是它不是很穩定,過去數年,大量與之相關的bug被發現和確認。

Drop table t purge; show parameter cursor_sharing; create table t as select * from dba_objects; alter session set cursor_sharing=force; alter system flush shared_pool; select /*+test_force*/count(1) from t where object_id=100; select /*+test_force*/count(1) from t where object_id=100; select /*+test_force*/count(1) from t where object_id=200; col sql_text format a80; select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_force*/%' and sql_text not like '%sql_text%' And sql_text not like '%EXPLAIN PLAN%';
cursor_sharing=similar ,exact和force折中的參數。當探測到謂詞會導致執行計划的改變,就會重新解析,否則不會。
適用場景:在無法將應用的代碼修改為綁定變量情況下,oracle提供的又一種解決方法,similar的好處是最大限度地避免CBO在綁定變量的情況下做出的錯誤的判斷,但它的代價是分析的次數變大。

Drop table t purge; show parameter cursor_sharing; create table t as select * from dba_objects; Create index ind_t_object_id on t(object_id) nologging; exec dbms_stats.gather_table_stats(user,'t',cascade => true); alter session set cursor_sharing=similar; Set autotrace traceonly select /*+test_similar*/* from t where object_id<10; select /*+test_similar*/* from t where object_id<100000; Set autotrace off alter system flush shared_pool; Set autotrace traceonly select /*+test_similar*/* from t where object_id<10; select /*+test_similar*/* from t where object_id<100000; Set autotrace off col sql_text format a80; select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_similar*/%' and sql_text not like '%sql_text%' And sql_text not like '%EXPLAIN PLAN%'; alter system flush shared_pool; Set autotrace traceonly select /*+test_similar*/* from t where object_name='aaa'; select /*+test_similar*/* from t where object_name='bbb'; select /*+test_similar*/* from t where object_name='ccc'; select /*+test_similar*/* from t where object_name='ddd'; Set autotrace off select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_similar*/%' and sql_text not like '%sql_text%' And sql_text not like '%EXPLAIN PLAN%';