數(shù)據(jù)庫精華備份

計算階乘
select rownum rn, dbms_aw.eval_number(replace(wmsys.wm_concat(rownum)over(order by rownum), ',', '*')) serial_multi
      from dual connect by rownum <= 12

http://www.itpub.net/thread-1393352-1-1.html
hi,看到你關(guān)于數(shù)據(jù)庫存儲過程的一些討論,以下是我《面向程序員的數(shù)據(jù)庫訪問性能優(yōu)化法則》http://blog.csdn.net/yzsind/archive/2010/12/06/6059209.aspx
里的一段話。

3.4、使用存儲過程
大型數(shù)據(jù)庫一般都支持存儲過程,合理的利用存儲過程也可以提高系統(tǒng)性能。如你有一個業(yè)務(wù)需要將A表的數(shù)據(jù)做一些加工然后更新到B表中,但是又不可能一條SQL完成,這時你需要如下3步操作:

a:將A表數(shù)據(jù)全部取出到客戶端;

b:計算出要更新的數(shù)據(jù);

c:將計算結(jié)果更新到B表。

如果采用存儲過程你可以將整個業(yè)務(wù)邏輯封裝在存儲過程里,然后在客戶端直接調(diào)用存儲過程處理,這樣可以減少網(wǎng)絡(luò)交互的成本。

當(dāng)然,存儲過程也并不是十全十美,存儲過程有以下缺點(diǎn):

a、不可移植性,每種數(shù)據(jù)庫的內(nèi)部編程語法都不太相同,當(dāng)你的系統(tǒng)需要兼容多種數(shù)據(jù)庫時最好不要用存儲過程。

b、學(xué)習(xí)成本高,DBA一般都擅長寫存儲過程,但并不是每個程序員都能寫好存儲過程,除非你的團(tuán)隊有較多的開發(fā)人員熟悉寫存儲過程,否則后期系統(tǒng)維護(hù)會產(chǎn)生問題。

c、業(yè)務(wù)邏輯多處存在,采用存儲過程后也就意味著你的系統(tǒng)有一些業(yè)務(wù)邏輯不是在應(yīng)用程序里處理,這種架構(gòu)會增加一些系統(tǒng)維護(hù)和調(diào)試成本。

d、存儲過程和常用應(yīng)用程序語言不一樣,它支持的函數(shù)及語法有可能不能滿足需求,有些邏輯就只能通過應(yīng)用程序處理。

e、如果存儲過程中有復(fù)雜運(yùn)算的話,會增加一些數(shù)據(jù)庫服務(wù)端的處理成本,對于集中式數(shù)據(jù)庫可能會導(dǎo)致系統(tǒng)可擴(kuò)展性問題。

f、為了提高性能,數(shù)據(jù)庫會把存儲過程代碼編譯成中間運(yùn)行代碼(類似于java的class文件),所以更像靜態(tài)語言。當(dāng)存儲過程引用的對像(表、視圖等等)結(jié)構(gòu)改變后,存儲過程需要重新編譯才能生效,在24*7高并發(fā)應(yīng)用場景,一般都是在線變更結(jié)構(gòu)的,所以在變更的瞬間要同時編譯存儲過程,這可能會導(dǎo)致數(shù)據(jù)庫瞬間壓力上升引起故障(Oracle數(shù)據(jù)庫就存在這樣的問題)。

個人觀點(diǎn):普通業(yè)務(wù)邏輯盡量不要使用存儲過程,定時性的ETL任務(wù)或報表統(tǒng)計函數(shù)可以根據(jù)團(tuán)隊資源情況采用存儲過程處理。


我的觀點(diǎn)在很多帖子里都談過了,現(xiàn)在只不過老調(diào)重彈:

a.每種數(shù)據(jù)庫的實現(xiàn)原理都很不一樣,單單是鎖的處理就大不相同。因此,并不是棄用存儲過程,你的應(yīng)用就可以兼容多種數(shù)據(jù)庫。這是個不現(xiàn)實的目標(biāo)。你仍然要針對不同的數(shù)據(jù)庫寫不同的SQL和代碼。相反,為不同數(shù)據(jù)庫寫不同的存儲過程,反而使得你的應(yīng)用可以更好地兼容。

b.每種語言都有學(xué)習(xí)成本,并不是PLSQL就特別難學(xué)。DBA也不一定擅長寫存儲過程。應(yīng)該鼓勵程序員學(xué)習(xí)數(shù)據(jù)庫知識和存儲過程編程。如果使用ORACLE數(shù)據(jù)庫,團(tuán)隊中就必須有熟悉PLSQL和SQL的人才。

c.業(yè)務(wù)邏輯應(yīng)該集中,我完全同意這一觀點(diǎn)。但我認(rèn)為業(yè)務(wù)邏輯應(yīng)該在存儲過程中處理。這種架構(gòu)的統(tǒng)維護(hù)和調(diào)試成本并不會更高。你采用任何語言來實現(xiàn)業(yè)務(wù)邏輯,都有統(tǒng)維護(hù)和調(diào)試成本。才用存儲過程之后,數(shù)據(jù)處理隔離在數(shù)據(jù)庫中,調(diào)試和調(diào)優(yōu)更加方便。

d.ORACLE發(fā)展到現(xiàn)在,系統(tǒng)提供的包已經(jīng)很全面,沒有什么做不到的。“有些邏輯就只能通過應(yīng)用程序處理”請舉例。

e."如果存儲過程中有復(fù)雜運(yùn)算的話,會增加一些數(shù)據(jù)庫服務(wù)端的處理成本"
請舉例。大多數(shù)業(yè)務(wù)都是表連接和四則運(yùn)算而已,沒有什么復(fù)雜的科學(xué)運(yùn)算。

f.靜態(tài)代碼正是存儲過程的優(yōu)點(diǎn)。很多錯誤在編譯的時候就可以發(fā)現(xiàn)。如果采用其他語言,所有SQL都是動態(tài)的,有些錯誤必須到運(yùn)行時候才能發(fā)現(xiàn)。現(xiàn)實中凡是變更結(jié)構(gòu),就意味著變更代碼,你的業(yè)務(wù)處理模塊一定要變動,不管是否用存儲過程。因此變更結(jié)構(gòu)的時候你必須停止業(yè)務(wù)。否則你的業(yè)務(wù)將無法產(chǎn)生正確的數(shù)據(jù),或者兩個版本的數(shù)據(jù)同時存在,這都是很不利的。

ORACLE 11GR2已經(jīng)支持Edition-Based Redefinition, 請看TOM的系列文章:
http://www.oracle.com/technetwor ... 0asktom-172777.html
http://www.oracle.com/technetwor ... 0asktom-098897.html
http://www.oracle.com/technetwor ... 0asktom-082672.html

(文章翻譯:http://www.itpub.net/thread-1396183-1-1.html)

我的觀點(diǎn)恰恰相反:普通業(yè)務(wù)邏輯應(yīng)該盡量使用存儲過程。


1.把存儲過程做成調(diào)用接口包。
2.為了更方便不同客戶端調(diào)用,可以用表函數(shù)作為訪問方式(以自治事務(wù)進(jìn)行DML,用表函數(shù)返回狀態(tài)信息)
3.存儲過程里面用動態(tài)sql(execute immediate,dbms_sql),通過訪問元數(shù)據(jù)表讀取業(yè)務(wù)規(guī)則,實現(xiàn)訪問控制。即業(yè)務(wù)邏輯并不在存儲過程中,而在元數(shù)據(jù)表中。
4.通過維護(hù)元數(shù)據(jù)表來進(jìn)行業(yè)務(wù)擴(kuò)展、業(yè)務(wù)調(diào)整、業(yè)務(wù)作廢工作。元數(shù)據(jù)表加上機(jī)構(gòu)級別、業(yè)務(wù)起始終止時間、自定義限制等,實現(xiàn)生命周期及細(xì)粒度控制。
5.在此基礎(chǔ)上,將開發(fā)團(tuán)隊分為3個組:數(shù)據(jù)處理組、工具組、業(yè)務(wù)定制組,以簡馭繁,對項目需求進(jìn)行分解,實現(xiàn)極限編程。
已經(jīng)在3個以上項目中按此方式實施。如有興趣請加我msn:dayspring_chen@yahoo.com.cn

我很不贊同你的一些做法。
2.為了更方便不同客戶端調(diào)用,可以用表函數(shù)作為訪問方式(以自治事務(wù)進(jìn)行DML,用表函數(shù)返回狀態(tài)信息)
為什么用表函數(shù)就更方便?
你全都是用表函數(shù)返回嵌套表,然后用SELECT...FROM TABLE(...)? 這樣有什么好處?
如果要返回結(jié)果集,可以打開一個REF CURSOR并返回。
自治事務(wù)要特別小心。基本上,除了寫日志,其他的應(yīng)用都是錯的。你這樣做,將不得不把COMMIT放到你的函數(shù)里。這是不提倡的,COMMIT最好交給客戶端去處理。另外你的函數(shù)將很難模塊化,因為每個都是不同的事務(wù)!比如函數(shù)A調(diào)用函數(shù)B, A和B應(yīng)該在同一個事務(wù)里,你如果在A,B里用了自治事務(wù),全亂套了。
3.存儲過程里面用動態(tài)sql.....
動態(tài)SQL是應(yīng)該盡力避免的。它的應(yīng)用范圍及其有限,一般是為了組合靈活的查詢條件。如果你把事務(wù)處理也用動態(tài)SQL實現(xiàn),一定有問題。
動態(tài)SQL程序很難讀,很難維護(hù),無法在編譯的時候發(fā)現(xiàn)語法問題;因為多隔了一層,效率也要打個折扣。
你可以把業(yè)務(wù)邏輯中一些靈活的部分抽出來進(jìn)行參數(shù)化,也就是通過配置的方式來進(jìn)行控制,但是“執(zhí)行動作”應(yīng)該都是靜態(tài)代碼,它只是根據(jù)不同的配置走不同的分支。
如果你的需求決定了有很多雷同的代碼,那么你可以利用你的元數(shù)據(jù)來生成靜態(tài)代碼,這樣節(jié)省了人工,但是最終編譯、執(zhí)行的代碼還是靜態(tài)的。
4.通過維護(hù)元數(shù)據(jù)表來進(jìn)行....
如果元數(shù)據(jù)表是我上述提到的配置表,這樣的做法是可取的。 |


| 眾所周知PLSQL程序具有依賴性,一旦依賴對象被修改程序就會失效,需要重編譯,這也是很多人反對使用存儲過程的理由,比如這個:
http://www.itpub.net/thread-1393352-1-1.html

現(xiàn)在這個問題已經(jīng)被ORACLE完美解決了,你用PLSQL開發(fā)的應(yīng)用可以實現(xiàn)無縫的平滑過渡。總的來說有這么幾點(diǎn):
1. 你可以在當(dāng)前庫創(chuàng)建一個新的版本號,這個新版本在發(fā)布之前對其他會話是不可見的,你可以隨意修改編譯你的PLSQL程序而不會影響到正在運(yùn)行的舊版本應(yīng)用。
2. 讓你的應(yīng)用訪問版本視圖,而不是直接訪問表,這樣你可以修改表的結(jié)構(gòu),而舊版本程序看到的還是原來的結(jié)構(gòu),修改對它沒有任何影響。
3. ORACLE新提供的dbms_parallel_execute讓你可以把一個大型DML劃分成小片執(zhí)行,從而實現(xiàn)逐步的數(shù)據(jù)遷移,而不會一下子鎖住太多的數(shù)據(jù)。
4. 新的版本觸發(fā)器使得你可以在新舊版本之間互相傳遞數(shù)據(jù),這樣兩個版本的應(yīng)用程序可以同時運(yùn)行。

以下是TOM在ORACLE雜志寫的系列專欄,介紹這一奇妙的新功能:

11gR2 新特性:在線版本變更(Edition-Based Redefinition, 直譯為“基于版本的重定義”)
By Tom Kyte

第一部分:
http://www.Oracle.com/technetwor ... 9asktom-089919.html

節(jié)譯:

在線版本變更,在我看來,是Oracle數(shù)據(jù)庫11gR2版的殺手級新功能。它使得我們能夠去掉計劃中不得已的最后一點(diǎn)停機(jī)時間。

過去發(fā)行的Oracle數(shù)據(jù)庫已經(jīng)讓我們能夠做很多在線操作,比如:修改大部分參數(shù),創(chuàng)建索引,在線重新定義、組織任何對象結(jié)構(gòu),甚至數(shù)據(jù)庫升級。唯一必須真正停機(jī),在一段時間內(nèi)切斷對數(shù)據(jù)庫的訪問的時候,是在應(yīng)用程序升級期間。這是因為我們必須花時間進(jìn)行PL/SQL單元的重編譯,重新創(chuàng)建視圖,修改授權(quán),等等。這些變更通常要求沒有人在執(zhí)行我們的PL/SQL單元,使用我們的視圖,等等。但這已成為歷史。有了Oracle數(shù)據(jù)庫11gR2版,我們現(xiàn)在能夠在數(shù)據(jù)庫中創(chuàng)建不同版本的“新命名空間”。

一個新的版本是作為當(dāng)前版本的子版本創(chuàng)建的,它繼承了父版本的所有“狀態(tài)”:所有的PL/SQL代碼,視圖,同義詞,等等。當(dāng)一個子版本首次被創(chuàng)建,它是父版本的一個鏡像,但這個鏡像是可以修改的。例如,你可以在你當(dāng)前的生產(chǎn)庫代碼基礎(chǔ)上創(chuàng)建新版本,在這個新版本中,執(zhí)行CREATE OR REPLACE PROCEDURE P指令,在新版本中覆蓋存儲過程P。而當(dāng)前的版本,即你的最終用戶所見的那個版本,具有存儲過程P的舊版,你的用戶將不受新版本的影響。你可以在新版本中安裝新版本的存儲過程P并且重新編譯那些失效的代碼,不會對當(dāng)前版本有任何影響。當(dāng)你確認(rèn)你在新版本中的所有修改都已經(jīng)是正確和完整的,你可以把這個新版本公開發(fā)布,新代碼就可以使用了。為了安裝新版本的存儲過程P并且使得所有依賴代碼生效,你用不著把正在執(zhí)行過程P的用戶趕出去;我們可以在舊代碼還被使用的時候做這些事情。產(chǎn)品環(huán)境的變更本來可能是一個冗長的、復(fù)雜的、脫機(jī)的過程,現(xiàn)在對最終用戶(這是最重要的人群)而言瞬間就完成了。

如果你升級到11gR2需要一個理由,這個理由就是在線版本變更。我只是簡單描述了它的能力,下一篇我會講述更多細(xì)節(jié)。(更多信息請參見Oracle數(shù)據(jù)庫11gR2版高級應(yīng)用開發(fā)者指南第19章,“在線版本變更”)

第二部分:

http://www.Oracle.com/technetwor ... 0asktom-098897.html

上一期的Oracle雜志(2010 一/二月刊),我描述了Oracle數(shù)據(jù)庫11gR2版的殺手級新功能: 在線版本變更。你可能還記得上期的內(nèi)容, 在線版本變更指的是能夠在線升級你的應(yīng)用程序。

在歷史上,Oracle數(shù)據(jù)庫已經(jīng)允許很多操作可以在線完成,例如下面這些:

■修改大多數(shù)的參數(shù)(350個中只有90個不能夠在線修改)
■對象重組:把一個非分區(qū)表變成分區(qū)表,回收空閑空間,等等
■創(chuàng)建索引
■利用Oracle RAC為數(shù)據(jù)庫打補(bǔ)丁
■把Oracle數(shù)據(jù)庫從一個主版本升級到另一個主版本

現(xiàn)在, Oracle 數(shù)據(jù)庫11gR2版為這個清單加入了“升級你的應(yīng)用”功能。

上一次,在“A Closer Look at the New Edition”一文中,我們看到了一個簡單的應(yīng)用場景。我們的數(shù)據(jù)庫中安裝了一個版本為1.0的應(yīng)用程序,我們想為它打補(bǔ)丁;具體地說,我們需要改正其中的一個PL/SQL單元。通常情況下,DBA或者應(yīng)用管理員不得不安排一個系統(tǒng)維護(hù)時段來打這個補(bǔ)丁,因為他要重新編譯代碼。大部分DBA都知道,如果別人正在執(zhí)行一個PL/SQL單元,你就無法編譯它。此外,就算你能夠重編譯那個包,你的應(yīng)用用戶會告訴你,他們突然看到一個“ORA-04068: existing state of packages has been discarded” 的錯誤信息。這些情形使得在現(xiàn)實生活中,你不可能在產(chǎn)品環(huán)境中為你的應(yīng)用程序做在線升級或者在線補(bǔ)丁。這就是在線版本變更功能所要改變的:現(xiàn)在,當(dāng)最終用戶正在執(zhí)行舊版本的代碼時,我們能夠在線為應(yīng)用程序打補(bǔ)丁。

如果你沒有讀過上一篇專欄,我建議你在閱讀本篇之前去讀一讀。在本篇中,我假設(shè)你已經(jīng)知道在線版本變更,理解它的基本功能,并且看過了上一篇專欄的例子。

在這一部分,我們將比上次更進(jìn)一步。在上一篇專欄中,我們的應(yīng)用升級只是一個簡單的補(bǔ)丁:我們實時替換和測試了一個PL/SQL單元,而且沒有招致最終用戶停機(jī)。這一次我們不僅僅要替換某些可版本化的對象(上一期例子中的PL/SQL單元),同時還要做一個物理模式的變更。我們本次的目標(biāo)是要實現(xiàn)代碼和物理模式的變更,同時使得停機(jī)時間盡可能地最小化。
你可能記得,在Oracle數(shù)據(jù)庫11gR2版, 所有對象類型可分為兩大類:可版本化和非版本化。下列對象類型是可版本化的:

■同義詞
■視圖(包括版本視圖,我們下面會給出定義)
■所有PL/SQL對象(函數(shù),過程,包,等等)

所有其他類型的對象(例如表)就是非版本化的。但這一事實并不妨礙我們?yōu)槲覀兊膽?yīng)用程序制造出這些非版本化對象也是“可版本化”的假象。

讓我們從本次的例子入手。在標(biāo)準(zhǔn)的HR模式中有一張EMPLOYEES(職員)表。這張公司的所有職員的清單包含了一些屬性,例如姓,名,電子郵件,和電話號碼。我們假裝這張表歷來就是一個美國公司的應(yīng)用程序的一部分,所以電話號碼是以美國公司能夠方便辨認(rèn)的格式來存儲的。美國電話號碼存儲為一個地區(qū)號碼,然后是一個7位數(shù)字,例如: 650.507.9876。國際號碼的存儲格式有一個美國“轉(zhuǎn)義”碼(在撥打國際長途的時候使用),緊跟著是國家號碼,然后是電話號碼,例如:011.44.1644.429262。

這個虛構(gòu)的公司被收購,現(xiàn)在是一個全球化公司的一部分。為了適應(yīng)新的母公司的標(biāo)準(zhǔn),被收購公司必須修改它存儲電話號碼的格式。現(xiàn)在它利用兩個字段來存儲和現(xiàn)實所有的號碼:一個國家號碼,和一個電話號碼。例如,兩個電話號碼例子前后的存儲格式如下所示:


修改前 修改后
PHONE_NUMBER COUNTRY_CODE PHONE#
-------------------- ----------------- --------------
650.507.9876 +1 650.507.9876
011.44.1644.429262 +44 1644.429262

現(xiàn)在,這個小小的修改(事實上,任何的修改都不是“小小”的!)包含了幾個步驟。具體地說,我們需要:

■修改模式,使得EMPLOYEE表包含兩個新的列:COUNTRY_CODE 和 PHONE#
■修改模式,使得EMPLOYEE表不再包含PHONE_NUMBER列
■在PHONE#為一個搜索頁面創(chuàng)建一個索引(也許還有其他索引,但我們先做一個索引)
■將PHONE_NUMBER的數(shù)據(jù)批量轉(zhuǎn)移到COUNTRY_CODE and PHONE#
■將應(yīng)用程序中依賴于PHONE_NUMBER列的代碼替換掉

這些步驟的任何一個都可能需要很多時間;那些最費(fèi)時的步驟是索引創(chuàng)建、數(shù)據(jù)遷移和代碼替換。我們的目標(biāo)是將這次應(yīng)用升級導(dǎo)致的任何停機(jī)時間最小化,所以我們將會在版本1.0的應(yīng)用程序運(yùn)行期間執(zhí)行所有這些步驟,把所有的變更集結(jié)在應(yīng)用程序的2.0版本。我們將會把停機(jī)時間縮短到僅僅是切換到新應(yīng)用程序所需要的時間(一個ALTER DATABASE操作就可以為我們完成切換)。簡而言之,這次升級的停機(jī)時間應(yīng)該是以秒來衡量,而不是分鐘或小時。

那么我們來看看面對哪些挑戰(zhàn)。第一件要做的事是新增兩個列并去掉一個舊的列。我們必須以一種特殊方式來增加新列,使得現(xiàn)在的應(yīng)用程序不受影響。萬一應(yīng)用程序中有一個SELECT * FROM EMPLOYEES查詢怎么辦?這是一個編程壞習(xí)慣,但是它仍然可能發(fā)生。或者應(yīng)用程序中有這樣的代碼:INSERT INTO EMPLOYEES VALUES (. . . ), 沒有指定列的清單(另一個非常糟糕的編程習(xí)慣)。增加新的兩個列可能破壞現(xiàn)有的應(yīng)用程序。類似的,我們需要刪除一個當(dāng)前應(yīng)用程序正在使用的列(所以顯然我們不能夠真正刪除它)。

為了對付這些難題,Oracle數(shù)據(jù)庫11gR2版引入了一個版本視圖的概念,它為應(yīng)用程序代碼和物理模式之間提供了一個緩沖空間。一個版本視圖是一種特殊的視圖,它只能夠從單一的表執(zhí)行SELECT操作,并把它所需要的屬性清單投影到那個表。沒有WHERE子句,也沒有表連接,僅僅是SELECT和FROM。你可以把一個版本視圖想象為和同義詞非常相似,但這個同義詞不僅僅能夠?qū)⒅赶驅(qū)ο笾孛疫€能夠選擇列和列的命名。不僅如此,這個版本視圖還可以在上面定義觸發(fā)器;不是一個視圖通常具有的INSTEAD OF觸發(fā)器,而是所有常規(guī)的表觸發(fā)器類型,例如BEFORE, BEFORE EACH ROW, 和復(fù)合觸發(fā)器(譯者注:compound trigger是11G以上支持的新類型)。因此,我們可以利用這些版本視圖來為現(xiàn)有的應(yīng)用程序隱藏兩個新列,我們還能用一個新的版本視圖在我們的新應(yīng)用程序中來隱藏那個舊列(就像我們已經(jīng)刪除了它一樣)并且把兩個新列顯露出來。

在這個簡單的“增加兩個新列,刪除一個舊列”的操作中,我們還面臨其他的什么挑戰(zhàn)?嗯,還有數(shù)據(jù)庫中的阻塞和鎖的問題。在過去,為了向一個表增加一個新列,我們需要在那個表上的一個排他鎖。如果你試過為一個活躍的表增加一個新列,這個錯誤可能對你很親切:

SQL> alter table emp add resume blob;
alter table emp add resume blob
*
ERROR at line 1:
ORA-00054: resource busy and acquire
with NOWAIT specified

在過去,在一個活躍的系統(tǒng)上執(zhí)行DDL操作通常是不可能的。可是,在Oracle數(shù)據(jù)庫11GR1和R2的某些場景中,在一個活躍的系統(tǒng)上執(zhí)行DDL的能力被大大增強(qiáng)了。許多DDL操作,比如增加一個列,現(xiàn)在可以用一種不阻塞的方式來執(zhí)行,或者換句話說,假如這個DDL確實需要隔離執(zhí)行,我們可以使用等待型的DDL來避免ORA-00054錯誤(參見Oracle雜志 2008年五/六月刊中有關(guān)這個話題的文章“On Seeing Double in V$SQL” http://www.Oracle.com/technetwor ... 8asktom-085659.html

在過去,DDL修改例如"ADD COLUMN"所帶來的另一個問題是相關(guān)的對象失效:任何在此表上創(chuàng)建的視圖都會失效,任何依賴于這張表的PL/SQL單元都會失效,等等。這使得你不能夠在線增加一個列。但是,Oracle數(shù)據(jù)庫11GR1開始有了"精細(xì)依賴性跟蹤"(參見Oracle雜志2009年七/八月刊文章“ On Popularity and Natural Selection ”中有關(guān)“不用SELECT *的第13134213個理由”一節(jié), http://www.Oracle.com/technetwor ... 9asktom-090487.html),我們已經(jīng)解決了第一個問題:“如何安全地增加兩個新列而不會使任何現(xiàn)有代碼失效”。我們利用版本視圖對現(xiàn)有應(yīng)用程序隱藏新列,這可以受到精細(xì)依賴性跟蹤的保護(hù)。此外,我們現(xiàn)在可以利用版本視圖,虛擬地將現(xiàn)有的列刪掉;同時把兩個新增的列顯露給最新版本的應(yīng)用程序,同樣還是利用版本視圖。

那么在PHONE#列上創(chuàng)建一個索引又會如何?這里有兩個問題:創(chuàng)建索引,還要擔(dān)心這個索引影響現(xiàn)有系統(tǒng)的查詢性能。增加一個新索引可能改善某些查詢的響應(yīng)時間,對另外一些查詢沒有影響,而對第三類查詢卻可能有負(fù)面影響。過去,在Oracle數(shù)據(jù)庫11GR1之前,一個CREATE INDEX語句總是需要在表上加一個排他鎖, 甚至一個CREATE INDEX ONLINE的操作也需要在開始創(chuàng)建索引的瞬間需要這么一個鎖。然而,從Oracle數(shù)據(jù)庫11GR1開始,在線創(chuàng)建一個索引已經(jīng)完全沒有阻塞了。它完全不需要任何鎖,所以能夠避免加鎖和阻塞問題。(注意:CREATE INDEX ONLINE操作是Oracle企業(yè)版數(shù)據(jù)庫才有的功能)此外,索引現(xiàn)在可以被創(chuàng)建為隱形的,意思是它會存在、被修改維護(hù),但是不會被用在存取路徑中,除非有一個會話明確地要求使用這個索引。所以,現(xiàn)在我們能夠在PHONE#列上增加一個索引而不用擔(dān)心阻塞/加鎖的問題,并且能夠確定這個新索引不會對現(xiàn)有的查詢性能產(chǎn)生影響,而這個查詢我們也許還沒有用新索引測試過。因此我們可以安全地增加這個索引而不會對現(xiàn)版本的應(yīng)用程序產(chǎn)生影響。

下一步我們有個真正困難的任務(wù):批量數(shù)據(jù)遷移。我們需要把數(shù)據(jù)從PHONE_NUMBER列遷移到兩個新列。此外,我們還要考慮當(dāng)我們在做這個應(yīng)用升級的時候,那些被現(xiàn)有版本的應(yīng)用程序插入和修改的數(shù)據(jù)要怎么辦。如果僅僅在EMPLOYEES表上作一個批量UPDATE操作來遷移數(shù)據(jù),是行不通的,有兩個原因:

■單個UPDATE的批量操作會鎖住整張表,這就不那么“在線”。
■如果單個批量UPDATE完成了,現(xiàn)有的應(yīng)用程序插入了一個新行或者修改了一個舊記錄的PHONE_NUMBER列,那些修改將不會反映到新列中,所以我們就丟失了這些修改。

幸運(yùn)的是,我們可以解決這兩個問題。第一個問題:單個批量UPDATE對整個表加鎖的問題可以利用一個新的包來解決,DBMS_PARALLEL_EXECUTE (在Oracle雜志2009年十一/十二月刊的文章“Looking at the New Edition”中我們已經(jīng)見識過這個功能,我給它取了個昵稱叫做“DIY并行操作”http://www.Oracle.com/technetwor ... 9asktom-089919.html)利用DBMS_PARALLEL_EXECUTE包,我們能夠把任務(wù)劃分成很小的片, 以我們所需的任何并行度:從并行度1(其實就是串行)到1000不等,來修改表中的每一行。這會縮減每片表數(shù)據(jù)涉及到的數(shù)據(jù)量,以及被鎖的時間。如果我們把表分成100片并且使用并行度1, 我們每次加鎖的表數(shù)據(jù)不超過1%;如果我們把表分成1000片, 我們每次加鎖的表數(shù)據(jù)不超過0.1%, 以此類推。

第二個問題:持續(xù)地將數(shù)據(jù)從現(xiàn)有應(yīng)用遷移到新的應(yīng)用,就更難解決。我們必須以某種方式教會現(xiàn)有系統(tǒng)來為新版本維護(hù)數(shù)據(jù),但是我們在這樣做的同時又不需要修改現(xiàn)有的版本!聽起來像“魔法”,這確實就是魔法。這個魔法就是一種新型的觸發(fā)器:一個跨版本觸發(fā)器。跨版本觸發(fā)器僅在應(yīng)用升級的過程中使用(我們在升級過程結(jié)束后將會盡快把它們刪除)。跨版本觸發(fā)器可以被用來“轉(zhuǎn)送”舊版本上的一個修改到新的版本。它們也能被用來“返送”新版本所作的一個修改到舊版本。(接下來,在關(guān)于“在線版本變更”的這一系列文章的下一篇也就是終結(jié)篇中,我們將會使用一個返送型的跨版本觸發(fā)器)

最后,為了使用新版本代碼,我們必須替換現(xiàn)有的應(yīng)用程序代碼,而不會影響現(xiàn)有的應(yīng)用程序。幸運(yùn)的是,我們上次已經(jīng)知道這一步很容易做到。為了完整性我們會再重復(fù)這個步驟。

在我們開始增加和刪除列之前,我們必須介紹一下版本視圖:應(yīng)用程序和物理模式之間的緩沖區(qū)。這將會導(dǎo)致停機(jī),是一次性的停機(jī),你的應(yīng)用程序?qū)⒂肋h(yuǎn)不需要重復(fù)這個步驟。

提示:在將來,當(dāng)你開發(fā)新的應(yīng)用程序的時候,從一開始就在你的應(yīng)用程序中使用版本視圖,這樣你就可以完全避免這次停機(jī)。這個辦法可以允許你輕而易舉地重命名或重新排列你的表中的列,并且可以讓你的應(yīng)用系統(tǒng)結(jié)合物理模式更新進(jìn)行在線升級。

為了嵌入版本視圖,我們需要一個一次性的停機(jī)時段。這個過程通常需要以下幾個步驟:

■把表改名,因為版本視圖需要占用舊的名字。
■創(chuàng)建版本視圖,把它命名為原表的名字。
■把當(dāng)前表上的觸發(fā)器刪除,轉(zhuǎn)移到版本視圖。這是推薦做法,因為CREATE TRIGGER需要直接引用表名。你需要版本視圖上的觸發(fā)器擁有舊的表名。
■在版本視圖上重新創(chuàng)建觸發(fā)器。
■收回對基表的訪問權(quán)限,賦予對版本視圖的訪問權(quán)限。
■其他步驟,例如把一個精細(xì)訪問控制的策略從基表轉(zhuǎn)移到版本視圖。

我們的應(yīng)用程序沒有觸發(fā)器和權(quán)限控制,所以我們可以跳過其中的幾步。但是通常來說,如果你不單要進(jìn)行應(yīng)用程序代碼修改,同時還要修改物理模式,那么為了做好準(zhǔn)備,上述過程是你必須執(zhí)行的。

我們假設(shè)我們已經(jīng)有了在上一篇專欄中設(shè)置和使用過的DEMO賬戶。你可能記得,DEMO賬戶是一個普通模式,它僅僅有CREATE SESSION 和 CREATE PROCEDURE,另外還有為模式創(chuàng)建版本的權(quán)限(ALTER USER DEMO ENABLE EDITIONS),同時還能夠使用一個現(xiàn)有的稱為VERSION2 的版本(GRANT USE ON EDITION VERSION2 TO DEMO)。在這個例子中,我們會賦予DEMO賬戶創(chuàng)建表和序列的權(quán)限,這樣我們就能夠拷貝EMPLOYEES表;我們還要賦予創(chuàng)建視圖和觸發(fā)器的權(quán)限,除此之外的權(quán)限集保留原樣。

我們來看看現(xiàn)有版本1.0的應(yīng)用程序的設(shè)置:

SQL> create table
2 employees
3 as
4 select *
5 from hr.employees;

Table created.

SQL> create sequence emp_seq
2 start with 500;

Sequence created.

這就是我們要用的數(shù)據(jù)的拷貝;為了這次演示創(chuàng)建一個序列,它的開始號碼大于任何一個EMPLOYEES中現(xiàn)有的號。現(xiàn)有的應(yīng)用程序代碼執(zhí)行兩個功能:給定一個搜索字串,顯示一個EMPLOYEES報表,帶有職員的電話號碼和e-mails;雇用一個新職員,這將會修改現(xiàn)有的表。這個包的包頭定義部分如代碼清單1所示。

代碼清單1: emp_pkg 包頭,應(yīng)用程序版本1.0

SQL> create or replace package emp_pkg
2 as
3 procedure show
4 ( last_name_like in employees.last_name%type );
5
6 function add
7 ( FIRST_NAME in employees.FIRST_NAME%type := null,
8 LAST_NAME in employees.LAST_NAME%type,
9 EMAIL in employees.EMAIL%type,
10 PHONE_NUMBER in employees.PHONE_NUMBER%type := null,
11 HIRE_DATE in employees.HIRE_DATE%type,
12 JOB_ID in employees.JOB_ID%type,
13 SALARY in employees.SALARY%type := null,
14 COMMISSION_PCT in employees.COMMISSION_PCT%type := null,
15 MANAGER_ID in employees.MANAGER_ID%type := null,
16 DEPARTMENT_ID in employees.DEPARTMENT_ID%type := null )
17 return employees.employee_id%type;
18 end;
19 /

Package created.

實現(xiàn)代碼也是非常直觀的,一個“展示”程序和一個簡單的“新增職員”事務(wù),如代碼清單2所示。

代碼清單2: emp_pkg 包體,應(yīng)用程序版本1.0

SQL> create or replace package body emp_pkg
2 as
3
4 procedure show
5 ( last_name_like in employees.last_name%type )
6 as
7 begin
8 for x in
9 ( select first_name, last_name,
10 phone_number, email
11 from employees
12 where last_name like
13 show.last_name_like
14 order by last_name )
15 loop
16 dbms_output.put_line
17 ( rpad( x.first_name || ' ' ||
18 x.last_name, 40 ) ||
19 rpad( nvl(x.phone_number, ' '), 20 ) ||
20 x.email );
21 end loop;
22 end show;
23
24 function add
25 ( FIRST_NAME in employees.FIRST_NAME%type := null,
26 LAST_NAME in employees.LAST_NAME%type,
27 EMAIL in employees.EMAIL%type,
28 PHONE_NUMBER in employees.PHONE_NUMBER%type := null,
29 HIRE_DATE in employees.HIRE_DATE%type,
30 JOB_ID in employees.JOB_ID%type,
31 SALARY in employees.SALARY%type := null,
32 COMMISSION_PCT in employees.COMMISSION_PCT%type := null,
33 MANAGER_ID in employees.MANAGER_ID%type := null,
34 DEPARTMENT_ID in employees.DEPARTMENT_ID%type := null
35 )
36 return employees.employee_id%type
37 is
38 employee_id employees.employee_id%type;
39 begin
40 insert into employees
41 ( EMPLOYEE_ID, FIRST_NAME, LAST_NAME,
42 EMAIL, PHONE_NUMBER, HIRE_DATE,
43 JOB_ID, SALARY, COMMISSION_PCT,
44 MANAGER_ID, DEPARTMENT_ID )
45 values
46 ( emp_seq.nextval, add.FIRST_NAME, add.LAST_NAME,
47 add.EMAIL, add.PHONE_NUMBER, add.HIRE_DATE,
48 add.JOB_ID, add.SALARY, add.COMMISSION_PCT,
49 add.MANAGER_ID, add.DEPARTMENT_ID )
50 returning employee_id into add.employee_id;
51
52 return add.employee_id;
53 end add;
54
55 end;
56 /

Package body created.

現(xiàn)在我們可以看看這個應(yīng)用程序在SQL*PLUS中如何工作,如代碼清單3所示。

代碼清單3: 在SQL*Plus 中運(yùn)行應(yīng)用程序版本1.0

SQL> exec emp_pkg.show( '%K%' );
Payam Kaufling 650.123.3234 PKAUFLIN
Alexander Khoo 515.127.4562 AKHOO
Steven King 515.123.4567 SKING
Janette King 011.44.1345.429268 JKING
Neena Kochhar 515.123.4568 NKOCHHAR
Sundita Kumar 011.44.1343.329268 SKUMAR

PL/SQL procedure successfully completed.

SQL> begin
2 dbms_output.put_line
3 ( emp_pkg.add
4 ( first_name => 'Tom',
5 last_name => 'Kyte',
6 email => 'TKYTE',
7 phone_number => '703.123.9999',
8 hire_date => sysdate,
9 job_id => 'IT_PROG' ) );
10 end;
11 /
500

PL/SQL procedure successfully completed.

SQL> exec emp_pkg.show( '%K%' );
Payam Kaufling 650.123.3234 PKAUFLIN
Alexander Khoo 515.127.4562 AKHOO
Janette King 011.44.1345.429268 JKING
Steven King 515.123.4567 SKING
Neena Kochhar 515.123.4568 NKOCHHAR
Sundita Kumar 011.44.1343.329268 SKUMAR
Tom Kyte 703.123.9999 TKYTE

PL/SQL procedure successfully completed.

現(xiàn)在我們來準(zhǔn)備好我們的模式,使得它能允許應(yīng)用程序的在線升級,包括物理模式的更新。請記住,這需要一次停機(jī)——最后一次的停機(jī),這是為了把版本視圖放進(jìn)去。在這個例子中,創(chuàng)建版本視圖包含下列步驟:

SQL> alter table employees
2 rename to employees_rt;

Table altered.

SQL> create editioning view employees
2 as
3 select
4 EMPLOYEE_ID, FIRST_NAME,
5 LAST_NAME, EMAIL, PHONE_NUMBER,
6 HIRE_DATE, JOB_ID, SALARY,
7 COMMISSION_PCT, MANAGER_ID,
8 DEPARTMENT_ID
9 from employees_rt
10 /

View created.

就是這樣。一旦做好,我們又在線了。不僅如此,現(xiàn)有的應(yīng)用程序百分之百不受影響;我們放進(jìn)去的版本視圖看起來、用起來都和表一樣。現(xiàn)有的應(yīng)用程序和以前一樣運(yùn)行。

現(xiàn)在我們增加我們的新列和索引,為發(fā)布新的應(yīng)用程序做準(zhǔn)備:

SQL> alter table employees_rt
2 add
3 ( country_code varchar2(3),
4 phone# varchar2(20)
5 )
6 /
Table altered.

SQL> create index employees_phone#_idx
2 on employees_rt(phone#)
3 ONLINE INVISIBLE
4 /

Index created.

從Oracle數(shù)據(jù)庫11GR1開始,新增列可以在線操作。不僅如此,注意索引是如何可以在線創(chuàng)建(在Oracle數(shù)據(jù)庫11GR1以上版本,是真正的在線操作)并可隱形(INVISIBLE)的。在這個特定例子中,這個INVISIBLE選項在技術(shù)上并不是必須的,因為PHONE#在現(xiàn)有的應(yīng)用程序中從未以任何途徑、形式、方式被引用過。我們在這里把它包括進(jìn)去只是為了演示的完整性。

現(xiàn)在我們的任務(wù)是把數(shù)據(jù)從現(xiàn)有版本遷移到新版本。為了做到這一點(diǎn),我們要依靠一個轉(zhuǎn)送型的跨版本觸發(fā)器,我們利用這個觸發(fā)器來保證在當(dāng)前版本中所有被插入或修改的數(shù)據(jù)會被發(fā)送到新版本中。利用這個觸發(fā)器,我們不僅可以捕獲舊程序產(chǎn)生的數(shù)據(jù)變更,而且還可以作批量數(shù)據(jù)遷移。也就是說,我們要寫一段邏輯代碼來根據(jù)舊的PHONE_NUMBER列維護(hù)COUNTRY_CODE和PHONE#列(我們必須得這樣做,才能把當(dāng)前應(yīng)用程序的修改轉(zhuǎn)送到新的物理模式),同時我們要利用這段邏輯來做數(shù)據(jù)遷移。一旦這個轉(zhuǎn)送型的跨版本觸發(fā)器準(zhǔn)備就緒,利用DBMS_PARALLEL_EXECUTE包發(fā)出一個“update employees set phone_number = phone_number”指令來做數(shù)據(jù)遷移將是一件很簡單的事。代碼清單4展示了這個轉(zhuǎn)送型的跨版本觸發(fā)器。

代碼清單 4: 跨版本觸發(fā)器

SQL> alter session set edition = version2;

Session altered.

SQL> create or replace trigger employees_fwdxedition
2 before insert or update of phone_number on employees_rt
3 for each row
4 forward crossedition
5 declare
6 first_dot number;
7 second_dot number;
8 begin
9 if :new.phone_number like '011.%'
10 then
11 first_dot
12 := instr( :new.phone_number, '.' );
13 second_dot
14 := instr( :new.phone_number, '.', 1, 2 );
15 :new.country_code
16 := '+'||
17 substr( :new.phone_number,
18 first_dot+1,
19 second_dot-first_dot-1 );
20 :new.phone#
21 := substr( :new.phone_number,
22 second_dot+1 );
23 else
24 :new.country_code := '+1';
25 :new.phone# := :new.phone_number;
26 end if;
27 end;
28 /

Trigger created.

關(guān)于這個觸發(fā)器有幾件事要注意:

我們是在一個“未來”的版本中創(chuàng)建的,這個版本就是新的應(yīng)用模式(VERSION2)。因此ALTER SESSION 語句是必需的。我們的目標(biāo)是不影響當(dāng)前的應(yīng)用,所以我們只會在新版本中做這些版本相關(guān)的工作。你的跨版本觸發(fā)器必須在“未來”版本中創(chuàng)建,永遠(yuǎn)不在舊版本的應(yīng)用中創(chuàng)建。在觸發(fā)器的第四行你可以見到這個“魔法”:FORWARD CROSSEDITION。這個子句意味著這個觸發(fā)器僅僅在INSERT, UPDATE, 或DELETE語句的當(dāng)前版本為舊版(即舊版本應(yīng)用程序發(fā)出的這些語句)才會被觸發(fā)。

現(xiàn)在我們已經(jīng)準(zhǔn)備好批量數(shù)據(jù)遷移。這可以用一個簡單的UPDATE來做到:
SQL> update employees
set phone_number = phone_number;

109 rows updated.

但是請記住,這將會鎖住整個表,因為它修改了每一行,而這是我們盡量要避免的。但是,在任何情況下,現(xiàn)有舊應(yīng)用發(fā)出的任何插入或刪除都會為我們把這些變更轉(zhuǎn)送到新的列中。

在下一期,我們將會完成這個例子并演示:

■如何將批量的修改劃分成小片,一點(diǎn)一點(diǎn)完成,這樣就不會在任一時刻鎖住表的太多數(shù)據(jù),也就是說,如何在線地完成一個批量修改。

■安裝新的應(yīng)用程序代碼,包括為這個新應(yīng)用服務(wù)的新的版本視圖。

■完成到新版本應(yīng)用的切換,清理舊版本應(yīng)用遺留下來的代碼和列,這些已經(jīng)用不著了。

然后我會給出這個例子的另一個結(jié)尾。它不是簡單地“切斷舊版本并開始使用新版本”,即“溫切換”,我們要進(jìn)行的是一個“熱交割”,借助它我們可以使得舊版本和新版本同時并發(fā)運(yùn)行,直到我們不再需要舊版本應(yīng)用程序為止。這個熱交割技術(shù)把停機(jī)時間從幾秒縮減到根本沒有。等這些做完,我們會看看如何利用在線版本變更來進(jìn)行:

■為一個在線系統(tǒng)打補(bǔ)丁而不需要任何停機(jī)(第一期提到的)
■將一次應(yīng)用系統(tǒng)升級的停機(jī)時間最小化到幾秒鐘,包括物理模式變更和批量修改操作
■完全去除停機(jī)時間

如果你等不及下一期的終結(jié)篇,我建議你下載:
http://www.Oracle.com/technetwor ... nition-1-133045.pdf
這是Bryn Llewellyn為在線版本變更寫的白皮書。Llewellyn繼續(xù)擔(dān)任Oracle的PL/SQL產(chǎn)品經(jīng)理,現(xiàn)在他還負(fù)責(zé)在線版本變更。Oracle數(shù)據(jù)庫高級應(yīng)用開發(fā)者指南,11gR2版 (11.2) 的第19章也包含了一個完整的在線版本變更教程。

第三部分:
http://www.Oracle.com/technetwor ... 0asktom-082672.html

在上一期的Oracle雜志中(2010年三/四月刊),我繼續(xù)描述了Oracle數(shù)據(jù)庫11gR2版的殺手級功能:在線版本變更。如果你還記得,在線版本變更使得你能夠在線升級你的應(yīng)用程序。

這是關(guān)于這一新功能的系列專欄文章的第三篇也是終結(jié)篇。如果你還沒有讀過前兩篇,我建議你現(xiàn)在就去讀一讀,因為本篇專欄基于我們一直在使用的例子。

上次,我們的應(yīng)用升級不是一個簡單的應(yīng)用程序補(bǔ)丁。它不僅替換了某些PL/SQL單元(補(bǔ)丁),而且修改了我們的應(yīng)用程序所涉及到的一個表的物理結(jié)構(gòu)。這是一個真正的應(yīng)用升級,它包括了可版本化的對象類型的變更(我們例子中的PL/SQL單元),同時也包括了非版本化類型的對象的變更(我們的表結(jié)構(gòu))。以下的對象類型是可版本化的:

■同義詞
■視圖(包括版本視圖,我們下面會給出定義)
■所有PL/SQL對象(函數(shù),過程,包,等等)

其他所有的數(shù)據(jù)庫對象類型是非版本化的。一個表是永遠(yuǎn)不可版本化的,這是有意的設(shè)計。這是因為它包含了數(shù)據(jù),開發(fā)者必須明確決定哪些數(shù)據(jù)(也就是說,哪些表列)不會被升級所修改,哪些表列會被替換。回想上一篇專欄,為了給我們的應(yīng)用創(chuàng)造一個表是可版本化的假象,我們利用版本視圖隱藏了對表的物理模式修改。同一個版本視圖在不同版本中出現(xiàn),從表中僅僅讀取應(yīng)用程序在那一個特定版本中所需要的列。

讓我們從本次的例子入手。在標(biāo)準(zhǔn)的HR模式中有一張EMPLOYEES(職員)表。這張公司的所有職員的清單包含了一些屬性,例如姓,名,電子郵件,和電話號碼。我們假裝這張表歷來就是一個美國公司的應(yīng)用程序的一部分,所以電話號碼是以美國公司能夠方便辨認(rèn)的格式來存儲的。美國電話號碼存儲為一個地區(qū)號碼,然后是一個7位數(shù)字,例如: 650.507.9876。國際號碼的存儲格式有一個美國“轉(zhuǎn)義”碼(在撥打國際長途的時候使用),緊跟著是國家號碼,然后是電話號碼,例如:011.44.1644.429262。

我們先來概括一下現(xiàn)在例子的狀況。我們有一張應(yīng)用程序中要用到的表,它來自標(biāo)準(zhǔn)的例子HR模式,確定地說就是EMPLOYEES表。這張某個公司所有職員的清單包括了這些屬性,如:姓,名,電子郵件地址和電話號碼。我們假裝這張表歷來就是一個美國公司的應(yīng)用程序的一部分,所以電話號碼是以美國公司能夠方便辨認(rèn)的格式來存儲的。美國電話號碼存儲為一個地區(qū)號碼,然后是一個7位數(shù)字,例如: 650.507.9876。國際號碼的存儲格式有一個美國“轉(zhuǎn)義”碼(在撥打國際長途的時候使用),緊跟著是國家號碼,然后是電話號碼,例如:011.44.1644.429262。

這個虛構(gòu)的公司被收購,現(xiàn)在是一個全球化公司的一部分。為了適應(yīng)新的母公司的標(biāo)準(zhǔn),被收購公司必須修改它存儲電話號碼的格式。現(xiàn)在它利用兩個字段來存儲和現(xiàn)實所有的號碼:一個國家號碼,和一個電話號碼。例如,兩個電話號碼例子前后的存儲格式如下所示:


修改前 修改后
PHONE_NUMBER COUNTRY_CODE PHONE#
-------------------- ----------------- --------------
650.507.9876 +1 650.507.9876
011.44.1644.429262 +44 1644.429262

現(xiàn)在,這個小小的修改(事實上,任何的修改都不是“小小”的!)包含了幾個步驟。具體地說,我們需要:

■修改模式,使得EMPLOYEE表包含兩個新的列:COUNTRY_CODE 和 PHONE#
■修改模式,使得EMPLOYEE表不再包含PHONE_NUMBER列
■在PHONE#為一個搜索頁面創(chuàng)建一個索引(也許還有其他索引,但我們先做一個索引)
■將PHONE_NUMBER的數(shù)據(jù)批量轉(zhuǎn)移到COUNTRY_CODE and PHONE#
■將應(yīng)用程序中依賴于PHONE_NUMBER列的代碼替換掉

在我的前一篇專欄中,我們演練了版本視圖的概念和實現(xiàn)。那個視圖為我們的應(yīng)用代碼和底層的物理模式之間提供了一個緩沖區(qū)。我們在例子中引入的版本視圖,配合Oracle數(shù)據(jù)庫11GR1版以上所提供的非阻塞、快速新增列功能,使得我們能夠增加兩個新列而完全不對當(dāng)前應(yīng)用系統(tǒng)有任何影響。版本視圖也使得我們能夠把現(xiàn)有的PHONE_NUMBER列對新版本應(yīng)用隱藏,效果上相當(dāng)于使得我們能夠在應(yīng)用程序的版本2中虛擬地刪除PHONE_NUMBER列,而不影響版本1。

我們還利用ONLINE和INVISIBLE功能,在新增的列上建立了一個新索引。從Oracle數(shù)據(jù)庫11GR1版開始,CREATE INDEX ONLINE操作是100%的在線操作:它從未在任何時間點(diǎn)鎖住表,而在以前的版本是要鎖表的。新的INVISIBLE選項使得我們能夠創(chuàng)建索引而不會影響當(dāng)前應(yīng)用版本的查詢計劃。我們使用這個選項是因為增加一個索引可能使得某些查詢跑得更快,另一些維持原速度,而第三類查詢可能變得更慢。因為我們沒有用這個新索引測試過現(xiàn)有應(yīng)用的查詢計劃,所以我們想讓這個新索引隱藏起來。

上一期我們的例子停留在“批量遷移數(shù)據(jù)”的階段。我們描述了一個單一的批量UPDATE操作來遷移數(shù)據(jù)會如何鎖住整張表。在批量修改的過程中。EMPLOYEES表上的其他事務(wù)將被阻塞;事實上,現(xiàn)有的應(yīng)用(版本1)已經(jīng)離線了。此外,一旦這個單一的批量UPDATE結(jié)束,如果現(xiàn)有的應(yīng)用(版本1)插入一個新行,或者修改一個現(xiàn)有記錄的PHONE_NUMBER列,這些變更將不會反映到新列中。我們丟失了這些變更。

為了克服這些障礙,我們查看了兩個新功能。首先我們討論了DBMS_PARALLEL_EXECUTE包。(在Oracle雜志2009年十一/十二月刊的文章“Looking at the New Edition”中我們首次見識過這個功能,我給它取了個昵稱叫做“DIY并行操作”http://www.Oracle.com/technetwor ... 9asktom-089919.html)利用DBMS_PARALLEL_EXECUTE包,我們能夠把任務(wù)劃分成很小的片, 以我們所需的任何并行度:從并行度1(其實就是串行)到1000不等,來修改表中的每一行,因而我們永遠(yuǎn)不需要鎖住整個表,而是每次只鎖住一小片。然后,我們引入了一個新型的觸發(fā)器:跨版本觸發(fā)器。跨版本觸發(fā)器只在應(yīng)用升級的過程中被使用(我們在升級過程結(jié)束后將會盡快把他們刪除)。跨版本觸發(fā)器可以被用來“轉(zhuǎn)送”舊版本上的一個修改到新的版本。它們也能被用來“返送”新版本所作的一個修改到舊版本。

所以,為了趕上本篇的進(jìn)度,我假設(shè)你已經(jīng)準(zhǔn)備好這幾樣?xùn)|西:

■我們一直在使用的DEMO賬戶。
■作為ORABASE版本的子版本而創(chuàng)建的VERSION2版本。 ■HR.EMPLOYEES表的一個拷貝,在DEMO模式中已經(jīng)更名為EMPLOYEES_RT(“RT”代表“Real Table”(真正的表):這是我的命名習(xí)慣)。這個表已經(jīng)被改過了,增加了我們需要的兩個新列。此外,EMPLOYEES_PHONE#_IDX索引已經(jīng)建好了。 ■在ORABASE版本中的原來的EMP_PKG包,它代表我們的應(yīng)用代碼;它知道如何增加一個新職員,以及如何展示現(xiàn)有的數(shù)據(jù)。
■EMPLOYEES在ORA$BASE版本中的版本視圖,它只返回了EMPLOYEES_RT的那些列,就是我們的應(yīng)用程序所需要的。
■一個轉(zhuǎn)送型的跨版本觸發(fā)器EMPLOYEES_FWDXEDITION,它將舊格式的電話號碼轉(zhuǎn)換為新的兩列格式的電話號碼。

這是在第二篇專欄中提供的(代碼參見http://www.Oracle.com/technetwor ... 0asktom-098897.html)。在例子的結(jié)尾我們留下了一個簡單的UPDATE:

SQL> update employees
set phone_number = phone_number;
109 rows updated.

它進(jìn)行了數(shù)據(jù)的批量遷移,而轉(zhuǎn)送型的跨版本觸發(fā)器保證了當(dāng)前版本應(yīng)用所做的后續(xù)修改會被反映到新列中。但那個UPDATE操作會鎖住整個表,當(dāng)它處理所有的行時。它將會是一個離線操作,而我們的目標(biāo)是要使它成為一個在線升級。因此,為了完成整個應(yīng)用升級,我們這次要講到的步驟將會:

■利用DBMS_PARALLEL_EXECUTE包來將表劃分成小片進(jìn)行修改,使得批量遷移和現(xiàn)有應(yīng)用程序能夠并發(fā)執(zhí)行
■安裝新的應(yīng)用,同時現(xiàn)有應(yīng)用還處于運(yùn)行狀態(tài)
■安裝一個返送型的跨版本觸發(fā)器,它使得我們能夠并發(fā)地執(zhí)行新舊版本的應(yīng)用代碼,從而實現(xiàn)“熱交割”,根本不需要任何停機(jī)時間。
■當(dāng)新版本應(yīng)用是唯一使用的版本時,作一個清理

使用DBMS_PARALLEL_EXECUTE包:

我們假裝沒有執(zhí)行過那個UPDATE,而是為EMPLOYEES_RT表增加了列,同時將轉(zhuǎn)送型的跨版本觸發(fā)器準(zhǔn)備就緒。現(xiàn)在我們想要修改EMPLOYEES_RT表的每一行來觸發(fā)那個轉(zhuǎn)送型的跨版本觸發(fā)器,從而將舊列中的數(shù)據(jù)拷貝并轉(zhuǎn)換到兩個新列中。現(xiàn)在我們進(jìn)入DBMS_PARALLEL_EXECUTE包。

在我的書《Oracle 9i10g編程藝術(shù)》中,我花了幾頁篇幅描述了如何將批量操作“并行”執(zhí)行,采用的是一種DIY的并行化辦法。這個方法是把一個表按照范圍劃片,使用ROWID(參見asktom.Oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10498431232211這里有關(guān)于這個方法的概述)或者主鍵的范圍(參見"分割一個大表" http://www.Oracle.com/technetwor ... 6asktom-101983.html 這里有關(guān)于這個方法的概述)。雖然我描述的方法都很直觀,但它們畢竟是手工操作。你不得不拿過我的“技術(shù)”并且為你的特定情況作調(diào)整。

現(xiàn)在,沒有必要調(diào)整了。在Oracle數(shù)據(jù)庫11gR2版, 我們有一個簡單辦法,來達(dá)到和我的演示完全相同的目的。新的DBMS_PARALLEL_EXECUTE包能夠把一個大表按照ROWID范圍,或鍵值范圍,或用戶自定義方法進(jìn)行分割。表被邏輯上進(jìn)行分割,數(shù)據(jù)庫在后臺處理每一個范圍,利用調(diào)度器,錯誤日志,重試,等等。但是,在我們使用DBMS_PARALLEL_EXECUTE之前,我們將不得不(為了演示的目的)擴(kuò)充我們的EMPLOYEES_RT表,因為它現(xiàn)在實在是太小了。首先我們會把它擴(kuò)大100倍,如清單1所示:

代碼清單 1: 把 EMPLOYEES_RT 表擴(kuò)大

SQL> insert into employees
2 select * from
3 (
4 with data(r)
5 as
6 (select 1 r from dual
7 union all
8 select r+1 from data where r <= 100
9 )
10 select rownum+(select max(employee_id)
11 from employees_rt),
12 FIRST_NAME, LAST_NAME, EMAIL,
13 PHONE_NUMBER, HIRE_DATE, JOB_ID,
14 SALARY, COMMISSION_PCT, MANAGER_ID,
15 DEPARTMENT_ID
16 from employees_rt, data
17 );

11009 rows created.

清單1在第4行到第9行使用了Oracle數(shù)據(jù)庫11gR2版的一個新功能:遞歸的WITH子查詢,來產(chǎn)生100個行。這個清單還作了一個和EMPLOYEES_RT的笛卡爾連接,這將會產(chǎn)生一個結(jié)果集,在這個集合中EMPLOYEES_RT的每行數(shù)據(jù)都被重復(fù)100遍。它還利用ROWNUM加上當(dāng)前的最大的EMPLOYEE_ID來在我們的表上產(chǎn)生一個新的主鍵,并且插入這些新的行,立即使得我們的表比原來增大了100倍(嚴(yán)格地說是原來的101倍)。我們可以用清單2來查看現(xiàn)有的數(shù)據(jù):

代碼清單 2: 查看EMPLOYEES_RT 中的數(shù)據(jù)

SQL> select phone_number, country_code, phone#
2 from employees_rt
3 where country_code is null
4 and rownum <= 5
5 union all
6 select phone_number, country_code, phone#
7 from employees_rt
8 where country_code is NOT null
9 and rownum <= 5;

PHONE_NUMBER COUNTRY_CODE PHONE#


650.507.9833
650.507.9844
515.123.4444
515.123.5555
603.123.6666
011.44.1344.429268 +44 1344.429268
011.44.1344.467268 +44 1344.467268
011.44.1344.429278 +44 1344.429278
011.44.1344.619268 +44 1344.619268
011.44.1344.429018 +44 1344.429018

10 rows selected.

SQL> select count(*),
2 count(distinct dbms_rowid.rowid_block_number(rowid)) cnt_blk
3 from employees
4 /

COUNT(*) CNT_BLK


11118 138

注意,其中的一些行的新列已經(jīng)被填入了數(shù)據(jù)。我們并沒有插入數(shù)據(jù),那是我們放入的轉(zhuǎn)發(fā)型跨版本觸發(fā)器在起作用。我們已經(jīng)把它設(shè)置好了,所以當(dāng)我們在當(dāng)前的應(yīng)用插入或者修改PHONE_NUMBER列時,轉(zhuǎn)發(fā)型跨版本觸發(fā)器將會自動把變動從舊版本應(yīng)用使用的列轉(zhuǎn)發(fā)到新應(yīng)用使用的列。因此,這演示了在舊版本應(yīng)用中新產(chǎn)生的數(shù)據(jù)會保持并且為我們透明地轉(zhuǎn)換為新版本應(yīng)用的格式。

接下來,這些步驟將執(zhí)行對EMPLOYEES_RT表的批量修改操作而不會鎖住整張表。我們需要修改所有當(dāng)前的行,將會觸動到PHONE_NUMBER列使得數(shù)據(jù)被從PHONE_NUMBER拷貝到COUNTRY_CODE和PHONE#列。我們當(dāng)前的表有差不多138個數(shù)據(jù)塊(如清單2所示),我們將會每次修改它的10%。(EMPLOYEES_RT是一張小表;在更大的表中,你可能會用一個小得多的百分比來避免一次鎖定太多的表數(shù)據(jù)。)所以我們將會把它分割為10個數(shù)據(jù)塊或者類似尺寸的小片。這樣做的方法如清單3所示。

代碼清單 3: 為批量修改創(chuàng)建數(shù)據(jù)劃片

SQL> begin
2 dbms_parallel_execute.create_task('update employees_rt');
3 dbms_parallel_execute.create_chunks_by_rowid
4 ( task_name => 'update employees_rt',
5 table_owner => user,
6 table_name => 'EMPLOYEES_RT',
7 by_row => false,
8 chunk_size => 10);
9 end;
10 /

PL/SQL procedure successfully completed.

代碼清單 3中的PL/SQL塊做了兩件事:首先它創(chuàng)建了一個帶名字的任務(wù),我們隨后可以操縱它。它為這個任務(wù)所做的第一件事,是利用CREATE_CHUNKS_BY_ROWID過程加入表的數(shù)據(jù)劃片。當(dāng)代碼調(diào)用到這個過程,過程會讀取EMPLOYEES_RT表并把它按照10個數(shù)據(jù)塊為一片來劃分(chunk_size=>10)。請注意這個CREATE_CHUNKS_BY_ROWID API運(yùn)行使用兩種方式來為表劃片:基于數(shù)據(jù)塊數(shù)或者行數(shù)。因為代碼里面用了by_row=>false,API使用了按數(shù)據(jù)塊劃片的方法。代碼清單3的PL/SQL過程的結(jié)果可以在新的USER_PARALLEL_EXECUTE_CHUNKS視圖看到,如清單4所示。

代碼清單 4: 查看 USER_PARALLEL_EXECUTE_CHUNKS 里面的劃片

SQL> select chunk_id, status, start_rowid, end_rowid
2 from user_parallel_execute_chunks
3 where task_name = 'update employees_rt'
4 and rownum <= 5
5 /

CHUNK_ID STATUS START_ROWID END_ROWID


134 UNASSIGNED AAAVkkAAEAAAA/YAAA AAAVkkAAEAAAA/fCcP
135 UNASSIGNED AAAVkkAAEAAAA/oAAA AAAVkkAAEAAAA/vCcP
136 UNASSIGNED AAAVkkAAEAAAA/wAAA AAAVkkAAEAAAA/3CcP
137 UNASSIGNED AAAVkkAAEAAAA/4AAA AAAVkkAAEAAAA//CcP
138 UNASSIGNED AAAVkkAAEAAABwIAAA AAAVkkAAEAAABwPCcP

我們可以看到一系列的數(shù)據(jù)片,實際上是ROWID的范圍,它們邏輯上把這個表劃成了小片。清單4中的每一個ROWID范圍是不交疊的,但是所有范圍覆蓋了整張表。這就是我們?nèi)绾螌蝹€UPDATE劃分成N個更小的UPDATE而不會彼此沖突。

現(xiàn)在我們已經(jīng)準(zhǔn)備好執(zhí)行我們的UPDATE操作了。這是通過清單5中的代碼來完成的,它使用兩個執(zhí)行線程(parallel_level=>2)運(yùn)行我們的任務(wù)。我使用2只是為了演示你可以把某件東西劃成比你并發(fā)運(yùn)行的數(shù)目多得多的小片。我們知道我們至少有5片數(shù)據(jù)(如清單4所示),但我們每次只是執(zhí)行其中的2片。我們執(zhí)行的UPDATE被參數(shù)化,只接受一個ROWID范圍并且操作那個范圍內(nèi)的記錄。因此,利用“并行度2”,我們一點(diǎn)一點(diǎn)地修改了表中所有的行,每次只是鎖住表的一個小小的子集。這使得我們正在同時批量遷移數(shù)據(jù)的時候,現(xiàn)有的應(yīng)用能夠正常運(yùn)作。

代碼清單 5: 運(yùn)行UPDATE EMPLOYEES_RT 任務(wù)

SQL> begin
2 dbms_parallel_execute.run_task
3 ( task_name => 'update employees_rt',
4 sql_stmt => 'update employees_rt
5 set phone_number = phone_number
6 where rowid between :start_id
7 and :end_id',
8 language_flag => DBMS_SQL.NATIVE,
9 parallel_level => 2 );
10 end;
11 /

PL/SQL procedure successfully completed.

注意:用并行模式執(zhí)行任務(wù)需要一個具有CREATE JOB權(quán)限的模式,因為RUN_TASK會利用調(diào)度器來并行地執(zhí)行更新。如果你串行地執(zhí)行任務(wù),則CREATE JOB權(quán)限不是必需的。

操作完成后,如果我們對結(jié)果滿意,可以把創(chuàng)建的任務(wù)刪掉:

SQL> begin
2 dbms_parallel_execute.drop_task('update employees_rt');
3 end;
4 /

PL/SQL procedure successfully
completed.

現(xiàn)在我們已知所有的舊數(shù)據(jù)已經(jīng)被轉(zhuǎn)換和修改到新應(yīng)用中,并且轉(zhuǎn)送型的跨版本觸發(fā)器會自動轉(zhuǎn)換和修改所有“新產(chǎn)生的舊格式數(shù)據(jù)”,所以我們是安全的。

安裝新應(yīng)用的代碼:

現(xiàn)在我們想要安裝我們的新應(yīng)用代碼。我們采用的方法,和這個三部分系列文章中的第一篇中使用的方法相同:我們簡單地切換到新版本并且CREATE OR REPLACE我們的可版本化對象(即EMPLOYEES版本視圖和EMP_PKG包)。注意為了簡潔起見,并不是所有代碼都在清單6中列出,而僅僅是對當(dāng)前代碼的變動。

代碼清單 6: 安裝新的應(yīng)用

SQL> alter session set edition = version2;

Session altered.

SQL>
SQL> select object_name, object_type, status, edition_name
2 from user_objects_ae
3 where object_name in ( 'EMPLOYEES', 'EMP_PKG' );

OBJECT_NAME OBJECT_TYPE STATUS EDITION_NAME


EMPLOYEES VIEW VALID ORABASE EMP_PKG PACKAGE VALID ORABASE
EMP_PKG PACKAGE BODY VALID ORA$BASE

這個清單顯示我們正在使用VERSION2版本,并且現(xiàn)在所有的代碼從現(xiàn)有的版本(version 1),即ORA$BASE版本中的應(yīng)用程序繼承過來。

現(xiàn)在我們替換視圖和包,如清單7所示。

代碼清單 7: 替換視圖和包

SQL> create OR REPLACE editioning view employees
2 as
3 select EMPLOYEE_ID, FIRST_NAME,
4 LAST_NAME, EMAIL, COUNTRY_CODE, PHONE#,
...
9 /

View created.

SQL> create or replace package emp_pkg
2 as
...
9 EMAIL in employees.EMAIL%type,
10 COUNTRY_CODE in employees.COUNTRY_CODE%type := null,
11 PHONE# in employees.PHONE#%type := null,
12 HIRE_DATE in employees.HIRE_DATE%type,
...
18 return employees.employee_id%type;
19 end;
20 /

Package created.

SQL> create or replace package body emp_pkg
2 as
3
4 procedure show
...
9 ( select first_name, last_name,
10 country_code, phone#, email
11 from employees
12 where last_name like
13 show.last_name_like
14 order by last_name )
15 loop
16 dbms_output.put_line
17 ( rpad( x.first_name || ' ' ||
18 x.last_name, 40 ) ||
19 rpad( nvl(x.country_code, ' '), 5 ) ||
20 rpad( nvl(x.phone#, ' '), 20 ) ||
21 x.email );
22 end loop;
23 end show;
24
25 function add
26 ( FIRST_NAME in employees.FIRST_NAME%type := null,
27 LAST_NAME in employees.LAST_NAME%type,
28 EMAIL in employees.EMAIL%type,
29 COUNTRY_CODE in employees.COUNTRY_CODE%type := null,
30 PHONE# in employees.PHONE#%type := null,
...
37 )
38 return employees.employee_id%type
39 is
40 employee_id employees.employee_id%type;
41 begin
42 insert into employees
43 ( EMPLOYEE_ID, FIRST_NAME, LAST_NAME,
44 EMAIL, COUNTRY_CODE, PHONE#, HIRE_DATE,
45 JOB_ID, SALARY, COMMISSION_PCT,
...
55 end add;
56
57 end;
58 /

Package body created.

現(xiàn)在我們可以看到兩個版本都安裝了,如清單8所示:

代碼清單 8: 查看已安裝的兩個版本的應(yīng)用

SQL> select object_name, object_type, status, edition_name
2 from user_objects_ae
3 where object_name in ( 'EMPLOYEES', 'EMP_PKG' );

OBJECT_NAME OBJECT_TYPE STATUS EDITION_NAME


EMP_PKG PACKAGE BODY VALID ORABASE EMP_PKG PACKAGE VALID ORABASE
EMPLOYEES VIEW VALID ORA$BASE
EMP_PKG PACKAGE BODY VALID VERSION2
EMP_PKG PACKAGE VALID VERSION2
EMPLOYEES VIEW VALID VERSION2

6 rows selected.

安裝返送型跨版本觸發(fā)器:

兩個版本都已經(jīng)安裝了,但還未真正就緒。ORA$BASE中的代碼已經(jīng)準(zhǔn)備好,但VERSION2中的代碼還不是完全到位。如果我們調(diào)用了VERSION2中的EMP_PKG.ADD過程,會怎么樣?它會填寫COUNTRY_CODE和PHONE#列,但不會寫入舊的PHONE_NUMBER列!現(xiàn)在進(jìn)入返送型跨版本觸發(fā)器, 如清單9所示。

代碼清單 9: 創(chuàng)建返送型跨版本觸發(fā)器

SQL> create or replace trigger employees_revxedition
2 before insert or update of country_code,phone# on employees_rt
3 for each row
4 reverse crossedition
5 declare
6 first_dot number;
7 second_dot number;
8 begin
9 if :new.country_code = '+1'
10 then
11 :new.phone_number :=
12 :new.phone#;
13 else
14 :new.phone_number :=
15 '011.' ||
16 substr( :new.country_code, 2 ) ||
17 '.' || :new.phone#;
18 end if;
19 end;
20 /

Trigger created.

現(xiàn)在我們有了一個觸發(fā)器,它僅僅在VERSION2版本應(yīng)用程序的上下文中觸發(fā),并且會將數(shù)據(jù)維護(hù)到舊版本應(yīng)用的格式。它將會存儲不帶國家代碼的美國電話號碼,或者存儲美國格式的國外號碼,采用的是011.國家代碼.電話號碼的格式。只要它準(zhǔn)備就緒,我們就可以使用VERSION2的新應(yīng)用代碼了:

SQL> begin
2 dbms_output.put_line
3 ( emp_pkg.add
4 ( first_name => 'Tom'
5 last_name => 'Kight',
6 email => 'TKYTE',
7 country_code => '+44',
8 phone# => '703.123.4567',
9 hire_date => sysdate,
10 job_id => 'IT_PROG' ) );
11 end;
12 /
502

PL/SQL procedure successfully
completed.

SQL> exec emp_pkg.show( 'Kight' );
Tom Kight +44 703.123.4567 TKYTE

PL/SQL procedure successfully
completed.

我們可以看到數(shù)據(jù)被正確地輸入了。不僅如此,在ORA$BASE版本我們可以驗證舊的數(shù)據(jù)格式依然有效:

SQL> connect demo/demo
Connected.
demo> exec emp_pkg.show( 'Kight' );
Tom Kight 011.44.703.123.4567 TKYTE

請記住,因為數(shù)據(jù)庫的缺省版本是ORA$BASE,任何會話缺省地會使用(看到)這個版本。所以只要簡單地再次登錄,我們就會運(yùn)行舊版本的應(yīng)用程序并且看到舊的PHONE_NUMBER列的輸出格式。

設(shè)置缺省版本:

為了讓新版本成為缺省版本,我們會用一個AFTER LOGON觸發(fā)器來為每個新創(chuàng)建的會話設(shè)置版本。這使得新的會話會看到我們的新版本代碼,同時現(xiàn)有的會話繼續(xù)執(zhí)行舊的版本。因為現(xiàn)有版本繼續(xù)執(zhí)行舊代碼,我們可以完全去除可怕的“ORA-4068 existing state of packages has been discarded”錯誤,在過去每當(dāng)你替換現(xiàn)有的代碼時,你往往會得到這個錯誤。這個功能進(jìn)一步增強(qiáng)了應(yīng)用系統(tǒng)升級的能力。我們不僅僅消除了應(yīng)用升級過程中的停機(jī)時間,而且還消除了切換到新版本的停機(jī)時間。

這個AFTER LOGON觸發(fā)器將會使用DBMS_SESSION包中的一個叫做SET_EDITION_DEFERRED的新的API調(diào)用。這個API切換到指定版本,該版本是一個單獨(dú)參數(shù),而且是以一種延遲的方式,這意味著切換發(fā)生在(登錄之后)的數(shù)據(jù)庫調(diào)用完成之后。會話版本的延遲設(shè)置是必需的,這是為了防止發(fā)生“代碼該做什么?”這樣的問題。如果版本切換在初始化調(diào)用的時候立即發(fā)生,就會出現(xiàn)這種問題。假設(shè)有一段代碼執(zhí)行了這樣的邏輯:

Begin
調(diào)用存儲過程Proc;
立即切換到另一個不同版本;
調(diào)用存儲過程Proc;
End;

如果在執(zhí)行這段代碼的過程中,PROC有兩個不同的版本怎么辦?它應(yīng)該執(zhí)行兩個不同版本的代碼嗎?那怎么可能(又有什么合理性)?并非如此,版本的切換是延遲發(fā)生的,它發(fā)生在初始化調(diào)用之后。那樣的話,所發(fā)生的一切就不會有歧義。

寫代碼來把我們的新版本應(yīng)用設(shè)置為新會話的缺省版本,是很簡單的:

SQL> grant use on
edition version2 to public;

Grant succeeded.

SQL> create or
replace trigger set_edition_on_logon
2 after logon on database
3 begin
4 dbms_session.set_edition_deferred( 'VERSION2' );
5 end;
6 /

Trigger created.

GRANT語句是必不可少的,它使得所有用戶都可見到版本VERSION2(當(dāng)然,你也可以把它授權(quán)給一個更小的用戶群,這取決于你的需求),而觸發(fā)器則讓我們的新版本就緒。現(xiàn)在,每當(dāng)我們登錄到數(shù)據(jù)庫,我們會看到:

SQL> connect demo/demo
Connected.
demo> SELECT SYS_CONTEXT ('userenv','current_edition_name') sc
2 FROM DUAL;

SC

VERSION2

demo> exec emp_pkg.show( 'Kight' );
Tom Kight +44 703.123.4567 TKYTE

PL/SQL procedure successfully
completed.

清理:

現(xiàn)在剩下的工作就是清理。清理必須發(fā)生在所有人都不再使用ORABASE版本之后,也就是沒有任何現(xiàn)存的會話使用舊代碼。我們可以查看數(shù)據(jù)庫里的每個會話正在使用哪個版本,只要查詢VSESSION的SESSION_EDITION_ID列(這個列可以被連接到*_OBJECT視圖的OBJECT_ID, 這樣你就可以看到版本名稱),如果沒有人在使用ORA$BASE,我們就指定可以進(jìn)行清理了。在此情況下,清理包括了:

■刪除轉(zhuǎn)送和返送跨版本觸發(fā)器
■你也可以刪除PHONE_NUMBER列,或者把它設(shè)置為UNUSED

就是這樣。我們已經(jīng)完成了應(yīng)用系統(tǒng)的在線升級!

(完)

[* 本帖最后由 newkid 于 2011-2-15 02:43 編輯 *] |

翻譯, 11gR2, 在線, 版本, 變更

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內(nèi)容