在之前的專欄中,我提到過不必要的 SQL。目標是讓程序員停下來想想每條語句,確定在某個特定的時間它是完全必要的。我展示了兩類 SQL 示例:一類是可以完全去掉的 SQL;另一類 SQL 可以延遲到之後某個合適的時間,然後只在仍然必要的時候執行。
在本期的專欄中,我將繼續提供一些 SQL 示例,包括執行太過頻繁以及可以使用新的、更好的 SQL 替換的 SQL 示例。現在,歡迎進入減少與 IBM DB2 會話的第二部分(共三部分)。
經過驗證的技術
控件中斷邏輯
編寫程序,尤其是批量程序時我們應該遵守的性能基本規則之一是,在可能時使用控件中斷邏輯。這不是一個專門的 DB2 概念 —— 它只是一個好的編程實踐。查看控件中斷或值的更改,可以減少與 DB2 的連接,因為您只有在擔心它與上次查詢的值不同時才在表格中查詢值。
優化控件中斷邏輯要求以特定的順序輸入數據;例如,ITEM 位於 STORE 中,STORE 位於 REGION 中。但是,即使輸入數據以特定的順序存儲,也可以使用中斷邏輯,確保 REGION 需要的任何 SQL 都只對每個 REGION 執行一次;對於 STORE,只對該 REGION 的每個 STORE 執行一次;對於 ITEM,只對該 STORE 中的每個 ITEM 執行一次。
順便提一下,DB2 執行的引用完整性不使用控件中斷邏輯。對每個 INSERT、UPDATE、DELETE 和 MERGE 檢查外鍵值,即使目標列值與之前執行了 INSERT(等操作)的行中的值相同。使用程序執行的引用完整性時,我們可以選擇只在目標列值與前一個行中的值不同時進行驗證;即在控件中斷時驗證。
將小型引用表預先加載到程序內存
性能優化的另一個基本規則是將小型引用表預先加載到工作存儲表(當這樣做恰當時),以避免不必要地多次連接 DB2。
一個不那麼和藹的人曾經問我:“哪種破產品會將值加載到工作存儲庫以避免連接到該產品?”我想了一會後回答:“那種能夠認識到程序員有選擇且有腦子,並理解避免不必要的調用、連接和獲取頁面是一個好主意的產品。”由於 DB2 很快且有緩沖池,可以減少實際的 I/O 並不意味著要連接 DB2 100 萬次來讀取 10 行的表格,與此相比,連接 DB2 一次就將 10 行讀取到工作存儲表,然後尋址程序內存 100 萬次要聰明的多。
減少重復執行嵌套選擇的次數
考慮以下 SQL:
Select col1, col2, col3
From big_table where item = :hv-item-just-read
And big_table.deptno in (select deptno from little_dept-table
Where division = Eastern)
對於輸入序列文件的每個選項,程序邏輯需要在一個大表格中進行查詢,以獲取 col1、col2 和 col3 的相關值。正如上文所述,希望輸入的數據集按照項目編號進行排序,然後查看程序以了解該項目編號是否與查詢完成前的編號有所不同。
但讓我們更深入的查看 SQL。嵌套選擇在 Eastern 分區創建一個部門列表,然後確保目標部門位於該列表中。我保證不需要太多精力就可以立即創建 “Eastern 分區的部門” 列表,然後可以執行查詢,無需連接到 DB2。例如,我們可以使用 V8 行集定位指針連接 DB2 並獲取列表;然後可以將 SQL 更改為硬編碼列表,可以從我們的數組構建:
Declare CursorDept with rowset positioning for
Select deptno from little_dept-table
Where division = Eastern)
Fetch next rowset from CursorDept
For 20 rows
Into :hvarray-deptno
(code to ensure that +100 was reached)
Select col1, col2, col3
From big_table where item = :hv-item-just-read
And big_table.deptno in (:hvdept1, :hvdept2, :hvdept3....:hvdept20
記住,如果部門數少於 20 個(比如 15),您不需要編碼動態 SQL,您可以在 16 到 20 個主機變量中保留最新的值。
在更新/刪除 —— 獲取/更新/獲取/更新之前讀取行
更有經驗的程序員必須克服一些根深蒂固的做法。在以前的日子裡,我們必須讀取記錄來更新或刪除。使用 DB2,如果您不需要建立在之前的圖像,就不需要讀取行來更新和刪除它。我常常看到一些批處理程序使用指針獲取、更新、獲取、更新、獲取、更新...,您要做的只是使用 Declare Cursor 中相同的 Where 子句執行合理大小的 SET 更新。而不是:
Declare CursorUpd for
Select col1, col2, ...
From tableA
Where jobcode = :hvj for update of salary
Fetch CursorUpd into :hvcol1, :hvcol2, ...
Update salary set salary = salary + 1000.00 where current of CursorUpd
如果需要重復進行獲取、更新、獲取更新,為什麼不:
Update tableA
Set salary = salary + 1000.00
Where jobcode = :hvj
SET 處理是關系設計的最大優勢之一。您當然必須確保 SET 大小沒有使用鎖定級聯創建無法接受的恢復單元或問題,或者導致其他並發運行程序的鎖定問題(比如超時)。
有時候我看到一些上述場景(獲取、更新)混合了更新行的一個後續單一實例 SELECT —— 之前讀取,更新,之後查看。使用最新的編程技術,同時意識到不需要查看之前圖像,我們可以 DECLARE 一個行定位指針,來 SELECT 我們的 SET update,然後 FETCH 一個合理大小的行集合(比如一次 100 行),以查看維護的結果。
單行獲取的使用很少。當您可以連接一次就看到 100 行時為何要連接 100 次?
請關注第 3 部分
在下一期中,我將繼續本主題的最後部分,我們將查看更多(更新)的技術,來避免或減少與 DB2 的連接。