您是否正將您的數據庫從Oracle遷移至 IBM® DB2® Universal Database™?您是否認為可能會丟失 Oracle 的某個特殊功能?DB2 UDB 就是這樣一種十分靈活的數據庫,它可以滿足您的許多需要。在本文中,我將介紹一下如何實現一個稱為警告的功能。警告用於允許多重會話之間的通信。
警告概述
如圖1中所顯示,警告是為在會話間傳遞消息而設計的。警告標識、會話標識和消息存儲在表中(這裡是 alerts.dbms_alert_info)。消息由一個會話發送再由另一個會話讀取,這個過程由一個在指定表中設置的標志來控制。在設計參數(像列長度、主機變量長度、輪詢時間間隔等)時需要了解 Oracle 到 DB2 的遷移情況。您可以根據需要增加警告名和消息長度的大小。
圖 1. 警告在會話間發送消息
設計概述
以下對象在實現類似 Oracle 具有的警告功能時都是必需的。所有對象都包含在本文的 下載部分中:
存儲警告信息的表: alerts.dbms_alert_info
注冊警告的存儲過程: alerts.register
刪除警告的存儲過程: alerts.remove和 alerts.delete
發送警告消息的存儲過程:alerts.signal
接收警告消息的存儲過程: alerts.waitone和 alerts.waitany
實現延遲的 UDF: alerts.delay
實現會話標識的 UDF: alerts.session_id
您可以更改我的設計以滿足自己的特殊要求。所有對象都使用了一個共同的模式,即警告。設計存儲過程以便您可以擴展它們來捕獲 SQLCODE、SQLSTATE 和 ERROR_MESSAGE。大量的異常處理在上面列出的所有存儲過程中實現,但是這裡並沒有使用到。您可以取消樣本代碼中異常處理部分的注釋然後再使用該部分。它們也可以返回相應的返回碼,以便用主語言實現必需的操作。游標可以在所有存儲過程中實現,但是在一些地方您可能希望更改為 fullselect。所有警告對象都被授予了公共執行(Public execute)許可權。您應該使用 DBADM 或 SYSADM 權限創建所有這些對象。
存儲警告信息:alerts.dbms._alert_info
目標:該表存儲警告標識、會話標識、消息和標志。該標志是一個指示符,表明一條消息是否已經由所有者會話放置在表中。
該表有四列:
NAME
該列(主鍵)存儲了警告名的名稱。在 Oracle 中,name 是一個可變長度為 30 個字符的字段,然而這裡我們將它定義為 varchar(60)。當然您可以擴展您的命名模式。
SID
這是會話標識。使用一個用戶定義的函數(session_id())來標識會話標識。
MESSAGE
用戶會話發送的警告消息正文,這個用戶會話需要和其他會話進行通信。它定義為使用 1800 個字符,但是如果您希望的話,可以擴展它的長度。
CHANGED
這是前面描述的指示符標志。缺省值是“N”。
授權:授予 PUBLIC 的權限包括:SELECT、INSERT、UPDATE、DELETE
CREATE TABLE ALERTS.DBMS_ALERT_INFO (
NAME VARCHAR(60) NOT NULL ,
SID VARCHAR(50) NOT NULL,
MESSAGE VARCHAR(1800) ,
CHANGED CHAR(1) NOT NULL WITH DEFAULT 'N',
CONSTRAINT PK_DBMS_ALERT_INFO PRIMARY KEY (NAME),
CONSTRAINT CC_DBMS_ALERT_INFO CHECK (CHANGED IN ('Y','N','y','n')));
注冊警告:alerts.register
目標:該存儲過程注冊警告名。
相關性:session_id() UDF
輸入參數:警告名(最大長度為 60 個字符)
輸出參數:SQLCODE、SQLSTATE 和 ERROR_MESSAGE(以備擴展使用)
授權:將 EXECUTE 授權給 PUBLIC
功能行為:該存儲過程注冊警告名和會話標識並存儲這些內容。它將 MESSAGE 字段設置為 null 並將 CHANGED 字段設置為“N”。成功的話返回 0;否則返回 -1。
等待並讀取特定警告:alerts.waitone
目標:存儲過程等待並從指定警告名中讀取警告消息。
相關性:delay() UDF
輸入參數:警告名(最大長度為 60 個字符)
輸出參數:SQLCODE、SQLSTATE、ERROR_MESSAGE(以備擴展使用)、警告消息和超時狀態。
授權:將 EXECUTE 授權給 PUBLIC
功能行為:該存儲過程首先檢查指定警告名是否存在以及其標志是否設置為“Y”。這個標志意味著此會話可以從一個注冊警告接收警告消息,因為另一個會話已經發送了一條消息,而此會話正在查找來自那個會話的消息。
這個存儲過程等待警告消息。如果該警告沒有消息,它將等待一個預定義的時間間隔然後再檢查。缺省輪詢時間間隔是 5 秒,但是如果超時輸入參數設置為少於 5 秒或不是 5 秒的倍數,則輪詢時間間隔將設置為 1 秒。閱讀完消息以後,此存儲過程清除消息字段並將標志重新設置為“N”以使其不會阻塞其它會話。現在其它會話可以使用注冊警告發送消息。如果成功的話返回 0;否則返回 -1。
等待並讀取任何警告消息:alerts.waitany
目標:等待並從任意警告名中讀取警告消息的存儲過程。
相關性:delay() UDF
輸入參數:超時值,按秒計。
輸出參數:SQLCODE、SQLSTATE、ERROR_MESSAGE(以備擴展使用)、發出消息的警告名、消息和超時狀態。
授權:將 EXECUTE 授權給 PUBLIC
功能行為:waitone 和 waitany 存儲過程的差別在於,waitany 從 任何警告名中查找消息並將該警告名作為輸出參數返回。waitone 從指定警告名查找消息。
這兩個存儲過程的另一個不同就是輪詢時間間隔。對於 waitany,默認的輪詢時間間隔設置為 1 秒並且在每個輪詢周期中按指數級增加。如果超時時間間隔小於某個輪詢周期,則在開始此輪詢周期之前,此存儲過程將以該超時狀態結束。如果成功的話返回 0;否則返回 -1。
延遲執行:alerts.delay
目標:DB2 UDB 中沒有任何諸如“delay”或是“sleep”的系統調用,因此我們無法通過它們來將執行暫掛一段指定時間。該 UDF 通過運行一個在 Java™ 中實現的稱為“sleep”的系統調用,以在執行中實現延遲。
相關性:無
輸入參數:延遲時間,以秒計。
輸出參數:0(成功),-1(失敗)
授權:將 EXECUTE 授權給 PUBLIC
功能行為:該函數使用 OS 調用來實現延遲,這比其它任何一種類型的實現都更加高效。
目錄語句: CREATE FUNCTION delay(INTEGER)
RETURNS INTEGER
EXTERNAL NAME 'delayUDFjar:delayUDF.DELAY(INTEGER)'
LANGUAGE Java
PARAMETER STYLE Java
NOT DETERMINISTIC
NO SQL
FENCED
RETURNS NULL ON NULL INPUT
EXTERNAL ACTION;
捕獲會話標識:alerts.session_id
目標:捕獲會話標識的用戶定義的函數。
相關性:無
輸入參數:延遲時間,以秒計。
輸出參數:0(成功),-1(失敗)
授權:將 EXECUTE 授權給 PUBLIC
功能行為:該函數捕獲存儲在 SQLUDF_DBINFO 內部結構中的會話標識。
目錄語句: CREATE FUNCTION SESSION_ID() RETURNS CHAR(34)
EXTERNAL NAME 'sessionUDF!SESSION_ID'
FENCED
LANGUAGE C
PARAMETER STYLE DB2SQL
NOT VARIANT
NO SQL
NO EXTERNAL ACTION
DBINFO;
安裝警告樣本
必備軟件:Microsoft VC++ v6.0 和 JDK v1.3.1。必須在操作系統環境設置中啟用這兩個編譯器路徑。
1.將文件 alert.zip 解壓縮至一個文件夾中,例如 C:tempalert
2.打開 alertsetup.bat 批處理文件並用適當的值編輯以下兩行。
·在 DB2HOMEPATH=C:SQLLIB 中,適當地更改 SQLLIB 路徑。
·在以下行中,更改路徑至 C:tempalertdelayUDF.jar :
DB2 CALL SQLJ.INSTALL_JAR('file:C:Documents and SettingssudiptaMy Documentsdb2KnowledgeBasearticlealertdelayUDF.jar','delayUDFjar')
3.在 DB2 Command Window 中運行批處理文件:
alertsetup.bat
例如: C:tempalert>alertsetup.bat SAMPLE TEST TEST01
DB2 UDB V8 測試過程
1.打開兩個 DB2 COMMAND WINDOW 會話(啟用了 DB2 環境的命令窗口)。讓我們將一個會話表示為“會話 A”,另一個表示為“會話 B”。
2.在兩個會話中,連接至 SAMPLE 數據庫。
3.在會話 A 中執行:
db2 call alerts.register('abc')
4.在會話 A 中執行:
db2 call alerts.signal('abc','Hello World from Session A.....')
5.在會話 B 中執行:
db2 call alerts.waitone('abc',?, ?, 30)
6.在會話 A 中執行:
db2 call alerts.register('xyz')
7.在會話 A 中執行:
db2 call alerts.signal('xyz','Message through alert xyz....')
8.在會話 B 中執行:
db2 call alerts.waitany(?,?,?,30)
9.在會話 B 中執行:
db2 call alerts.remove('abc')
這條語句將失敗,因為會話 B 不是名為“abc”的警告的創建者。
10.在會話 A 中執行同樣的命令:
db2 call alerts.remove('abc')
這次將成功。
11.在會話 B 中執行:
db2 call alerts.delete('xyz')
這次將成功,因為 alerts.delete 設計為可以從任何會話刪除任何注冊警告。
12.在兩個 CLP 會話中執行 db2 terminate 以終止連接。