天天看點

PL/SQL專家指南2——PL/SQL精髓

1、入門級概述

用PL/SQL很久了,很多其他搞IT的人問我PL/SQL是什麼,一般我的回答是:1,Oracle的開發語言。2,比SQL複雜了些,多了很多東西(至于什麼東西,現在還搞不清楚,還幼稚的以為:PL/SQL=PLus SQL),今天仔細潛心讀了些書終于完善了這個定義(不是偷懶複制的,是手抄的):

Procedure Language/SQL(PL/SQL) is Oracle Corporation's procedure language extension to SQL, the standard data access language for relational databases.PL/SQL offers software engineering features such as data encapsulation, exception handling, information

hiding, objects orientation, and brings state-of-the-art programming to the Oracle Sever and toolset.

當應用程式(如sql*plus)與資料庫伺服器進行連接配接的時候,使用者程序(user process)使用SQL*NET發送連接配接請求。伺服器上的listener接收這個請求,然後建立一個專用的伺服器程序(dedicated server process)對請求進行處理。

使用者全局區在使用專用伺服器時,UGA位于PGA中,而在運作MTS(共享伺服器)時,UGA位于SGA中。可以利用如下腳本檢視目前UGA的大小:

SELECT   SUBSTR (a.NAME, 9, 10) "Name",

            ROUND (SUM (b.VALUE) / 1024 / 1024,

                   1)

         || ' M' "Total UGA for all sessions"

    FROM v$statname a, v$sesstat b

   WHERE a.statistic# = b.statistic# AND a.NAME = 'session uga memory'

GROUP BY a.NAME;

Name                 Total UGA for all sessions

-------------------- ------------------------------------------

uga memory                 8.7 M

注意,這裡顯示的是對所有活躍會話的統計,如果資料庫配置為專用伺服器,那麼每個程序隻能獲得自己的PGA,是以也隻能獲得自己的UGA大小。是以,要确定配置設定給UGA的所有記憶體空間,唯一的方法就是要确定程序總數。

2、PL/SQL體系結構

oracle中有一個處理PL/SQL程式單元的編譯器,該編譯器首先建立文法樹,然後利用優化器轉化成機器代碼存放在資料庫中,以便稍後執行。9i加入了本地編譯,把代碼轉換為資料庫主機上的C語言共享庫。因為oracle在處理計算密集型的PL/SQL程式時不需要對指令作過多的解釋,是以這類程式的執行效率得到了提高。

在PL/SQL虛拟機和SQL引擎的輔助下,執行碼在PL/SQL引擎上運作。當調用某一程式單元時,經過解釋編譯的代碼将相應的機器代碼(MCode)堆棧加載到SGA中,PL/SQL虛拟機負責處理這些指令,與RDBMS核心進行通信。在SQL引擎的輔助下,PL/SQL引擎執行指令配合SQL語句工作。

如果程式單元使用本地編譯,而不是将機器代碼堆棧加載到SGA中進行編譯,共享庫就加載到PGA中,盡管不需要對代碼做任何解釋,但是這種情況下仍需要PL/SQL虛拟機,同時,PL/SQL和SQL引擎也和做解釋時一樣行使相同的功能。

下面解釋一下SQL引擎和PL/SQL 引擎是什麼?

你可以認為SQL就是DDL或DML語句,而PL/SQL就是function procedure trigger package等程式段,包括匿名塊等。

業務邏輯比較複雜時,單靠SQL就解決不了,需要配合一些程式代碼來實作,比如java或C+SQL。java或C代碼在client上執行,而SQL要在server上執行,client、servr之間需要進行較多的互動,效率較低。

為了提高效率,就有了PL/SQL。client向server發起一個PL/SQL請求,server的PL/SQL引擎就開始執行,在需要時會調用SQL引擎執行

SQL語句。PL/SQL引擎執行完畢後,向client發回結果。plsql中的sql代碼還是交給sql引擎的,隻有plsql代碼交給plsql引擎,然後兩個引擎之間互相聯系,把結果傳到client端

前期綁定和後期綁定:

前期綁定是指在代碼執行前編譯,進行命名空間驗證、權限和文法檢查。由于在編譯時已經完成了大部分工作,這為執行節省了相當多的時間。oracle PL/SQL就是采用的前期綁定

後期綁定是指代碼在執行時編譯,由于在編譯時和執行時都能進行修改,是以使用後期綁定的語言非常靈活。

3、PL/SQL編譯步驟

編譯器前端:

步驟1:文法分析——文法分析的主要功能是生成Diana(descriptive intermediate attribute notation for ada:ada的中間屬性描述符号(ada是一種程式設計語言))。Dinan是描述程式的AST(abstract syntax tree:抽象文法樹),隻要對程式進行編譯,就會建立一個Dinan執行個體并儲存在資料庫中。編譯器在最後完成運作時,(編譯相關對象,分析類型和包的規範說明等任務時)都要用到這個Diana執行個體。

從名稱上看,Diana使用的是Ada程式設計語言,像Ada一樣,oracle PL/SQL使用一種稱為IDL(interface descriptive language:接口描述語言)的元符号定義Diana。oracle中的IDL(接口描述語言)稱為FIDL(fuction IDL,IDL函數)。。。讀到這裡我們可以歸納一下,Diana是由一種接口描述語言來定義的,這種接口描述語言叫FIDL。

Diana是一種樹形結構抽象類型,提供關于plsql代碼的中繼資料。雖然PL/SQL編譯器是使用C語言編寫的,但是Diana有幾個 為庫和其他資料結構  提供資訊的 内置PL/SQL程式包。一個與Diana相關的程式包是DIANA(重名) 。 另一個實用程式包是DIUIL。DIUIL程式包建立腳本diutil.sql。在$ORACLE_HOME/rdbms/admin中(我找到了)。

對于與Diana相關的PL/SQL對象的大小存在一些限制。這些限制不是取決于代碼行的數量,而是取決于代碼生成Diana的節點數量。如何檢測代碼的大小呢?一種方法是檢視user_object_size視圖。這個視圖根據如下幾個表傳回代碼大小:

IDL_CHAR$

IDL_SB4$

IDL_UB1$

IDL_UB2$

上面這些表在資料庫中儲存已編譯的代碼。要在user_object_size中檢測代碼的大小,用下面的程式包示例(也可以在即查詢),我們之是以用這個包,正好可以來查詢這個包在解析中Diana的大小:

CREATE OR REPLACE PACKAGE diana_size

AS

   PROCEDURE get_parsed_size(

      i_object_name IN VARCHAR2, 

      cv_result IN OUT SYS_REFCURSOR);

END diana_size;

/

CREATE OR REPLACE PACKAGE BODY diana_size

      i_object_name IN VARCHAR2,

      cv_result IN OUT SYS_REFCURSOR)

   IS

   BEGIN

      OPEN cv_result FOR

      SELECT name, type, parsed_size

      FROM user_object_size

      WHERE name = i_object_name;

   END get_parsed_size;

END;

SQL> variable x refcursor

SQL> exec diana_size.get_parsed_size('DIANA_SIZE',:x)

PL/SQL 過程已成功完成。

SQL> print x

NAME                            TYPE                          PARSED_SIZE

------------------------------ -------------                  -----------

DIANA_SIZE                     PACKAGE                  320

DIANA_SIZE                     PACKAGE BODY        0

程式包的規範中有一個PARSED_SIZE變量值,但是在程式包的實體中卻不存在。這是什麼原因呢? 原來在資料庫伺服器編譯成功後,會丢棄

根據程式包和對象類型體所生成的Diana。是以他們的大小當然為0.

oracle提供一個很少人知道,并使用過的腳本對Diana進行檢查。可以從$ORACLE_HOME/RDBMS/admin/dumpdian.sql中找到腳本(我找到了)

在文法分析中,除了建立Diana執行個體,還要與RDBMS SQL文法分析器通信來探測和引用綁定AST(抽象文法樹,也就是Diana所描述的東東)。SQL文法分析器對PL/SQL和SQL*plus 的通用性保證率SQL文法在SQL*plus 和 PL/SQL中都是有效的。

在進行文法分析期間要執行的一個更為重要的任務,就是必須用到WRAP使用程式的功能,WRAP隐藏了源代碼(實際上沒有加密)。包裝後的代碼無法由人工解讀,但是對oracle卻不存在這個問題。為了WRAP代碼,Diana以一個位元組數組的形式寫入平面檔案。

步驟2:語義分析——語義分析将在文法分析步驟中生成的AST作為輸入。下面是在語義分析中所要執行的幾個任務:

***完成名稱解析(包括函數重載)

***構造類型繼承的層次結構

***确定代碼的依賴關系

***與RDBMS SQL文法分析器一起,共同對SQL語句進行語義檢查。

由于語義分析步驟并不實際生成代碼,是以這裡最重要的功能就是準備好Diana執行個體,在下一步中送出代碼生成器處理。在完成語義分析後,帶有注釋的AST将被送往代碼生成器。

編譯器後端:

編譯器前端對代碼樹的分析和映射做了大量處理,PL/SQL編譯器後端将嘗試生成和優化用于描述PL/SQL對象的機器代碼。其實,将Diana執行個體轉化為機器代碼。要大緻經過如下步驟:PL/SQL代碼經過文法分析 ——生成Diana執行個體——經過語義分析——生成代碼——由Diana建立IL——局部和全局優化——将IL轉化為MCode

步驟3:代碼生成——代碼生成器輸入的是Diana,輸出的是MCode。

更細一步,代碼生成器在第一階段使用ILGEN(IL Generator:中間語言生成器)将PL/SQL程式的Diana執行個體轉換為IL(intermediate language:中間語言)形式。Diana執行個體在轉換成IL之前會儲存在資料庫中。

第二階段,引入了局部和全局優化器。局部優化器對IL進行掃描,去掉其中許多低效内容,所有PL/SQL對象都要經過局部優化器的快速處理,這對提高最終機器代碼的執行效率是不可或缺的。全局優化器的執行速度就沒有那麼高了,它在局部優化器處理之後啟動,對PL/SQL對象進行控制流程分析、異常處理、去除無效和備援代碼的處理。這兩個優化器的目的都是為了提高最終機器代碼的運作時性能。

第三個階段,也是最後一個階段,将IL轉換為MCode。MCode存放在資料庫中,他就是執行PL/SQL程式單元時的執行碼,使用PL/SQL虛拟機合一解釋執行MCode。