SQL過程的性能:提示和技巧
SQL過程的性能是大家使用數據庫必須要掌握的,可見SQL過程的性能是數據庫中比較重要的知識點,下文中將為大家帶來SQL過程的性能,提示和技巧,希望對大家能夠有所幫助。
但是,有關如何組織存儲過程自身中的邏輯并著眼于其性能的專門建議卻并不多見。本文就提供了這樣一種建議。盡管本文著重于介紹 SQL 過程,但是這里所提供的大多數信息同樣適用于用其它語言編寫的在應用程序中或存儲過程中嵌入的 SQL 邏輯。
背景知識和術語
在深入研究詳細問題之前,讓我們先回顧 DB2 中有關過程化 SQL 的一些基本術語和概念。過程化 SQL 構造(例如標量變量、IF 語句和 WHILE 循環)是在 DB2 Universal Database™ (UDB) V7 發行版中引入 DB2 的。以前的 DB2 發行版支持 C 和 Java™ 作為存儲過程的語言。V7 引入了 SQL 存儲過程,以及其它許多可以促進 OLTP 應用程序開發的特性(例如臨時表、應用程序保存點和標識列)。
當創建 SQL 過程時,DB2 將過程主體中的 SQL 查詢與過程邏輯區分開來。為了使性能最優,SQL 查詢被靜態地編譯成包中的節。(對于靜態編譯的查詢而言,節主要是由 DB2 優化器為該查詢選擇的存取方案構成的。包是節的集合。有關包和節的更多信息,請參閱 DB2 SQL 參考大全,第 1 卷。)另一方面,過程邏輯被編譯成 DLL(動態鏈接庫)。
在過程的執行期間,每當控制從過程邏輯流向 SQL 語句時,在 DLL 和 DB2 引擎之間就存在“上下文切換”。(在 DB2 V8 中,SQL 過程是在“不受保護的方式”下運行的,即與 DB2 引擎在相同的尋址空間中。因此我們這里談及的上下文切換并不是操作系統級別上的完全的上下文切換,而是指 DB2 中層的更換。)減少頻繁調用的過程(例如 OLTP 應用程序中的過程)或者處理大量行的過程(例如執行數據清理的過程)中的上下文切換次數,對它們的性能有顯著的影響。本文中的幾個技巧恰好旨在減少這些上下文切換。
剛開始的時候(DB2 通用數據庫 V7 GA),只允許在 SQL 過程中使用 SQL 過程語言(通常稱為 SQL PL)。后來(在 DB2 UDB V7.2 中),在 SQL 函數和觸發器主體中開始支持該語言的子集。SQL PL 的這個子集即所謂的 內聯(inline)SQL PL。“內聯”一詞突出顯示了它與完整語言的重要區別。SQL PL 過程是通過將其單獨的 SQL 查詢靜態地編譯成包中的節實現的,而內聯 SQL PL 函數就象其名稱所展示的,是通過將函數主體內聯到使用它的查詢中實現的。稍后我們將再看一下內聯 SQL PL 及其用法的一些示例。
現在,讓我們研究在使用 SQL 過程語言時可用來提高性能的一些具體工作。
在只使用一條語句即可做到時避免使用多條語句
讓我們從一個簡單的編碼技巧開始。
如下所示的單個 INSERT 行序列:
INSERT INTO tab_comp VALUES (item1, price1, qty1);
INSERT INTO tab_comp VALUES (item2, price2, qty2);
INSERT INTO tab_comp VALUES (item3, price3, qty3);
可以改寫成:
INSERT INTO tab_comp VALUES (item1, price1, qty1),
(item2, price2, qty2),
(item3, price3, qty3);
執行這個多行 INSERT 語句所需時間大約是執行原來三條語句的三分之一。孤立地看,這一改進看起來似乎是微乎其微的,但是,如果這一代碼段是重復執行的(例如該代碼段位于循環體或觸發器體中),那么改進是非常顯著的。
類似地,如下所示的 SET 語句序列:
SET A = expr1;
SET B = expr2;
SET C = expr3;
可以寫成一條 VALUES 語句:
VALUES expr1, expr2, expr3 INTO A, B, C;
如果任何兩條語句之間都沒有相關性,那么這一轉換保留了原始序列的語義。為了說明這一點,請考慮:
SET A = monthly_avg * 12;
SET B = (A / 2) * correction_factor;
將上面兩條語句轉換成:
VALUES (monthly_avg * 12, (A / 2) * correction_factor) INTO A, B;
不會保留原始的語義,因為是以“并行”方式對 INTO 關鍵字之前的表達式進行求值的。這意味著賦給 B 的值并不以賦給 A 的值為基礎,這是原始語句預期的語義。
從多個 SQL 語句到一個 SQL 表達式
跟其它編程語言一樣,SQL 語言提供了兩類條件構造:過程型(IF 和 CASE 語句)和函數型(CASE 表達式)。在大多數環境中,可使用任何一種構造來表達計算,到底使用哪一種只是喜好問題。但是,使用 CASE 表達式編寫的邏輯不但比使用 CASE 或 IF 語句編寫的邏輯更緊湊,而且更有效。
請考慮下面的 SQL PL 代碼片段:
IF (Price <= MaxPrice) THEN
INSERT INTO tab_comp(Id, Val) VALUES(Oid, Price);
ELSE
INSERT INTO tab_comp(Id, Val) VALUES(Oid, MaxPrice);
END IF;
IF 子句中的條件僅用于決定將什么值插入 tab_comp.Val 列中。為了避免過程層和數據流層之間的上下文切換,可利用 CASE 表達式將相同的邏輯表示成一個 INSERT 語句:
INSERT INTO tab_comp(Id, Val)
VALUES(Oid,
CASE
WHEN (Price <= MaxPrice) THEN Price
ELSE MaxPrice
END);
值得注意的是,CASE 表達式可在任何希望有標量值的上下文中使用。特別地,可在賦值符號的右邊使用它們。例如:
IF (Name IS NOT NULL) THEN
SET ProdName = Name;
ELSEIF (NameStr IS NOT NULL) THEN
SET ProdName = NameStr;
ELSE
SET ProdName = DefaultName;
END IF;
可以改寫成:
SET ProdName = (CASE
WHEN (Name IS NOT NULL) THEN Name
WHEN (NameStr IS NOT NULL) THEN NameStr
ELSE DefaultName
END);
實際上,這個特殊的示例有一個更好的解決方案:
SET ProdName = COALESCE(Name, NameStr, DefaultName);
到這里關于SQL過程的性能提示和技巧的知識就為大家講解完了,相信大家通過上文的學習,現在對 SQL過程的性能有所了解,希望大家都能夠從文中有所收獲。