這幾天看了下 oracle pl/sql 實戰 這本書,出於對sql語句效能的研究,先研究下游標對**的影響。
程式宣告了乙個游標c1,然後用游標for迴圈隱式地開啟了這個游標,對從游標c1取出的每一行,程式查詢customers表,並把first_name和last_name的值填充到變數,隨後插入一行資料到top_sales_customers表。
問題1:
**清單1-1的程式設計方法很有問題。即使在迴圈中呼叫的sql語句是高度優化的,程式的執行還是會消耗大量時間。假設查詢customers表的sql語句消耗0.1秒,insert語句也消耗0.1秒,那麼在迴圈中每次執行就要0.2秒。如果游標c1取出了100 000行,那麼總時間就是100 000乘以0.2秒,即20 000秒,也就是大約5.5小時。很難去優化這個程式的結構。基於顯而易見的理由,tomkyte把這種處理方式定義為慢之又慢的處理(slow-by-slow processing)。
問題2:
**清單1-1的**還有乙個固有的問題。從pl/sql的迴圈中呼叫的sql語句會反覆在pl/sql引擎和sql引擎之間切換執行,這種兩個環境之間的切換稱作上下文切換。上下文切換增加了程式執行的時間,並增加不必要的cpu開銷。你應當通過消除或減少這種兩個環境之間的切換來減少上下文切換的次數。一般應當禁止逐行處理,更好的程式設計實踐是把**清單1-1的程式轉換成乙個sql語句。**清單1-2重寫了**,完全避免了pl/sql
**清單1-2除了解決逐行處理的缺陷以外,還有更多的優勢。重寫後的sql語句可以使用並行執行來調優,使用多個並行執行程序可以大幅地減少執行時間。並且,**變得簡明且可讀性強。
在**清單1-3中,c1、c2和c3是巢狀游標。游標c1是頂級游標,從表t1取得資料,c2是開放游標,傳遞從游標c1取得的值,c3也是開放游標,傳遞游標c2取得的值。有乙個update語句對游標c3返回的每一行執行一次。儘管update語句已經優化為執行一次只要0.01秒,但程式的效能還是會由於深度巢狀游標而難以忍受的。假設游標c1、c2和c3分別返回20、50和100行,那麼上述**需要迴圈100 000行,程式的總執行時間超過了1000秒。對這類程式的調優通常需要完全重寫它。
**清單1-3中**的另乙個問題在於先執行乙個update語句。如果update 語句產生了
no_data_found異常④,那麼再執行乙個insert語句。這種型別的問題可以利用merge語句從pl/sql
轉到sql引擎處理。
從概念上講,**清單1-3中的三重迴圈表示表t1、t2和t3之間的等值連線。**清單1-4展示
了根據上述邏輯改寫的使用表別名t的sql語句。update和insert邏輯的結合用merge語句代替,
merge語法提供了更新存在的行和插入不存在的行的功能。
不要在pl/sql語言中編寫深度巢狀游標的**。審查這類**的邏輯,看是否能用sql語句來代替。
查詢式查詢(lookup query)一般用於填充某些變數或執行資料的合法驗證。但在迴圈中執行查詢式查詢會導致效能問題。在**清單1-5中,高亮顯示的部分就是使用查詢式查詢來得到country_name值。程式對游標c1中的每一行都要執行乙個查詢來取得country_name的值。當從游標c1中取得的行數增加時,執行查詢式查詢的次數也增加了,這導致**的效率低下。
**清單1-5的**是過分簡化的,對country_name的查詢式查詢實際上可以重寫為主游標c1本身中的乙個連線。第一步,應將查詢式查詢修改為連線,可是在實際的應用程式中,並不一定可以實現這種改寫。如果無法利用改寫**來減少查詢式查詢的執行次數,那麼還有另乙個選擇。你可以定義乙個關聯陣列來快取查詢式查詢的結果,以便在隨後的執行中重用這個陣列,這樣也能有效地減少查詢式查詢的執行。**清單1-6演示了陣列快取技術。不必再在游標c1返回的每一行中執行查詢來得到country_name,而是用乙個名為l_country_names的關聯陣列來儲存本例中的country_id 和country_name鍵—值對。關聯陣列和索引類似,任意給定的值都可以通過乙個鍵值來訪問。在執行查詢式查詢前,通過exists操作對乙個陣列中是否存在乙個匹配country_id鍵值的元素做乙個存在性驗證,如果陣列中存在這麼乙個元素,那麼country_name就從陣列中獲取而不需
要執行查詢式查詢。如果沒有這樣的元素,那麼就執行查詢式查詢,並且把查到的結果作為乙個新元素存入陣列。你還需要理解,這種技術非常適用於不同的鍵值很少的語句,在本例中,當country_id列的唯一值個數越少時,查詢式查詢的執行次數可能也越少。如使用示例模式,執行查詢式查詢的次數最多是23,因為country_id列只有23個不同的值。
注意 關聯陣列所需記憶體是在資料庫伺服器中專用伺服器程序的pga(program global area,程式全域性區)中分配的,如果數千個連線都要把程式的中間結果快取到陣列中,那麼記憶體的占用將會大幅增加。你應當掌握每個程序的記憶體使用增加情況,並設計資料庫伺服器以適應記憶體的增長。
Oracle PL SQL學習筆記
游標分類 游標概念 與游標相關的語法型別 建立游標 如同宣告任何其他變數一樣 cursor cursor name is sql statement 開啟與關閉游標 開啟游標 open cursor arg arg 關閉游標 close cursor 取資料 fetch cursor into va...
Oracle PL SQL 學習筆記
一 pl sql 塊 塊定義語法 declare 定義部分 定義常量 變數 複雜資料型別 游標 begin 執行部分 pl sql語句和sql語句 exception 異常處理部分 處理執行錯誤 end 注 pl sql中結束輸入使用 字元 dbms output.put line 輸出的字串內容 ...
Oracle PL SQL游標的學習
一 游標是什麼 游標字面理解就是游動的游標。用資料庫語言來描述 游標是對映在結果集中一行資料上的位置實體,有了游標,使用者就可以訪問結果集中的任意一行資料了,將游標放置到某行後,即可對該行資料進行操作,例如提取當前行的資料等。二 游標的分類 顯式游標和隱式游標 顯式游標的使用需要4步 1.宣告游標 ...