優化你的PowerBuilder程序

優化你的PowerBuilder程序

(加入日期:2001-6-1)

保存文章至硬盤】【打印文章】【字體:

分享到: 0
    一段程序,完成了所需要的功能固然重要,但是提高執行效率,提高代碼的可重複利用性,提高程序的可讀性也是很重要的。剛剛讀了一個前輩的帖子,感覺受益菲淺,拿將出來與大家共享

                優化你的PowerBuilder程序
一、處理 SQL 語句
1、緩衝 SQL 語句
   在應用程序中,有時需要反覆調用同一組 SQL語句,在這種情況下,可以通過在應用中爲這些 SQL 建立緩衝區來提高執行性能。在缺省情況下,SQL 語句的緩衝區是關閉的,你可以通過如下語句打開它:
SQLCACHE = n
        n 表示裝入緩衝區的 SQL 語句數量(缺省爲0)。
例如:
dw_1.SetTransObject(sqlca)
SQLCA.dbParm = "SQLCache = 0"
dw_1.retrieve()
    如果將上例的 "SQLCache = 0" 改爲 "SQLCache = 25",此句的執行效率將提高五分之一左右。但應注意緩衝區的大小,否則也將影響程序執行的性能。
    注:此方法對用 ODBC 和 ORACLE 連接的數據庫非常有效。
2、捆綁變量
    請看下例:
    SQLCA.DBPARM = "DISABLEBIND=1"
    INSERT INTO DA_DH VALUES("1","河南0")
    INSERT INTO DA_DH VALUES("2","河南1")
    INSERT INTO DA_DH VALUES("3","河南2")
    INSERT INTO DA_DH VALUES("4","河南3")
    INSERT INTO DA_DH VALUES("5","河南4")
    INSERT INTO DA_DH VALUES("6","河南5")  
這裏未使用捆綁變量,在插入是 PB 將重新處理每個帶有新值的SQL語句。如果將上例改爲:
SQLCA.DBPARM = "DISABLEBIND=0"
    INSERT INTO DA_DH VALUES("1","河南0")
    INSERT INTO DA_DH VALUES("2","河南1")
    INSERT INTO DA_DH VALUES("3","河南2")
    INSERT INTO DA_DH VALUES("4","河南3")
    INSERT INTO DA_DH VALUES("5","河南4")
    INSERT INTO DA_DH VALUES("6","河南5")  
    則系統將把 INSERT 語句按如下格式進行處理:
        INSERT INTO DA_DH VALUES(?,?)
    其中 "?" 稱爲佔位符。系統性能將有所增強。
3、用數據窗口代替 SQL 語句
    通常,爲了獲得某些數據,採用數據窗口和 SQL 語句都是可行的,但是PB 對數據窗口和 SQL 語句採用不同的處理機制,因此,具有不同的效率。
例:爲裏檢索電話檔案中的用戶名,可以利用 SQL 語句,將所有的數據檢索到一個多行編輯中,也可以檢索到一個數據窗口中。如果使用第一中方法:首先定義一個遊標:
    DECLARE CUR CURSOR FOR  
    SELECT "DA_DH"."HM"
    FROM "DA_DH";
然後可以:
    STRING stxt[],st
    int li
        open cur
    do li = li + 1
        fetch cur
        into :stxt[li] ;
        st=st+stxt[li] + "~r~n"
    loop while stxt[li]<>""
    close cur;
    mle_1.txt = st
也可以使用第二種方法:  
    dw_1.settransobject(sqlca)
    dw_1.retrieve()
    利用 POWERBUILDER PROFILER 工具進行檢查,對比兩種方法所需時間如下
方法                     所需時間 (百分之一秒)
SQL 語句       100.9857
數據窗口                49.0133  
  由於數據窗口或 DATASTORE 使用了標準的內嵌的代碼,而不是有開發人員進行全部編碼,同時編譯執行的速度比解釋執行的速度快的多,因此在開發過程中應儘量使用數據窗口和DATASTORE..即使是必須用 SQL 語句的時候,也應該儘量將它們定義爲存儲過程(特別是在多用戶的環境中),以提高應用程序的性能. 

二、數據窗口的編程和執行
    數據窗口是PB最值得被稱道的, 其具有如下特點:
1. 多種顯示方式.
2. 多種編輯方式.
3. 使用方法簡單.
4. 具有多種報表形式.
5. 可實現屏幕滾動.
6. 可實現數據的有效性校驗.
7. 執行性能顯著提高.
8. 編程工作變少.
9. 可以在數據窗口內部實現數據哭的更新.
下面, 我將介紹一些用於提高數據窗口性能的技術.
1. 減少連接數據庫的次數
連庫操作是非常影響執行速度的操作. 因此在程序中,一旦與數據庫連接後就應當儘量保持與數據庫的連接, 減少連接數據庫的次數.PowerBuilder 提供裏兩個函數來建立數據窗口與事務對象的連接:
        SetTrans()  
        SetTransObject()
在程序中應當儘量使用 SETTRANSOBJECT(), 因爲SETTRANS() 函數在每次調用 RETRIEVE(), UPDATE() 等函數之後, 都要執行數據庫的連接和斷開操作.
2. 下拉數據窗口與表的連接
    對於數據庫服務器來說, 表的連接操作是一項非常大的開銷, 而 POWERBUILDER 提供的下拉數據窗口在某些情況下可以代替表的連接操作.例如, 爲了在數據窗口上顯示用戶的電話號碼和姓名:如果用表的連接的方法, 數據窗口對應的 SQL 語句應是這樣的:
        SELECT "DA_DH"."DHHM",
                "DA_HTH"."DWM"
        FROM "DA_HTH",
        "DA_DH"
        WHERE ("DA_HTH"."DHHM"="DA_DH"."DHHM")
同樣的程序可用下拉數據窗口來完成, 這裏不再具體介紹.但是, 應當注意, 以上兩種方法究竟哪一種數據更快, 與表的結構, 表的數量, 連接的方法等均有關係, 應當具體分析.
3. 共享數據
在一個應用程序中, 某些數據需要頻繁的使用, 如果在每次使用時都從數據庫中進行檢索, 則需佔用大量的服務器資源和網絡資源. 爲了減少開銷, 可以在客戶端對這些數據只進行一次檢索, 然後允許其它任務共享這些數據.  
    例如, 如果有兩個數據窗口, 都需要從第三方表中檢索出用戶的電話號碼, 且此列用下拉數據窗口給出. 如果每次都對電話號碼進行檢索, 則性能較低. 因此, 可以單獨建立一個關於電話號碼的數據窗口. 在每次打開窗口時, 首先將電話號碼檢索到此數據窗口中, 然後另外兩個數據窗口中關於電話號碼的下拉數據窗口可以共享此數據窗口中的數據.
在窗口的 OPEN 事件中編寫如下程序:
        dw_1.settransobject(sqlca)
        dw_2.settransobject(sqlca)  
        dw_3.settransobject(sqlca)  
        // 檢索 dw_1  
        dw_1.retrieve()
        // 使 DW_2 的下拉數據窗口共享 DW_1
        datawindowchild child1
        dw_2.getchild('dhhm',child1)
        child1.settransobject(sqlca)
        dw_1.sharedata(child1)
        // 使 DW_2 的下拉數據窗口共享 DW_1
        datawindowchild child2
        dw_3.getchild('dhhm',child2)
        child2.settransobject(sqlca)
        dw_1.sharedata(child1)
    使用這種方法, 避免了各個數據窗口間物理的拷貝數據, 因此減少了空間上的開銷,提高了應用程序的綜合性能.
4. 數據窗口間數據的拷貝
    如果需要在數據窗口間共享數據, 應當儘量使用 SHAREDATA() 函數, 但是, SHAREDATA() 函數並不是物理地在數據窗口間拷貝數據, 如工在顯示數據的同時, 還要對數據進行操作, 則需要進行數據的拷貝.
例如, 要求將 DW_1 中選定的行拷貝到 DW_2 中:
    在窗口的 OPEN 事件中編程:
        dw_1.settransobject(sqlca)
        dw_2.settransobject(sqlca)
        dw_1.retrieve()
    在數據窗口 DW_1 的 ROWFOCUSCHANGED 事件中編寫下列程序:  
        long lr  
        lr = dw_1.selectrow(0,false)
        lr = dw_1.getrow()
        lr = dw_1.selectrow(lr,true)
要完成從 DW_1 到 DW_2 的拷貝工作, 有兩種方法:
第一種:  
在按鈕 "拷貝" 的 CLICKED 事件中編程
long lr  
lr = dw_1.getselectedrow(0)
dw_1.rowscopy(lr,lr,primary!,dw_2,100,primary!)
執行程序, 利用 POWERBUILDER PROFILER 得出所需時間爲 1.7034(百分之一秒)
第二種:  
在按鈕 "拷貝" 的 CLICKED 事件中編程
dw_2.object.data = da_1.object.data.selected
執行程序, 利用 POWERBUILDER PROFILER 得出所需時間爲 0.8062(百分之一秒)
5. 數據窗口屬性的訪問和修改
A. 數據窗口屬性的訪問
    在程序中訪問數據窗口的屬性有下列幾種方法:
    A1. 採用點表達式訪問
    A2. 應用多個獨立的 DESCRIBE() 函數訪問
    A3. 只使用一個 DESCRIBE() 函數, 採用複合參數訪問多個屬性
上面三中方法, 通常第一種方法最慢, 第三種方法最快.  
例如:  
    在窗口的 OPEN 事件中
    DW_1.SETTRANSOBJECT(SQLCA)
    DW_1.RETRIEVE()
第一種方法:  
在檢索按鈕的 CLICKED 事件中編程.
    string dx, dy, dh, dw
    dx = dw_1.object.da_dh.x
    dy = dw_1.object.da_dh.y
    dx = dw_1.object.da_dh.height
    dy = dw_1.object.da_dh.width
    st_1.text =dx+","+dy+","+dh+","+dw
第二種方法:
    string dx, dy, dh, dw
    dx=dw_1.describe("da_dh.x")
    dx=dw_1.describe("da_dh.y")
    dx=dw_1.describe("da_dh.height")
    dx=dw_1.describe("da_dh.width")
    st_1.text =dx+","+dy+","+dh+","+dw
第三種方法:
    string dx, dy, dh, dw
    st_1.text=dw_1.describe("da_dh.x" +&
                            "da_dh.y" +&
                            "da_dh.height" +&
                            "da_dh.width")
實驗證明, 第三種方法的速度是最快的. 但是程序的可讀性最差.
B. 數據窗口屬性的修改
在程序中修改數據窗口的屬性有下列幾種方法:
    A1. 採用點表達式修改
    A2. 應用多個獨立的 MODIFY() 函數訪問
    A3. 只使用一個 MODIFY() 函數, 採用複合參數訪問多個屬性
上面三中方法, 通常第一種方法最慢, 第三種方法最快.  
例如:  
    在窗口的 OPEN 事件中
    DW_1.SETTRANSOBJECT(SQLCA)
    DW_1.RETRIEVE()
第一種方法:  
在檢索按鈕的 CLICKED 事件中編程.
    DW_1.SETREDRAW(FALSE)
    dw_1.object.da_dh.x = 18
    dw_1.object.da_dh.y = 16
    dw_1.object.da_dh.height = 100
    dw_1.object.da_dh.width = 200
    DW_1.setredraw(true)
    st_1.text =dx+","+dy+","+dh+","+dw
第二種方法:
    DW_1.SETREDRAW(FALSE)
    dw_1.modify("da_dh.x = 18")
    dw_1.modify("da_dh.y = 16")
    dw_1.modify("da_dh.height = 100")
    dw_1.modify("da_dh.width = 200")
    dw_1.setredraw(true)
第三種方法:
    dw_1.modify("da_dh.x=18" +&
                "da_dh.y=16" +&
                "da_dh.height=100" +&
                "da_dh.width=200")
實驗證明, 第三種方法的速度是最快的. 但是程序的可讀性最差.注意, 在方法一和方法二中, 都使用的 setredraw() 函數以減少屏幕的重繪, 否則, 執行速度將嚴重下降.
6. 數據窗口中數據的訪問  
在程序中, 經常會需要動態的修改數據窗口中的數據. 對此, PB 提供了多種方法, 各種方法在性能上會有一些差異.
A. 數據窗口中數據的訪問
    目的: 將數據窗口中的電話號碼存放在一個數組中.請比較下面兩中方法.
    方法一:
    string da_dh[]
    long ll,i  
    ll = dw_1.rowcount()
    for i = ll to 1 stet -1
    da_dh[i] = dw_1.getitemstring(i,"dhhm")
    next
    方法二:  
    string da_dh[]
    da_dh[] = dw_1.object.dhhm.current
測試發現, 第二種方法比第一種方法快將近一倍. 數據量越大這種差異越明顯.
B. 數據窗口中數據的修改
    目的: 修改數據窗口中的電話號碼列的值.
    請比較下面兩中方法.
    方法一:
    dw_1.setitem(i,"dhhm",l_name)
    方法二:  
    dw_1.object.name[i] = l_name
測試發現, 第二種方法比第一種方法快將近一倍. 數據量越大這種差異越明顯.
7. 數據窗口事件對性能的影響
對於數據窗口控制中經常發生的事件, 應當儘量減少其中的程序代碼.
特別是如下事件:
    A. itemchanged
    b. editchanged
    c. itemfocuschanged
    d. pbm_dwnkey
    e. rowfocuschanged
    f. retrieverow
在這些事件中的任何處理程序, 都會降低應用程序的處理速度. 所以應當儘量減少這些事件中的處理程序, 必要時, 可以考慮只將重要的代碼放在這些事件中, 而將剩餘的代碼放在一個傳遞的事件中.
例如,如果需要用到數據窗口的 ROWFOCUSCHANGED 事件,可以爲數據窗口定義一用戶事件 "UE_RE",而在數據窗口的 ROWFOCUSCHANGED 事件中寫如下代碼:
   PARENT.postevent("ue_re")
    在 UE_RE 事件中再編寫相應的程序代碼,如:
    string code  
    dw_1.selectrow(0,false)
    dw_1.selectrow(rownum,true)
    code = getitemstring(dw_1,rownum,"dhhm")
    dw_2.retrieve(code)
另外, 爲了獲得當前行號,應儘量使用數據窗口的 CURRENTROW 變量,而少用 GETROW() 函數。
8、 數據窗口的列名稱與列編號
對數據窗口的某列進行訪問, 可以採用該列的名稱, 也可以使用該列的編號,
例如:
採用列編號:
    dw_1.object.data[ll_row,2]
dw_1.getitemstring(3,2)
採用列名稱表示某列:
    dw_1.object.article_text[ll_row]
    dw_1.getitemstring(3,"dhhm")
    dw_1.setitem(3,"date",1999-03-31)
    ...
對於以上兩種方法, 如果只進行一次查詢, 在速度上並沒有太大的區別,如過需要循環訪問數據窗口上的某一列, 則速度上的差異就表現的比較明顯,才用第一種方法要快, 但是程序的可讀性比較差。但是, 在使用函數時(如 GETITEM() 和 setitem() ), 執行速度往往沒有很大差別。
9、 計算域
數據窗口的計算域會對數據的操作性能產生影響。 如果數據窗口中包含許多複雜的計算域,將嚴重影響數據窗口的執行速度。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章