簡介
性能總是Microsoft? FoxPro?數(shù)據(jù)庫管理系統(tǒng)的主要特點(diǎn)之一,尤其是在數(shù)據(jù)庫引擎方面。在介紹了Microsoft Visual FoxPro?有關(guān)的數(shù)據(jù)庫開發(fā)系統(tǒng)以及它相關(guān)的對象模型,加強(qiáng)引擎,還有客戶端/服務(wù)器特性之后,你就會(huì)發(fā)現(xiàn)整個(gè)產(chǎn)品的功能變得越來越強(qiáng)大了。然而強(qiáng)大的另一面就是產(chǎn)品也變得越來越復(fù)雜了。因此,當(dāng)開發(fā)越來越強(qiáng)勁時(shí),面向?qū)ο蟮膽?yīng)用程序使用遠(yuǎn)程數(shù)據(jù)存儲(chǔ)器中的數(shù)據(jù)變得越來越容易了,同時(shí),應(yīng)用程序也變得更加慢了。
本文要為你介紹一些實(shí)踐技巧和技術(shù),幫助你改善你的應(yīng)用程序的性能。只要你在應(yīng)用程序中正確的使用了這些技術(shù),你就能夠明顯改善你的用戶界面和數(shù)據(jù)訪問性能。
盡管應(yīng)用起來只有部分技巧結(jié)果比較明顯,我們還是希望我們所介紹的能夠給你提供有用的信息,幫助你全面改善應(yīng)用程序的性能。同時(shí),我們希望你能夠把你的意見或經(jīng)驗(yàn)反饋給我們,這樣今后使用Visual FoxPro的開發(fā)者要提高應(yīng)用程序的性能時(shí)就能獲得更多的幫助,這正如你的前人所做的一樣。
硬件配置
在這一部分我們將介紹調(diào)整操作系統(tǒng)的技巧,經(jīng)過調(diào)整之后操作系統(tǒng)將會(huì)與Visual FoxPro一起工作得更加和諧。在某些情況下你需要改變硬盤緩存器的值以達(dá)到優(yōu)化性能,從而滿足應(yīng)用程序需要的目的。
Microsoft Windows 95 和Windows NT
在大多數(shù)情況下Microsoft Windows? 95和 Windows NT?操作系統(tǒng)與Visual FoxPro一起工作得非常好。唯一的例外就是在Windows NT Server上運(yùn)行Visual FoxPro。因?yàn)閃indows NT Server 需要的內(nèi)存比Visual FoxPro想象的還多,所以Visual FoxPro只能夠?yàn)樽约撼Х峙鋬?nèi)存使用。
為了解決這種情況,你可以用SYS(3050)系統(tǒng)函數(shù)來調(diào)整Visual FoxPro的內(nèi)存緩沖區(qū),把它近似調(diào)整為它的缺省值的三分之一(詳情見內(nèi)存管理)。例如,你可以把下面這一行代碼添加到啟動(dòng)編碼或者是CONFIG.FPW文件中去:
=SYS (3050, VAL (SYS (3050, 1, 0)) / 3)
本地?cái)?shù)據(jù)性能
Rushmore 和索引
在MSDN Library的Visual FoxPro Developer‘s Guide中,第十五章“優(yōu)化應(yīng)用程序”一文詳細(xì)地討論了Rushmore?技術(shù)。下面再補(bǔ)充說明一些技巧。
使用索引
使用索引能夠提高數(shù)據(jù)查詢的性能,這是很明顯的。在Visual FoxPro中,索引是樹形的數(shù)據(jù)結(jié)構(gòu),所以在索引中查詢非???。
Visual FoxPro最獨(dú)特的方面之一就是支持在索引中使用公式。大多數(shù)數(shù)據(jù)庫管理系統(tǒng)(DBMSs)允許使用一個(gè)或更多個(gè)字段進(jìn)行查詢,而Visual FoxPro允許在索引中使用任何公式,甚至是用戶自定義的函數(shù)。
不過你要注意的是,當(dāng)你往表格中添加了很多的索引時(shí),更新和插入速度會(huì)比較慢,因?yàn)榇藭r(shí)Visual FoxPro要更新每一個(gè)索引。通常,你只需要為那些在過濾器和匯合指令中使用的數(shù)據(jù)建立索引。
你要避免使用FOR <條件 > 或者NOT <條件 >形式的索引公式,因?yàn)檫@些公式對于索引來說是不可優(yōu)化的。例如:
INDEX ON DELETED() TAG DEL
是Rushmore可優(yōu)化的,但是
INDEX ON NOT DELETED()TAG NOTDEL
不是。作為一個(gè)查詢的特殊情況,當(dāng)你不想刪掉記錄時(shí),如果你已經(jīng)SET DELETED ON,那么使用第一個(gè)例子中的索引公式會(huì)幫助你加快操作速度。
如果你在公式中使用了一個(gè)FOR的句子,Visual FoxPro不會(huì)使用索引來達(dá)到最優(yōu)化。例如
INDEX ON ORDNUM FOR DISCOUNT > 10 TAG ORDDISC
最后,注意不要使用那些只能用離散值的索引(如邏輯字段等)。
何時(shí)進(jìn)行查詢Rushmore優(yōu)化?
什么時(shí)候?qū)isual FoxPro表格進(jìn)行的查詢或者過濾要用Rushmore優(yōu)化,以及什么時(shí)候只是需要部分地進(jìn)行優(yōu)化,這些問題都很重要。
Visual FoxPro用Rushmore技術(shù)來優(yōu)化過濾條件,它會(huì)找尋符合過濾器左邊表達(dá)式的索引關(guān)鍵表達(dá)式。通常,開發(fā)者們會(huì)錯(cuò)誤地試圖把一個(gè)索引的標(biāo)記名與一個(gè)過濾器的表達(dá)式的相匹配,這樣只會(huì)發(fā)現(xiàn)Visual FoxPro不能夠用這種方法進(jìn)行優(yōu)化。以下是一個(gè)錯(cuò)誤地使用了索引的例子:
USE CUSTOMER
INDEX ON UPPER(contact) TAG name
SELECT * FROM customer ;
WHERE contact="BILL" && not optimized. No index on "contact"
以下是在過濾查詢中正確利用索引的方法:
SELECT * FROM customer WHERE UPPER(contact)="BILL" && Optimized!
你還可以用SYS(3054)函數(shù)來決定Rushmore查詢優(yōu)化的等級。SYS(3054)可以顯示也可以不顯示查詢的Rushmore優(yōu)化等級。SYS(3054,1)表示顯示Rushmore 優(yōu)化的等級,而SYS(3054,0) 則表示不顯示Rushmore優(yōu)化的等級。
數(shù)據(jù)庫引擎性能特性
主索引和侯選的索引
在FoxPro 2.x中,用唯一的ID號來增加記錄的典型技術(shù)就是在你的關(guān)鍵字段中建立一個(gè)索引,要增加記錄時(shí)就運(yùn)行一個(gè)搜索程序來查看這個(gè)ID號是否已經(jīng)被使用了。雖然這個(gè)方法已經(jīng)比較快了,但是它還不是最理想的。
用Primary 和Candidate關(guān)鍵字插入(或者更新)Visual FoxPro表格中,這種方法是由數(shù)據(jù)庫引擎驗(yàn)證的唯一方法,它用于比較低的級別中,速度非???。
用可區(qū)別的標(biāo)記來提高性能
在Visual FoxPro中,我們改進(jìn)了查找具有通用特征(指那些有可區(qū)別標(biāo)記的特征)的數(shù)據(jù)的方法。如果你的索引表達(dá)式的結(jié)果不支持可區(qū)別的標(biāo)記(如B或M),查找會(huì)快一些。
Non-Machine比較順序
non-Machine比較順序(如General)比Machine比較順序慢的原因有兩點(diǎn):
Non-Machine的索引關(guān)鍵字比Machine的索引關(guān)鍵字長一倍,因?yàn)樗鼈円蓞^(qū)別的信息。
Non-Machine比較用了許多特別的規(guī)則來為特征值建立索引,這樣它才能返回正確的結(jié)果。
因?yàn)镸achine比較順序相對來說要快一些,所以它通常用于連接和查找,而其他的比較順序通常用來為記錄排序。
注意:Visual FoxPro利用的索引僅僅是用SET COLLATE當(dāng)前的設(shè)置來建立的。因此,典型的解決方法是在主要的搜索和分類字段中有兩個(gè)索引:
SET COLLATE TO "GENERAL"
INDEX ON lastname TAG lastname && sort index
SET COLLATE TO "MACHINE"
INDEX ON lastname TAG _lastname && seek/select index
如果你想要用相對來說比較快的Machine索引來對字段進(jìn)行查找,選擇或者連接,請?jiān)谶M(jìn)行之前使用SET COLLATE TO "MACHINE"。Rushmore會(huì)使用在Machine比較順序中建立的索引,查找和選擇/連接都非???。
用SQL SELECT
如果在Machine比較順序中完成了一個(gè)SQL SELECT,那么任何ORDER BY 或者GROUP BY都可以用Machine比較順序。如果你要用non-Machine比較順序?yàn)榻Y(jié)果記錄排序,你可以在這個(gè)比較順序中完成SELECT,當(dāng)然這樣做比較慢,也可以分兩步把它完成:
第一步:用"Machine"比較順序來選擇記錄。
SET COLLATE TO "MACHINE" && use only ‘machine‘ indexes
SELECT * FROM table INTO CURSOR temp1 WHERE lastname = "Müller"
第二步:用"General"比較順序?yàn)橛涗浥判颉?/p>
SET COLLATE TO "GENERAL" && use only ‘general‘ indexes
SELECT * FROM temp1 INTO TABLE output ORDER BY lastname
新數(shù)據(jù)類型
我們在Visual FoxPro中引進(jìn)了幾個(gè)新的數(shù)據(jù)類型:DateTime, Integer, Double, 和Currency。所有這些類型的數(shù)據(jù)在硬盤上將存為二進(jìn)制數(shù)據(jù)(其中Integer是一個(gè)四個(gè)字節(jié)的二進(jìn)制值,其他的是八個(gè)字節(jié)的二進(jìn)制值)。
這樣做有兩個(gè)優(yōu)點(diǎn)。第一個(gè)優(yōu)點(diǎn)是,因?yàn)榇鎯?chǔ)在硬盤上的數(shù)據(jù)量比較?。ㄒ粋€(gè)八位的數(shù)作為Numeric來存儲(chǔ)要占八個(gè)字節(jié),而作為Integer來存儲(chǔ)則只要四個(gè)字節(jié)),所以從硬盤往內(nèi)存里調(diào)數(shù)據(jù)和索引時(shí),一次能夠調(diào)更多的數(shù)據(jù)到內(nèi)存頁中去,這樣速度就會(huì)比較快一些。第二個(gè)優(yōu)點(diǎn)是,無須進(jìn)行數(shù)據(jù)的傳送。在內(nèi)部,如果Visual FoxPro把整數(shù)看作四個(gè)字節(jié)的二進(jìn)制值,那么需要把它轉(zhuǎn)換為數(shù)字才能以ASCII碼數(shù)據(jù)的形式存儲(chǔ)在硬盤中。因而,每一次讀取數(shù)據(jù)都必須先從ASCII碼轉(zhuǎn)換成為二進(jìn)制,然后再轉(zhuǎn)換回去。有了Integers, Doubles, DateTimes, 和Currency幾種數(shù)據(jù)類型,這種轉(zhuǎn)換就不必要了,對數(shù)據(jù)的訪問自然就快了很多。
在新的數(shù)據(jù)類型中,Integer對速度的影響最大。因此,只要有可能,盡量地把主要的和外來的關(guān)鍵值設(shè)置為Integer。好處就是:.DBF文件更小了,索引更小了,而連接就更快了(這也許才是最重要的)。
緩沖和事務(wù)處理
事務(wù)處理必須簡短??纯聪旅孢@個(gè)例子:
BEGIN TRANSACTION
DO FORM BIGFORM TO nResult
IF nResult = IDCOMMIT
END TRANSACTION
ELSE
ROLLBACK
ENDIF
上面這個(gè)例子說明了什么?如果你認(rèn)為這是一個(gè)操作的恰當(dāng)?shù)氖聞?wù)處理的封裝,的確如此。但是,事務(wù)處理僅僅用于限制數(shù)據(jù)的更新。如果你做的不僅僅是這些(如涉及用戶界面甚至編程的概念像CASE, WHILE,或 IF語句等),那么在實(shí)際的數(shù)據(jù)更新過程中效率就會(huì)變得很低。
很明顯,這是因?yàn)樵赩isual FoxPro中如果要使用事務(wù)處理就要求鎖住記錄。在事務(wù)處理和記錄更新的過程中,記錄被鎖住,一直要等到事務(wù)處理被委托或者是反轉(zhuǎn)才能夠解開。即使你在UNLOCK之后說明了要RLOCK()或者FLOCK(),記錄也一直被鎖死著,直到END TRANSACTION或者 ROLLBACK出現(xiàn)才會(huì)改變。進(jìn)一步說,把記錄懸掛起來也要求Visual FoxPro要把頭文件鎖住,直到事務(wù)處理完成之后才能夠解鎖。(被委托或者是反轉(zhuǎn))。因此,在一個(gè)大的,多用戶的系統(tǒng)中,減少記錄被鎖死的持續(xù)時(shí)間是很關(guān)鍵的。所以在上面這個(gè)例子中一個(gè)更加適當(dāng)?shù)膽?yīng)用程序是:
* Save method from the cmdSave command button.
BEGIN TRANSACTION
UPDATE PRODUCTS SET reorder_amt = 0 ;
WHERE discontinued = .T.
END TRANSACTION
使用SYS(3051)
這個(gè)SYS函數(shù)控制時(shí)間的數(shù)量,精度達(dá)到毫秒級,經(jīng)過一次不成功的鎖定嘗試之后,Visual FoxPro在再次試圖鎖定一個(gè)記錄,表格,索引,或備忘錄之前會(huì)一直等待。缺省值是333毫秒。
如果在你的數(shù)據(jù)庫文件中有許多鎖定競爭,你只要把這個(gè)值調(diào)大(最大是1000),就能夠改善你的應(yīng)用程序的性能。這樣你的應(yīng)用程序就不會(huì)把時(shí)間浪費(fèi)在快速通過網(wǎng)絡(luò)進(jìn)行鎖定上了。
不過,如果不希望鎖定競爭太高,你就要把這個(gè)值調(diào)低(最小值是100),這樣鎖定操作就會(huì)更快一些。
遠(yuǎn)程數(shù)據(jù)性能
從任何后端數(shù)據(jù)庫中檢索獲得數(shù)據(jù)都是很耗費(fèi)時(shí)間的。為了從一個(gè)服務(wù)器數(shù)據(jù)庫中檢索數(shù)據(jù),以下幾個(gè)步驟是必須的:
客戶端向后端服務(wù)器數(shù)據(jù)庫提出查詢。
服務(wù)器分析并且編輯查詢條件。
生成結(jié)果集。
服務(wù)器通報(bào)客戶端結(jié)果已經(jīng)完成。
客戶端通過網(wǎng)絡(luò)從服務(wù)器上獲得數(shù)據(jù)。
為了加快數(shù)據(jù)的檢索(或更新)過程要用到許多技術(shù)。
只檢索你需要的數(shù)據(jù)
一般來說,應(yīng)用程序中的一個(gè)函數(shù)單元(在大部分情況下是一個(gè)表單或者報(bào)表)要訪問表格中所有的數(shù)據(jù)只是特殊情況。建立一個(gè)遠(yuǎn)程視圖,僅僅獲?。ɑ蛘吒拢┠闼枰淖侄魏托校@樣通過線路所傳送大量數(shù)據(jù)的情況就會(huì)大大改善。例如,如果你已經(jīng)建立了一個(gè)遠(yuǎn)程視圖,它與一個(gè)表單的四個(gè)控件綁定在一起(如客戶表格中的客戶標(biāo)識號,公司名,聯(lián)系方式,地址等),用這樣一個(gè)SELECT語句來執(zhí)行視圖,如:
SELECT * FROM customers
效率會(huì)很低,因?yàn)槟銠z索了大量不做要求的字段。如果用下面這個(gè)SELECT語句來執(zhí)行視圖,效果會(huì)好一些。
SELECT customer_id, company, contact, address FROM customers
使用WHERE子句
為了限制檢索(或者更新)的數(shù)據(jù)量,適當(dāng)?shù)厥褂肳HERE子句是有必要的。我們用同樣的例子來進(jìn)行說明,如果你想知道西北地區(qū)的客戶情況,使用下面這個(gè)SELECT語句:
SELECT customer_id, company, contact, address FROM customers
WHERE region = ‘NORTHWEST‘
WHERE子句中的參數(shù)
Visual FoxPro中靈活的視圖和SQL Pass Through技術(shù)能夠讓你在SQL SELECT, SQL UPDATE,以及 SQL DELETE語句的WHERE子句中使用運(yùn)行時(shí)參數(shù)。例如,你能夠從任何區(qū)中檢索信息。
SELECT customer_id, company, contact, address FROM customers
WHERE region = ?pcRegion
這里pcRegion是參數(shù)的名字。如果視圖只是用于檢索或者再查詢必需的信息,這個(gè)參數(shù)應(yīng)該有(但不是必須有)。
使用正確的更新準(zhǔn)則
View Designer中的Update Criteria標(biāo)簽使你能夠明確你想要如何把視圖中的數(shù)據(jù)進(jìn)行更新,插入和刪除。標(biāo)簽中標(biāo)有“SQL WHERE clause includes”的部分能夠讓你控制UPDATE 和DELETE操作中WHERE子句的內(nèi)容。這對于控制后端的數(shù)據(jù)沖突以及性能都十分重要??紤]到上面所提到的視圖并且假設(shè)關(guān)鍵字段是customer_id。假設(shè)你想要更新除了關(guān)鍵字段之外的所有字段(這種情況不會(huì)經(jīng)常出現(xiàn),因?yàn)楦淖冴P(guān)鍵字段就意味著做一次刪除再加上一次插入)—也就是“公司名”,“聯(lián)系方式”,以及“地址”?;谀阍?quot;SQL WHERE clause includes"選項(xiàng)組中的選擇,你的WHERE子句可以是表1中的任何形式。
Table 1. Possible WHERE Clauses for Updates and Deletions
表1。更新和刪除的WHERE子句的可能情況
設(shè)置
WHERE 子句
僅僅是關(guān)鍵字段
WHERE OLDVAL (customer_id) = CURVAL (customer_id)
關(guān)鍵字段和經(jīng)過修改的字段(缺省設(shè)置)
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL () = CURVAL () AND
OLDVAL () = CURVAL () AND
[and so on.]
關(guān)鍵和可更新的字段
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL (company) = CURVAL (company) AND
OLDVAL (contact) = CURVAL (contact) AND
OLDVAL (address) = CURVAL (address)
關(guān)鍵字和時(shí)間標(biāo)記
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL (timestamp) = CURVAL (timestamp)
從上表中我們能夠很清楚地看出,WHERE子句在很大程度上會(huì)影響性能,尤其是有許多字段要更新時(shí)(關(guān)鍵字段和經(jīng)過修改的字段),或者是有許多字段能夠被更新時(shí)(關(guān)鍵字段和可更新字段)。這是因?yàn)閂isual FoxPro要把它的"oldval"數(shù)據(jù)緩沖區(qū)中的值(這只是一個(gè)系統(tǒng)指針)與硬盤中的值(服務(wù)器數(shù)據(jù)庫)相比較,然后及時(shí)給出充分的報(bào)告說明何時(shí)會(huì)發(fā)生更新沖突(如當(dāng)你首先獲得某些數(shù)據(jù)時(shí),其他的用戶或者程序卻改變了這些數(shù)據(jù))。如果有很多更新操作要進(jìn)行,上述比較過程會(huì)耗費(fèi)很多時(shí)間。
因此,我們推薦你在大多數(shù)情況下使用Key and Modified字段。一般來說,你要更新的后端字段總是你要更新的所有字段的一個(gè)子集(同時(shí)也是視圖中所有字段的一個(gè)子集)。
對于支持時(shí)間標(biāo)記的服務(wù)器數(shù)據(jù)庫而言,我們推薦你使用Key and Timestamp設(shè)置,而不是上面提到的Key and Modified Fields設(shè)置。Timestamp是一個(gè)行版本,每次行有所改變時(shí),它就要更新一次(順便提一句,timestamp數(shù)據(jù)類型并不是指數(shù)據(jù)和時(shí)間,而是指一個(gè)數(shù)值。你用不著擔(dān)心不同機(jī)器上的系統(tǒng)時(shí)鐘會(huì)導(dǎo)致更新問題。)使用這個(gè)設(shè)置,在WHERE子句中只比較了其中一列,因此處理速度就快了很多。
注意:這些設(shè)置也可以在視圖的WhereType屬性中通過DBSETPROP()或者CURSORSETPROP()函數(shù)進(jìn)行。
使用BatchUpdateCount 屬性
一些服務(wù)器數(shù)據(jù)庫(如Microsoft SQL Server)允許一批批的語句作為一個(gè)單獨(dú)的數(shù)據(jù)包送往服務(wù)器。例如,如果你把BatchUpdateCount屬性設(shè)置為4,然后更新前10個(gè)記錄,Visual FoxPro送往服務(wù)器的SQL語句就是這樣的:
UPDATE customer SET contact = ‘John Jones‘ WHERE customer_id = 1;
UPDATE customer SET contact = ‘Sally Park‘ WHERE customer_id = 2;
UPDATE customer SET company = ‘John Jones‘ WHERE customer_id = 3;
UPDATE customer SET contact = ‘John Jones‘ WHERE customer_id = 4
這一技術(shù)加快了更新,刪除,和插入的速度,因?yàn)橥ㄟ^線路傳送的網(wǎng)絡(luò)數(shù)據(jù)包的絕對數(shù)量減少了,并且語句被服務(wù)器數(shù)據(jù)庫壓縮為一批而不是零散的。
你應(yīng)該試一試用這一屬性的不同的值和PacketSize屬性來優(yōu)化你的更新過程。
這個(gè)屬性也能夠在Query菜單的Advanced Options對話框中進(jìn)行設(shè)置,或者是通過編碼用DBSETPROP()或CURSORSETPROP()來設(shè)置。要注意的是,要利用這種方法的優(yōu)點(diǎn)則需要用DBSETPROP()或者SQLSETPROP()把Transactions屬性中的手動(dòng)事務(wù)處理設(shè)為允許。
使用PacketSize 屬性
PacketSize屬性控制送往數(shù)據(jù)庫服務(wù)器的和從數(shù)據(jù)庫服務(wù)器中檢索獲得的網(wǎng)絡(luò)數(shù)據(jù)包的大?。ㄒ宰止?jié)計(jì)算)。它實(shí)際上是由開放式數(shù)據(jù)庫互接(ODBC)來設(shè)置的,并且是一個(gè)非負(fù)值。不同的網(wǎng)絡(luò)供應(yīng)商對于這個(gè)屬性的處理各不相同,所以你需要查看你的網(wǎng)絡(luò)服務(wù)文檔。例如,Novell NetWare把數(shù)據(jù)包大小的最大值設(shè)置為512字節(jié),因此在PacketSize屬性中,即使把它的值設(shè)置得大于512也沒有任何其他作用。
PacketSize屬性的缺省設(shè)置是4096字節(jié)。如果你的網(wǎng)絡(luò)支持更大的數(shù)據(jù)包大小,你可以把這個(gè)屬性的值設(shè)置為最大數(shù)據(jù)吞吐量來滿足SELECT, INSERT, UPDATE, 和DELETE的要求。
使用FetchMemo 屬性
當(dāng)從后端獲取行后,F(xiàn)etchMemo屬性能夠控制是否要獲取備忘錄和二進(jìn)制(普通字段)數(shù)據(jù)。如果把這個(gè)字段設(shè)置為.F.,一直到該字段被訪問后二進(jìn)制數(shù)據(jù)才會(huì)被傳送到你的機(jī)器中。因?yàn)楂@取被延遲到了需要數(shù)據(jù)的時(shí)候,所以查詢的獲取速度會(huì)得到提高。
把查找表格本地化
在許多例子中,只讀數(shù)據(jù)常常被你的應(yīng)用程序訪問。比如說,一個(gè)狀態(tài)表或者一個(gè)雇員名稱表就可能在你的應(yīng)用程序中被廣泛使用。如果把這些數(shù)據(jù)本地化(也就是說,不要把每一個(gè)查詢都上載到服務(wù)器中),查找就會(huì)變得非??臁δ切牟蛔兓蛘吆苌僮兓臄?shù)據(jù)使用這一技術(shù)用處最大。
使用本地規(guī)則
很少有人知道,Visual FoxPro支持本地字段和記錄級別的規(guī)則還有遠(yuǎn)程視圖。這些規(guī)則能夠防止那些與其他數(shù)據(jù)不同的數(shù)據(jù)或者不符合企業(yè)規(guī)則的數(shù)據(jù)進(jìn)入數(shù)據(jù)庫中。把這個(gè)規(guī)則放到視圖中而不是后端表格中的好處是,你可以在無效的數(shù)據(jù)進(jìn)入線路之前就把它剔除出去。而不利之處就是這個(gè)規(guī)則不會(huì)自動(dòng)地與后端服務(wù)器表格中的規(guī)則相匹配。因此,如果你的后端表格規(guī)則的定義有所改變,在多數(shù)情況下,你需要在本地把視圖規(guī)則的定義做相應(yīng)地改變。當(dāng)然,如果規(guī)則很簡單,這就不是一個(gè)大的負(fù)擔(dān)。還有,規(guī)則的定義通常改變得不是很頻繁,所以你不要希望能夠經(jīng)常地更新本地規(guī)則。
為了使本地規(guī)則能夠使用,你要用到這個(gè)函數(shù)DBSETPROP()。下面是一個(gè)例子:
* Make sure the order number field is > 0
=DBSETPROP("orders.ordnum")
表單/對象性能
這一部分將介紹在你的應(yīng)用程序中提高對象性能的技巧,特別是有關(guān)表單的性能。
使用數(shù)據(jù)環(huán)境
使用Form Designer 或者Report Designer中的數(shù)據(jù)環(huán)境,表格打開運(yùn)行速度要比在表單Load事件中執(zhí)行USE, SET ORDER, 和命令SET RELATION快得多。因?yàn)閂isual FoxPro使用的是低等級的引擎調(diào)用來打開表格,設(shè)置索引和關(guān)系。
限制表單集中的表單數(shù)
表單集僅僅在有許多個(gè)表單要共享一個(gè)私有的數(shù)據(jù)會(huì)話時(shí)才需要用到。當(dāng)你運(yùn)行一個(gè)Formset時(shí),實(shí)際上你就是告訴Visual FoxPro 要在Formset 中建立所有的表單實(shí)例以及建立所有這些表單的控件,即使在Formset中只顯示了第一個(gè)表單。如果這些表單用不著共享私有數(shù)據(jù),那么這么做將是非常耗時(shí)間的,也完全沒有必要。相反地,你應(yīng)該為其他表單在需要的時(shí)候執(zhí)行DO FORM。
從另外一個(gè)方面來說,如果你真的用了Formsets ,在訪問Formsets中的表格時(shí),你會(huì)發(fā)現(xiàn)性能有所提高,因?yàn)樗鼈円呀?jīng)加載了,只不過看不見。
在Pageframe中動(dòng)態(tài)加載Page Controls
Pageframes,像Formsets一樣,在加載Pageframes時(shí)就把每一頁的所有控件都加載了。你可以用這個(gè)技術(shù)在Pageframes的每一頁中建立一個(gè)超出控件之外的類別。
你的目標(biāo)是延遲看不見的頁面上的控件的實(shí)例化,直到用戶訪問這些頁面為止。以下是步驟:
如以往一樣設(shè)計(jì)你的表單,其中包括所有頁面中的控件。
設(shè)計(jì)完成之后翻到第二頁,把你覺得要合并為一類的控件省下來。
打開你建立的類,確保這些控件能夠完全打開。
在以下的頁中重復(fù)第二步和第三步。
在第二頁及以下頁的激活方法中,加入下列代碼:
IF This.ControlCount=0
This.Addobject("cnrpage1","cnrpage1")
This.cnrpage1.visible=.T.
ENDIF
用這種技術(shù)你能夠加快表單的加載速度,因?yàn)?Pageframe第二頁之后的頁面中的控件如果不需要就不會(huì)被加載了。
把控件和數(shù)據(jù)進(jìn)行動(dòng)態(tài)綁定
對于那些有許多要綁定數(shù)據(jù)的控件的表單,如果你能夠把這些綁定推遲到需要的時(shí)候再進(jìn)行,你就會(huì)發(fā)現(xiàn)它的性能有了很大的改善。
你能夠把表單的表格和視圖放到Data Environmen中,這樣一來,當(dāng)表單加載時(shí),它們也就被打開了。然后,如果選中一個(gè)控件—例如一個(gè)組合框—,你就可以把控件與數(shù)據(jù)值綁定在一起。在下面這個(gè)例子中,聯(lián)合方框已經(jīng)與GotFocus事件中的"customer.company"字段綁定在一起。
*檢查一下是否控件已經(jīng)被賦值。
IF this.RecordSource = ""
*把記錄資源設(shè)置為正確的值
*把記錄資源類型設(shè)置為“字段”
this.RecordSource = "customer.company"
this.RecordSourceType = 6
* Refresh the control.
this.Refresh
ENDIF
對要綁定數(shù)據(jù)的控件使用這個(gè)技術(shù),可以大大改進(jìn)表單的加載時(shí)間。
使用LockScreen
這一特性讓你能夠延遲表單中控件的任何變化所引起的屏幕刷新的速度。例如,如果你要讓控件看得見或者看不見,要改變控件的顏色,或者是要移走綁定控件中的記錄,等到這些全部完成之后再顯示這些控件,效率會(huì)比較高。
thisform.LockScreen = .T.
thisform.MyButton.Caption = "Save"
thisform.MyGrid.BackColor = RGB (255, 0, 0) && Red
SKIP IN customers
SKIP IN orders
thisform.Refresh
thisform.LockScreen = .F.
在這個(gè)例子中,我們設(shè)置了一個(gè)標(biāo)題,改變了背景顏色,并且把所有與客戶和順序綁定的控件中的記錄都移走了。如果沒有LockScreen特性,你將會(huì)看到以上的每一個(gè)操作都會(huì)引起相應(yīng)控件的外觀變化,所有的更新過程都會(huì)變得緩慢不已而不是利索爽潔。
如果需要對一個(gè)表單的顯示多次進(jìn)行改變時(shí),通常你會(huì)用到這一技術(shù)。我們不是讓你在每一次顯示改變時(shí)都使用LockScreen,而只是像上面這個(gè)例子中所描述的一樣,在有一組改變時(shí)使用它。
利用WITH … ENDWITH 或者使用Object References
當(dāng)你要用“對象特性”語法說明了一個(gè)對象的特性時(shí),Visual FoxPro必須首先在能夠訪問該特性之前首先找到這個(gè)對象。例如,下列編碼是要Visual FoxPro通過查找四個(gè)對象(thisform, pgfCustInfo, pagCustName, 和cboName),找出三個(gè)特性中要設(shè)置哪個(gè)特性:
thisform.pgfCustInfo.pagCustName.txtName.Value = "Fred Smith"
thisform.pgfCustInfo.pagCustName.lblName.Caption = "Name"
thisform.pgfCustInfo.pagCustName.grdOrders.BackColor = RGB (0,0,0)
lcSeek = ALLTRIM(thisform.pgfCustInfo.pagCustName.txtName.Value)
如果你接下來要設(shè)置一個(gè)對象的兩個(gè)或者更多個(gè)特性,則使用WITH…ENDWITH結(jié)構(gòu)。這么做,Visual FoxPro就只需確定其范圍并且找到該對象一次。在上面這個(gè)特性設(shè)置的例子中,用到WITH…ENDWITH就會(huì)快得多:
WITH thisform.pgfCustInfo.pagCustName
.txtName.Value = "Fred Smith"
.lblName.Caption = "Name"
.grdOrders.BackColor = RGB (0,0,0)
lcSeek = ALLTRIM(.txtName.Value)
ENDWITH
注意:就像在ALLTRIM函數(shù)中所說明的一樣,你可以在WITH … ENDWITH結(jié)構(gòu)的任何地方使用<.objectname>語法。
能夠做到這一點(diǎn)的另外一個(gè)方法就是利用對象引用。對象引用不過是一個(gè)控制對象引用的變量。還是以上面的例子為例說明,你也可以這么做:
oPageFrame = thisform.pgfCustInfo.pagCustName
oPageFrame.txtName.Value = "Fred Smith"
oPageFrame.lblName.Caption = "Name"
oPageFrame.grdOrders.BackColor = RGB(0,0,0)
在上面這個(gè)例子你不一定會(huì)見到有很明顯的性能改善,但是如果一個(gè)對象(在這個(gè)例子中是pagCustName PageFrame對象)在你的應(yīng)用程序中或者在一個(gè)循環(huán)中被多次引用,性能一定會(huì)有很大地提高。
使用多分配的變量
訪問全部的要比訪問一個(gè)變量慢。這一點(diǎn)在一個(gè)程序的循環(huán)中尤其明顯。典型的面向?qū)ο蟮某绦虻淖龇ㄊ侨绻粋€(gè)對象的特性被反復(fù)用到,就把它的內(nèi)容復(fù)制到一個(gè)變量中。例如,下面這些代碼就填充了一個(gè)特性數(shù)組:
FOR x = 1 to 256
thisform.aCharArray[x] = SUBSTR(thisform.cCharString, x, 1)
ENDFOR
而在下面這些代碼中,盡管要執(zhí)行的代碼顯得比較多,但是它們的確要快一些,因?yàn)檫@個(gè)代碼在循環(huán)外就已經(jīng)被賦值了:
*把字符串復(fù)制給一個(gè)本地變量。
lcChar = thisform.cCharString
*建立一個(gè)本地?cái)?shù)組。
LOCAL laCharArray[256]
FOR nCounter = 1 to 256
laCharArray[x] = SUBSTR(laChar, x, 1)
ENDFOR
*把本地?cái)?shù)組復(fù)制到特性數(shù)組中。
=ACOPY(laCharArray, thisform.aCharArray)
在Init, Refresh,和 Paint Methods中盡量避免大量的Xbase代碼
這些事件經(jīng)常出現(xiàn),你一定要消除(或減少)在這些方法中用到代碼。尤其是對于Refresh和 Paint方法而言,它們使用得最頻繁。
把Init代碼轉(zhuǎn)移到用得不那么頻繁的方法中,如Activate, Click,和 GotFocus等。然后你就可以在一個(gè)控件中使用一個(gè)特性,而且只需要運(yùn)行一次來跟蹤是否該控件已經(jīng)運(yùn)行了代碼。
在視圖中使用NoDataOnLoad特性
在一個(gè)視圖的Data Environment Cursor 對象中的NoDataOnLoad特性與視圖的USE命令中的NODATA子句作用是一樣的。它使得視圖被打開,但是視圖并沒有真正獲得任何數(shù)據(jù)。無論是本地視圖還是遠(yuǎn)程視圖都是這樣的。
一般來說,你在Data Environment中已經(jīng)設(shè)置了參數(shù)的視圖上可以使用這一技術(shù)(詳細(xì)情況請見“為你的WHERE子句設(shè)置參數(shù)”)。例如,你有一個(gè)客戶信息表單,它使用了一個(gè)設(shè)置了參數(shù)customer_id的視圖。你可以鍵入一個(gè)無效的customer_id,然后按下查找鍵。查找按鈕的代碼與下面的這些代碼很相似(假設(shè)該視圖的參數(shù)叫做vpCustID,這里vp是用于表示一個(gè)作為視圖參數(shù)的變量的匈牙利語符號):
*把視圖參數(shù)設(shè)置為客戶標(biāo)識符文本框中的值。
vpCustID = thisform.txtCustomerID
*鎖住屏幕延遲畫面改變。
thisform.LockScreen = .T.
*發(fā)出視圖中的再查詢。
=REQUERY(‘customerview‘)
*刷新數(shù)據(jù)綁定控件
thisform.Refresh
*為屏幕開鎖。
thisform.LockScreen = .F.
這些代碼是一個(gè)簡單的例子,但是它是處理本地的和遠(yuǎn)程的已經(jīng)設(shè)置了參數(shù)的視圖的典型方法。
這么做的好處是表單載入時(shí)間大大減少,因?yàn)橐晥D不會(huì)使任何數(shù)據(jù)回送給你,給客戶。那些與視圖的字段綁定了的控件仍然綁定著,因?yàn)檫@里是一個(gè)公開工作區(qū)(沒有數(shù)據(jù)在里面)。
OLE 性能
在你訪問OLE數(shù)據(jù)之前你要打開OLE服務(wù)器。與普通字段綁定的控件在這一類數(shù)據(jù)(如Microsoft Word 或者M(jìn)icrosoft Excel)的服務(wù)器已經(jīng)在客戶機(jī)上運(yùn)行時(shí)要表現(xiàn)得比較好。
Automation性能
在某些例子中,Automation服務(wù)器(如Microsoft Excel)總是開始一個(gè)自己的新實(shí)例,即使當(dāng)前已經(jīng)有一個(gè)實(shí)例在運(yùn)行了也是如此。為了改變這種情況(同時(shí)提高性能),要使用GetObject函數(shù)而不是CreateObject函數(shù)。例如下面這個(gè)調(diào)用:
x = GetObject(, "excel.Application")
就會(huì)使用現(xiàn)有的實(shí)例,而:
x = CreateObject("excel.Application")
就會(huì)建立一個(gè)新的實(shí)例。
當(dāng)你使用GetObject時(shí),如果服務(wù)器沒有運(yùn)行,你就會(huì)收到Visual FoxPro發(fā)出的一個(gè)錯(cuò)誤信息,此時(shí)你要捕獲這個(gè)錯(cuò)誤并且調(diào)用CreateObject()函數(shù)。
不要反復(fù)用子對象來評價(jià)長表達(dá)式。執(zhí)行使用OLE服務(wù)器中對象的表達(dá)式會(huì)花費(fèi)很多時(shí)間,特別在是要進(jìn)行多次評價(jià)時(shí)。如果把子對象緩存到變量中用于引用就會(huì)快很多(詳細(xì)情況請見為多次賦值使用變量)。
把對象作為圖標(biāo)插入
當(dāng)你要把一個(gè)OLE對象插入到一個(gè)字段中時(shí),你可以把它作為一個(gè)圖標(biāo)或者是一個(gè)占位符而不是作為一個(gè)整的對象來插入字段中。這么做能夠減少需要的存儲(chǔ)空間,因?yàn)閂isual FoxPro存儲(chǔ)的是對象的簡報(bào)圖象。同時(shí)在拖拽對象時(shí)性能也會(huì)有所提高,因?yàn)榇藭r(shí)只有圖標(biāo)需要變換。
使用圖象控件
如果在一個(gè)應(yīng)用程序中需要用到一個(gè)位圖,如一個(gè)公司的徽標(biāo),Image Controls比 OLEBoundControls.要快得多。
可能時(shí)使用手工鏈接
手工與對象進(jìn)行鏈接比較地快,因?yàn)樵谧詣?dòng)鏈接時(shí)需要的通告時(shí)間在這里可以省略掉,同時(shí)也因?yàn)樵谕献ο髸r(shí)不需要開始OLE服務(wù)器。如果你不需要經(jīng)常更新對象,那么最好用手工鏈接。
內(nèi)存管理
使用SYS(3050)
這個(gè)SYS函數(shù)能夠讓你優(yōu)化用于數(shù)據(jù)緩存的前臺(tái)和后臺(tái)的內(nèi)存。前臺(tái)內(nèi)存是指當(dāng)Visual FoxPro是前臺(tái)應(yīng)用程序時(shí)所用的內(nèi)存。而后臺(tái)內(nèi)存是指當(dāng)Visual FoxPro是一個(gè)后臺(tái)應(yīng)用程序時(shí)所用的內(nèi)存。
為了優(yōu)化你的應(yīng)用程序,你可以嘗試不同的值,讓Visual FoxPro有不同的內(nèi)存來用于數(shù)據(jù)緩存。
不要使用MEMLIMIT
Visual FoxPro不認(rèn)識MEMLIMIT,在FoxPro 2.6中, MEMLIMIT是指用于說明FoxPro能夠分配使用的最大內(nèi)存的配置設(shè)置。不要用這個(gè)配置來限制Visual FoxPro能夠使用的最大內(nèi)存。如果需要的話就用SYS(3050)。
謹(jǐn)致謝意
盡管這篇文章是由我來執(zhí)筆寫成的,但我還是要感謝Visual FoxPro組的成員們,他們是Geoff Kizer, Tom Cooper, Lori Sargent, Rodney Hill, Dave Berliner, Matt Pohle以及其他Visual FoxPro性能組的成員。如果沒有他們的幫助我將很難完成這篇文章。
我還要感謝George Goley of MicroEndeavors, Inc.,和 David T. Anderson of Alden Anderson Consulting, Inc,他們提供了許多技巧并且還為我審查了這篇文章。再次表示感謝!
聯(lián)系客服