第十五章:优化应用程序

用户在使用 Visual FoxPro 设计和运行应用程序时,一般都希望操作系统、Visual FoxPro,以及应用程序能发挥最佳性能。

有关优化操作系统的详细内容,请参阅《安装指南》中的第四章“对系统进行优化”

本章内容要点:

优化表和索引

有效地使用索引和缓冲,可以加快对表中数据的访问。另外,也可以使用 Rushmore 技术来优化您的查询。

使用索引

使用索引可以加快对表中数据的访问。向表中添加一个索引可以加快搜索,特别是当您能运用 Rushmore 技术优化搜索时。索引也可以让您按照特定的顺序操作数据,例如按照姓氏的顺序查看一个客户表。

如果表中的记录有唯一关键字,可以根据这个字段创建主索引或候选索引。这些类型的索引可以令 Visual FoxPro 在低层次上验证关键字,得到最好的性能。

除了在用于搜索和排序的字段上创建索引,最好在涉及到关系的字段上也创建索引。如果您通过两个未建立索引的字段来联接两个表,联接操作会多用去数百倍的时间。

Visual FoxPro 的一个重要特性是,您可以根据任何表达式创建一个索引。(在某些数据库产品中,您只能在字段上创建索引)此功能特性使您可以使用字段组合或由字段导出的表达式构成的索引来优化搜索、排序或联接。例如,您可以使用包含 SOUNDEX( ) 函数的表达式对一个名称字段创建索引。这样,应用程序就可以使用户极为迅速地访问发音相近的名字。

向表中添加索引后,虽然节省了搜索时间,但更新的过程会变慢,这是因为在表中更新或插入数据时 Visual FoxPro 需要相应地更新每一个索引。因此,您必须权衡索引带来的好处与不足。

最后,要避免在只有几个离散值的字段上使用索引,例如在逻辑字段上。在这种情况下,索引中只包含了少数几个数据项,维护索引带来的麻烦与索引带来的好处相比得不尝失。

有关如何使用 Rushmore 技术进行高效索引的详细内容,请参阅本章稍后的“使用 Rushmore 技术加速数据访问”

优化联接

当您使用 SELECT - SQL 创建联接时,下列情况会降低性能并产生意想不到的结果:

为避免这些情况,应根据一个表的主关键字与其他表的外部关键字之间的关系创建联接。如果您创建联接所依据的数据不唯一,最后造成的结果将是两个表的乘积。例如,下述 SELECT - SQL 语句创建的一个联接,将产生一个包含大量数据的结果:

SELECT *;
 FROM  tastrade!customer INNER JOIN tastrade!orders ;
 ON  Customer.postal_code = Orders.postal_code

在本示例中,邮政编码只能唯一地识别一个城市内的某一个地区,但是如果您想匹配用户记录及他们的订货记录,邮政编码就没有什么价值了。邮政编码不可能唯一地识别一个用户或一个订货。相反,您可以用下列语句创建一个联接:

SELECT *;
 FROM  tastrade!customer INNER JOIN tastrade!orders ;
 ON  Customer.customer_id = Orders.customer_id

在本示例中,customer_id 字段唯一地识别一个指定用户及属于这个用户的订单,因此创建了一个包含用户记录和该用户的每一个订货记录的集合。

此外,联接带有空字段的表时要小心,因为 Visual FoxPro 也将匹配空字段。然而,Visual FoxPro 并不匹配包含 null 值的字段。当创建一个联接时,可通过一个空字符串来测试联接条件中的字段表达式。

例如,如果您认为 Orders 表中的 customer id 字段可能为空,请使用下列语句筛选出没有用户编号的订货字段:

SELECT *;
 FROM  tastrade!customer INNER JOIN tastrade!orders ;
 ON  Customer.customer_id = Orders.customer_id; 
 WHERE tastrade!orders <> ""

提示 您还可以使用 EMPTY( ) 函数测试一个空字符串,但是,在筛选表达式中调用函数比直接比较常数要慢一些。

使用项目管理器

当您使用“项目管理器”时,在 .app 或 .exe 文件中不限制其包含的程序和过程的数目。这样,从各个角度讲都加快了程序的执行过程。

首先,Visual FoxPro 打开一个程序文件,并且将保持打开状态。以后,当您发出一个 DO 命令执行该文件的一个程序时,Visual FoxPro 就不需要重新打开这个文件了。

其次,只有一个或两个文件的应用程序减少了工作目录中需要的文件数。这样所有文件的操作(如,打开、重命名或删除文件)速度均会得到提高,因为操作系统只需检查较少的目录项。

有关使用“项目管理器”创建应用程序的详细内容,请参阅第十三章“编译应用程序”

优化表和索引的一般提示

若要创建尽可能快的表和索引,请遵循下列建议:

使用 Rushmore 技术加速数据访问

为了帮助您优化您的应用程序的性能,Visual FoxPro 提供了 Rushmore 数据访问技术。使用 Rushmore 技术,对一些复杂的表操作比不使用这项技术要快成百上千倍。

掌握 Rushmore 技术

Rushmore 技术是一种数据访问技术,它使用标准的 Visual FoxPro 索引优化对数据的访问。您可以对任何 Visual FoxPro 索引使用 Rushmore 技术,包括 FoxPro 1.x (.idx) 索引、压缩 (.idx) 索引和复合 (.cdx) 索引。

.cdx 索引和压缩 .idx 索引都使用了压缩技术,压缩后索引的大小是旧格式未压缩索引的六分之一。Visual FoxPro 能够处理压缩索引更快,这是因为压缩索引在物理空间上更小,所以 Visual FoxPro 处理索引时访问磁盘较少,大部分的索引在内存中进行缓冲处理。尽管 Rushmore 技术象其他文件访问技术那样,其优势在于压缩的索引占用的物理空间较小,但它对于其他旧格式索引也能很好地处理。

当 Visual FoxPro 在内存较小的机器上处理很大的表时,Rushmore 可能会找不到足够的内存。在这种情况下,将出现一警告信息(“内存不足,不能优化”)。尽管程序能正确地运行并且不会丢失数据,但查询将不能从 Rushmore 优化技术中受益。

在最简单的形式下,Rushmore 根据已有索引使用 FOR 子句指定一个记录集来加速对单个表进行操作的命令的执行。Rushmore 也可以加速其他一些命令的操作,如 LOCATEINDEX。完整的可优化命令的清单,请参阅下一节“在表中使用 Rushmore”。

Visual FoxPro SQL 命令使用 Rushmore 作为多表查询优化的基本工具,使用已有索引甚至创建新的特别索引来提高查询的速度。

在表中使用 Rushmore

您可以根据所涉及表的数目,使用 Rushmore 技术优化对数据的访问。当您访问单个表时,您可以在出现 FOR 子句的任何地方利用 Rushmore 技术。当您访问多个表时,SELECT - SQL 查询取代了所有的 Rushmore 优化技术。在 SQL 命令中,Visual FoxPro 决定需要什么来优化一个查询,并为您做这件事。您不需要打开表或索引。如果 SQL 确定它需要索引,它会为自己创建一个临时索引。

若要使用 Rushmore

请选择下列选项之一:

下表列出了使用 FOR 子句的命令。Rushmore 技术可优化的速度与检索的记录数目成正比。

带有 FOR 子句的潜在可优化命令

AVERAGE BLANK
BROWSE CALCULATE
CHANGE COPY To
COPY TO ARRAY COUNT
DELETE DISPLAY
EDIT EXPORT TO
INDEX JOIN WITH
LABEL LIST
LOCATE RECALL
REPLACE REPLACE FROM ARRAY
REPORT SCAN
SET DELETED SET FILTER
SORT TO SUM
TOTAL TO

除了可优化 FOR 子句表达式之外,如果还使用了 Scope 子句,如果要充分利用 Rushmore 技术,范围子句必须 ALL 或 REST。NEXT 或 RECORD 范围子句不能使用 Rushmore。因为默认范围是 ALL,所以当省略 Scope 子句时,Rushmore 会起作用。

Rushmore 可以使用任何打开的索引,但筛选过的索引和唯一索引除外。

注释 为了优化操作,请不要给表中记录设定顺序。

创建索引或标识时会自动地对表排序。所以如果一个大型的数据库要最大限度地使用 Rushmore , 而它又必须按某种顺序排列,那么请用 SET ORDER TO 命令关闭所有索引控制,然后使用 SORT 命令。

有效组织适于 Rushmore 技术的索引

Rushmore 技术并不能利用所有的索引进行优化。如果您在 INDEX 命令中使用了一个 FOR 子句,Rushmore 技术就不能使用该索引进行优化。例如,下面的语句由于包含一个 FOR 子句,所以不能优化:

INDEX ON ORDNUM FOR DISCOUNT > 10 TAG ORDDISC

类似地,Rushmore 技术不能使用由一个 NOT 条件创建的索引。例如,下面的表达式可以优化:

INDEX ON DELETED() TAG DEL

但是下面这个却不能被优化:

INDEX ON NOT DELETED() TAG NOTDEL

在特殊情况下,您若想在一个查询中排除被删除的记录,请将 SET DELETED 设置为 ON,然后使用上面第一个示例中的索引,这样可以加速操作。

无 Rushmore 的操作

在下列几种情况下的数据检索操作不使用 Rushmore 优化技术:

关闭 Rushmore

尽管要关闭 Rushmore 的情况很少,但有时确实需要关闭。当您发出一个使用 Rushmore 的命令时,Visual FoxPro 立即确定哪些记录符合 FOR 子句表达式的要求,然后再由此命令操作这些匹配记录。

如果一个潜在的可优化命令修改了 FOR 子句中的索引关键字,则 Rushmore 操作的记录集合就可能变得过时。在这种情况下,就需要关闭 Rushmore,以确保从表中获得的是最新信息。

若要对单独的命令关闭 Rushmore

例如,下面的 LOCATE 命令就不会被优化:

LOCATE FOR DueDate < {^1998-01-01} NOOPTIMIZE

使用 SET OPTIMIZE 命令,可以在全局范围内对所有的命令关闭或启用 Rushmore 功能。

若要在全局范围内关闭 Rushmore

若要全局范围内启用 Rushmore

Rushmore 优化的默认设置是 ON。

优化 Rushmore 表达式

Rushmore 技术取决于 FOR 子句或 SQL WHERE 子句中是否出现基本可优化表达式。一个基本可优化表达式可以是整个表达式,也可以是表达式的一部分。您也可以对基本表达式进行各种组合构成复杂的可优化表达式。

创建基本可优化表达式

基本可优化表达式一般是下面两种形式:

eIndex relOp eExp

– 或者 –

eExpr relOp eIndex

基本可优化表达式具有以下特点:

您可以使用下列两种格式 BETWEEN( ) 或 INLIST( ) 函数:

eIndex BETWEEN(eIndex, eExpr, eExpr)

– 或者 –

eExpr INLIST(eIndex, eExpr)

注释 Rushmore 技术不能优化 ISBLANK( ) 和 EMPTY( ) 函数。

如果您创建了 firstname、custno、UPPER(lastname) 和 hiredate 索引,那么下列表达式都是可优化的。

firstname = "Fred"
custno >= 1000
UPPER(lastname) = "SMITH"
hiredate < {12/30/96}

可优化表达式可以包含具有赋有特定值的变量和函数。例如,使用 addr 索引,如果发出了 STORE 'WASHINGTON AVENUE' TO cVar 命令,那么下列语句也是基本可优化表达式:

ADDR = cVar
ADDR = SUBSTR(cVar,8,3)

深入理解何时查询是优化的

理解查询何时能被优化或不能被优化是很重要的。Visual FoxPro 是通过查找筛选表达式左边的部分与索引关键字表达式完全匹配来优化查找条件。因此,只有查找到在索引中使用的精确匹配的表达式 Rushmore 才能优化表达式。

例如,假设您刚创建一个表,并用如下的命令添加了第一个索引:

USE CUSTOMERS
INDEX ON UPPER(cu_name) TAG name

在下面的命令中搜索条件只是基于 cu_name 字段,而不是作为索引的那个表达式(UPPER(cu_name)),所以,不能优下这个命令,:

SELECT * FROM customers WHERE cu_name ="ACME"

相反,您应该用下面的命令创建一个可优化的表达式,因为您搜索的表达式完全与索引表达式相匹配:

SELECT * FROM customers WHERE UPPER(cu_name) = "ACME"

提示 要确定所使用的 Rushmore 优化的程度,可调用 SYS(3054) 命令。

组合基本可优化表达式

如果 FOR 表达式具有基本可优化表达式的各种特征,那么可以在 FOR 子句或 WHERE 子句基础上组合简单或复杂表达式以提高数据检索速度。

基本表达式可能是可优化的,可以使用 AND、OR 或 NOT 逻辑操作符组合这些基本表达式,以构成可优化的复杂 FOR 子句表达式。由可优化基本表达式组合创建的表达式是全部可优化的。如果有一个或多个基本表达式是不可优化的,那么复杂表达式可能是部分可优化,或者根本不可优化。

有一系列规则用于确定由基本可优化或不可优化表达式构成的表达式是全部可优化、部分可优化,还是根本不可优化的。下表总结了 Rushmore 查询优化规则。

组合基本表达式

基本表达式 操作符 基本表达式 查询结果
可优化 AND 可优化 全部可优化
可优化 OR 可优化 全部可优化
可优化 AND 不可优化 部分可优化
可优化 OR 不可优化 不可优化
不可优化 AND 不可优化 不可优化
不可优化 OR 不可优化 不可优化
NOT 可优化 全部可优化
NOT 不可优化 不可优化

可以用 AND 操作符将两个可优化表达式组合成一个全部可优化表达式:

FIRSTNAME = "FRED" AND HIREDATE < {12/30/96}      && 可优化

在下面示例中,OR 操作符将一个基本可优化表达式与一个不可优化表达式组合起来,创建一个不可优化表达式:

FIRSTNAME = "FRED" OR "S" $ LASTNAME      && 不可优化

在一个可优化表达式中使用 NOT 操作符,创建一个全部可优化表达式:

NOT FIRSTNAME = "FRED"      && 全部可优化

您可以使用圆括号将基本表达式分组。

组合复杂表达式

正如组合基本表达式一样,也可以组合复杂表达式,创建全部可优化、部分可优化或不可优化的更加复杂的表达式。然后再组合这些更复杂的表达式以创建全部或部分可优化的,或者根本不可优化的表达式。下表说明了组合这些复杂表达式的结果。这些规则也适用于用圆括号分组的表达式。

组合复杂表达式

表达式 操作符 表达式 结果
全部可优化 AND 全部可优化 全部可优化
全部可优化 OR 全部可优化 全部可优化
全部可优化 AND 部分可优化 部分可优化
全部可优化 OR 部分可优化 部分可优化
全部可优化 AND 不可优化 部分可优化
全部可优化 OR 不可优化 不可优化
NOT 全部可优化 全部可优化
部分可优化 AND 部分可优化 部分可优化
部分可优化 OR 部分可优化 部分可优化
部分可优化 AND 不可优化 部分可优化
部分可优化 OR 不可优化 不可优化
NOT 部分可优化 不可优化
不可优化 AND 不可优化 不可优化
不可优化 OR 不可优化 不可优化
NOT 不可优化 不可优化

可以将全部可优化表达式与 OR 操作符组合,以创建同样全部可优化的表达式:

* 全部可优化表达式
(FIRSTNAME = "FRED" AND HIREDATE < {^1997-12-30}) ;
   OR (LASTNAME = "" AND HIREDATE > {^1996-12-30})

要创建部分可优化表达式,可将全部可优化表达式与不可优化表达式组合。在下例中,使用了 AND 操作符组合表达式:

* 部分可优化表达式
(FIRSTNAME = "FRED" AND HIREDATE < {^1997-12-30}) ;
   AND "S" $ LASTNAME

可以组合部分可优化表达式,以创建同样也是部分可优化的表达式:

* 部分可优化表达式
(FIRSTNAME = "FRED" AND "S" $ LASTNAME) ;
   OR (FIRSTNAME = "DAVE" AND "T" $ LASTNAME)

组合不可优化的表达式创建不可优化的表达式:

* 不可优化的表达式
("FRED" $ FIRSTNAME OR "S" $ LASTNAME) ;   
   OR ("MAIN" $ STREET OR "AVE" $ STREET)

优化表单和控件

也可在您的应用程序中的表单和控件上作重大的改进。

提示 有关高效设置和获得属性的详细内容,请参阅本章稍后的“高效地引用对象属性”

使用数据环境

如果您使用了“表单设计器”或“报表设计器”的数据环境,则打开表的操作就比在表单的 Load 事件中执行 USESET ORDERSET RELATION 命令要快得多。当您使用数据环境时,Visual FoxPro 调用低级引擎调用来打开表并建立索引和关系。

在表单集里限制表单数目

只有在必须让一组表单共享一个私有数据工作期时才使用表单集。当您使用表单集时,即使是只显示表单集的第一个表单,Visual FoxPro 也要创建表单集中所有表单和所有控件的实例,这样做是要花费时间的,并且如果表单集不是共享一个私有数据工作期,这是不必要的。相反,可以定义为独立的表单,在需要其他表单时执行 DO FORM 命令启动它。

但是,如果您使用了表单集,在访问表单集中的表单时您会得到一些性能,因为所有表单集中的表单都已经加载在内存中,只是看不到。

在页框中动态加载页面控件

与表单集类似,加载页框时,也同时加载了每个页面上的所有控件,所以加载页框会引起到明显的延迟。相反,在必要时您可以动态地加载页面控件,通过在每个页面上的控件以外创建类,然后当激活页面时再加载这些控件。

若要动态加载页面控件

  1. 象通常一样设计表单,在所有页面上包含所有的控件。

  2. 当您的设计结束时,转到页框的第二页,将页面上所有的控件保存为一个类。

  3. 打开您创建的类,确保控件布局仍然正确。

  4. 对于页框中的第三个以及其他页面重复步骤 2 和步骤 3。

  5. 在第二个和其他随后的页面的 Activate 事件中,添加对象并使它们可见。

    例如,如果您的控件类名为 cnrpage1,您可以添加如下的代码:

    IF THIS.ControlCount = 0
    THIS.AddObject("cnrpage1","cnrpage1")
    THIS.cnrpage1.Visible = .T.
    ENDIF
    

动态绑定控件与数据

对于一个包含很多与数据绑定的控件的表单,如果您将绑定的时间延迟到需要它们的时候,可以加速表单的加载时间。

若要动态绑定控件与数据

  1. 将表单使用的表和视图放在数据环境中,这样当加载表单时表和视图就打开了。

  2. 对于每一个绑定控件,向它的 GotFocus 事件代码中添加将控件与数据值绑定的代码。例如,下面的代码将一个 ComboBox 控件与 customer.company 字段绑定:
    * 检查控件是否已经被绑定。
    IF THIS.RecordSource = ""
    * 将记录源设为右边的值
    * 将记录源设为“fieldsTHIS.RecordSource = "customer.company"
    THIS.RecordSourceType = 6
    THIS.Refresh
    ENDIF
    

延迟屏幕刷新

如果您必须对屏幕进行多处更改 — 例如,同时更改多个控件的值 — 可以通过将屏幕刷新延迟到所有更改都结束的时候进行,减少更新屏幕所需的总时间。例如,如果您将控件变更为可见的或不可见的,更改控件颜色,或者在绑定型控件中移动记录,则在所有更改都结束时再刷新这些控件将会大大的提高效率。

若要延迟屏幕刷新

  1. 将表单的 LockScreen 属性设置为真。

  2. 在需要时更新控件。

  3. 调用表单的 Refresh 方法程序。

  4. 将表单的 LockScreen 属性设置为假。

例如,下面的示例一次更改多个属性中的显示属性,移动到一个新记录,并且只在这时用新的信息刷新屏幕。如果 LockScreen 属性没有设置为真,每个这样的操作就会单独地刷新受影响的控件,这样总的更新操作就显得很缓慢。

THISFORM.LockScreen = .T.
THISFORM.MyButton.Caption = "Save"
THISFORM.MyGrid.BackColor = RGB (255, 0, 0) && 红色
SKIP IN customers
SKIP IN orders
THISFORM.Refresh
THISFORM.LockScreen = .F.

提示 如果您只更新一个控件,这项技术就不会带来任何好处。

在经常使用的方法程序中减少代码

因为 Refresh 方法程序和 Paint 事件经常被调用,在这些方法程序中减少代码的数量就可以提高表单的性能。类似地,若要缩短表单的加载时间,您可以将 Init 事件中的代码移到不经常使用的方法程序中,例如 ActivateClickGotFocus 事件。然后,使用控件的一个属性(例如 Tag 属性或一个自定义属性)来跟踪控件是否已经运行了只需运行一次的代码。

优化应用程序

仔细地编写您的代码,您可以编写出尽可能快的程序。下面是几条提高 Visual FoxPro 程序性能的方法:

提高性能的一般技巧

要想编写尽可能最快的程序,请遵循下列建议:

使用名称表达式代替宏替换

如果用名称表达式取代宏替换,程序性能将有很大改进。例如,给 cFile 变量指定一个值,则用 cFile 创建的名称表达式比宏替换更快。

cFile = "CUST"
use &cFile      && 宏替换,慢
use (cFile)      && 名称表达式,更快更好

有效地引用对象属性

理解了 Visual FoxPro 如何处理属性和对象,您可以使您的应用程序更加有效地运行。

优化对属性的重复引用

当您使用 object.property 语法引用一个对象的属性时,Visual FoxPro 在访问属性之前必须找到该对象。如果您必须重复访问次属性,这种查找的策略会降低性能。

若要避免重复引用相同的过程(例如在一个循环中),将属性值读入到一个变量中,再进行更改,然后在完成时一次性的设置属性。例如,下列的代码为填充一个属性数组,首先在内存中创建一个数组,填充它,最后一次设置属性:

*向一个局部变量中复制字符串
lcChar = THISFORM.cCharString
LOCAL laCharArray[256]   &&创建一个本地数组
FOR nCounter = 1 to 256
   laCharArray[x] = SUBSTR(laChar,x,1)
ENDFOR
*将本地数组复制到属性数组
ACOPY(laCharArray,THISFORM.aCharArray)

有效地引用多个属性

如果更新对象的多个属性,Visual FoxPro 必须多次寻找这个对象,这会影响程序的性能。在下面的示例中,为了找到要设置的属性,代码将使 Visual FoxPro 查找四个属性(例如 THISFORMpgfCstInfopgCstNametxtName)。因为代码要设置两个属性,这个四重的查找进行了两次。

THISFORM.pgfCstInfo.pgCstName.txtName.Value = ;
 "Fred Smith"
THISFORM.pgfCstInfo.pgCstName.txtName.BackColor = ;
 RGB (0,0,0)  & 暗红色

为了避免这种重复查找,请使用 WITH ... ENDWITH 命令。这种方法使 Visual FoxPro 只查找一次对象。例如,下面的示例完成与前面的示例相同的任务,但是更快:

WITH THISFORM.pgfCstInfo.pgCstName.txtName
   .Value = "Fred Smith"
   .BackColor = RGB (0,0,0)  & 暗红色
ENDWITH

您也可以将对象的引用保存在变量中,然后在引用对象的地方使用该变量:

oControl = THISFORM.pgfCstInfo.pgCstName.txtName
oControl.Value = "Fred Smith"
oControl.BackColor = RGB (0,0,0)  & 暗红色

优化 ActiveX 控件

如果在您的应用程序中使用了自动服务或 ActiveX 控件,您可以润色应用程序,从自动服务和 ActiveX 控件以外得到最好的性能。

有效地使用 ActiveX 控件

为了能够在您的表单中使用 ActiveX 控件时得到最好的性能,请采用如下建议:

优化自动操作

如果您的应用程序要与其他应用程序交互使用,使用下列技术可以得到最佳性能。

避免使用服务程序的多个实例

在某些情况,即使已经运行了一个实例,自动服务程序(例如 Microsoft Excel)仍要经常启动另一个新的实例。要想改变这一情况并提高性能,请使用 GetObject( ) 函数而不要使用 CreateObject( ) 函数。例如,下面的调用总是使用一个已存在的实例:

x = GetObject(,"excel.Application")

相反地,下面的调用就创建了一个新的实例:

x = CreateObject("excel.Application")

如果调用了 GetObject( ) 函数,而服务程序还没有运行,系统会返回错误 1426。在这种情况下,您可以俘获这个错误并且调用 CreateObject( ) 函数:

ON ERROR DO oleErr WITH ERROR()
x = GetObject(,"excel.application")
ON ERROR  && 恢复系统错误句柄。

PROCEDURE oleErr
PARAMETER mError
IF mError = 1426 then
 x = CreateObject("excel.application")
ENDIF

有效地引用对象

执行使用自动服务程序里的对象的表达式会很不划算,特别是当表达式需要多次赋值的时候。如果将对象的引用保存到变量中后再引用,会快得多。有关的详细内容,请参阅本章前面的“优化对属性的重复引用”

在多用户环境中优化应用程序

如果您在为多用户环境编写应用程序,程序的性能特别重要,因为低效率会是加倍的。另外,如果多个用户同时访问数据,您的应用程序必须解决并发和网络访问的问题。

要解决这些问题,您可以:

在本章稍后的“优化对远程数据的访问”一节中,包含了如何处理远程服务器上的数据的一些建议,您也可以从这些建议中受益。

调整锁定重试的间隔

如果您的应用程序试图锁定一个记录或表,但没有成功,您可以让 Visual FoxPro 在较短的间隔后自动重试锁定操作。但是,每次锁定尝试都会加重网络负担。如果网络负担已经很重了,发送重复的锁定请求加重了网络的负担,并会导致所有用户的工作速度都减慢了。

要想处理这种情况,您可以调整两个锁定尝试之间的间隔。使用一个较长的间隔(使得每秒的重试次数减少),减少网络负担,从而获得更好的性能。

若要调整锁定重试的间隔

有效地使用事务处理

当使用事务处理时,必须在设计事务时就考虑到尽量减少该事务对其他用户的影响。在打开事务时,在事务中的任何锁定设置都将保持锁定状态,直到事务被提交或回滚以后。即使您发出了一个明确的 UNLOCK 命令锁定也一直保持着,直到运行 END TRANSACTION 命令或 ROLLBACK 命令。

另外,将记录追加到表时,需要 Visual FoxPro 锁定表头。在事务处理过程中表头一直保持锁定状态,防止其他用户也追加记录。

要想减少事务的影响,最好将事务的开始和结束尽可能的与实际上的数据更新接近。最理想的事务仅包含有数据更新语句。

如果您向表单的数据更新添加一个事务处理,不要打开事务,运行表单,然后在关闭表单时提交事务。相反,您最好将事物处理语句放在“保存”按钮中的事件代码中(如下例所示):

* cmdSave 命令按钮的 Save 方法程序
BEGIN TRANSACTION
UPDATE PRODUCTS SET reorder_amt = 0 WHERE discontinued = .T.
END TRANSACTION

优化对远程数据的访问

从任何远程数据库中检索数据都是很费时的。为了从服务器数据库得到数据,会经过以下步骤:

  1. 客户向远程数据库发出查询请求。

  2. 服务器分析并编译查询。

  3. 服务器生成一个结果集。

  4. 服务器通知客户结果已经生成。

  5. 客户通过网络从服务器取数据。这一步可以一次完成,或者客户也可以要求将结果按要求分批传送。

您可以应用一系列的技术来加快数据的检索(或更新)。下面将讨论这些策略:

只检索您需要的数据

在大多数使用远程数据的应用程序中,表单和报表并不需要一次访问表中的所有数据。因此,通过创建只获取或更新需要的字段和记录的远程视图可以提高性能,这样就减少了通过网络来传输的数据的数量。

要使创建的查询尽可能地减少从远程数据源获取不必要的数据,请采用如下建议:

有效地更新远程表

当您使用视图更新远程数据源上的表时,Visual FoxPro 必须检查您更新的记录是否已被别人更改过了。为了做到这一点,Visual FoxPro 必须检验服务器上的数据,并将它与您的计算机上的数据进行比较。有时,这会花费一些时间。

为了优化更新远程数据源上数据的过程,您可以指定一种方式以确定 Visual FoxPro 如何检查哪些记录已被更改。您指定的方式实际上是指定了 Visual FoxPro 更新数据时如何自动产生 WHERE 子句。

例如,假设您正在使用一个视图,该视图基于远程数据源上的 customers 表。您使用 SELECT - SQL 语句创建了这个视图:

SELECT cust_id, company, address, contact ; 
   FROM customers ;
   WHERE region = ?vpRegion

您想更新在视图中指定的除了关键字段 (cust_id) 的所有其他字段。下面的表说明了对于每个选项,Visual FoxPro 将要生成跟在 SQL WHERE 子句后面的 WHERE 子句。

注释 OLDVAL( ) 函数返回更新前字段(数据),CURVAL( ) 函数返回存储在远程数据源上当前值。通过比较这两个值,Visual FoxPro 可以确定在您将远程数据源上的记录下载到您的计算机上后,该记录是否已被别人更改过。

设置 生成的 WHERE 子句
关键字段
WHERE OLDVAL(cust_id) = CURVAL(cust_id)
关键字和可更新字段(默认)
WHERE OLDVAL(cust_id) = CURVAL(cust_id) AND
   OLDVAL(<mod_fld1>) = CURVAL(<mod_fld1>) AND
   OLDVAL(<mod_fld2>) = CURVAL(<mod_fld2>) AND
   ...
关键字和已修改字段
WHERE OLDVAL(cust_id) = CURVAL(cust_id) AND
   OLDVAL(company) = CURVAL(company) AND
   OLDVAL(contact) = CURVAL(contact) AND
   OLDVAL(address) = CURVAL(address)
关键字和时间戳
WHERE OLDVAL(cust_id) = CURVAL(cust_id) AND
   OLDVAL(timestamp) = CURVAL(timestamp)

通常您应该按照下面的优先顺序为 SQL WHERE 子句选择一个选项:

  1. 关键字和时间戳。如果远程数据库支持时间戳字段,这是告诉记录是否已被更改最快的途径。

  2. 关键字和已修改字段。因为对服务器上更新的字段总是您能更新的所有字段数的子集。

  3. 关键字和可更新字段。

  4. 关键字段。使用这个设置意味着远程服务器将要用被你更改的关键字插入一个全新的记录,同时删除旧记录。

批处理方式发送语句

有些服务器(例如 Microsoft SQL Server)允许您在单独的数据包中发送一批 SQL 语句。因为减少了网络负担,并且服务器可以同时编译多个语句,所以可以提高性能。

例如,如果您将批量处理的大小指定为 4,然后更新数据库中 10 条的记录,Visual FoxPro 一次向服务器数据库在中发送如下所示 4 条语句:

UPDATE customer SET contact = "John Jones" ; 
   WHERE cust_id = 1;
UPDATE customer SET contact = "Sally Park" ; 
   WHERE cust_id = 2;
UPDATE customer SET company = "John Jones" ;
   WHERE cust_id = 3;
UPDATE customer SET contact = "John Jones" ;
   WHERE cust_id = 4

若要批量发送语句

  1. 在“视图设计器”中,从“查询”菜单中选择“高级选项”命令,显示“高级选项”对话框。

  2. 在“性能”区,在“成批更新的记录数”的旁边指定要在一批中传送的记录数。

    注释 您应该对于这个属性和 PacketSize 属性用不同的值来试验,以优化您的更新。

设置数据包大小

通过调整向远程数据库发送和检索的网络数据包大小,可以优化对远程服务器的访问。例如,如果您的网络支持大的数据包(大于 4096 字节),您可以在 Visual FoxPro 中增加数据包大小,从而对网络读或写时,每次传送更多数据。

若要设置数据包大小

延迟对备注和二进制数据的检索

如果您正在向远程服务器存储备注和二进制数据,将下载这样的数据延迟到真正需要的时候进行,可以了高性能。

若要延迟备注和二进制数据的检索

将待查阅数据保存在本地机上

很多应用程序包含了静态的查阅数据,例如州名缩写、邮政编码和雇员职务。如果您的应用程序包含这类数据,并且表也不是很大,在每个用户的计算机上保存这些信息的复件,可以提高您的应用程序的性能,因为,查阅不造成网络负担。

这项技术主要适用于从不改变或很少改变的数据。如果数据经常改变,您必须设计一种方法,用来向每个用户的计算机下载待查阅表的新复件。

创建本地规则

在 Visual FoxPro 中创建本地的字段级规则和记录级规则,而不要依赖与服务器上定义的规则,可以使您的应用程序获得效率。这些规则防止与数据或商业规则不符的数据进入数据库。

通过在 Visual FoxPro 中定义规则,可以防止无效数据在网络中传送,这使数据传送更快,并且可以更好地控制错误的情况。但是,使用本地规则也意味着您必须将它们与远程服务器上的规则一致。例如,如果服务器上的规则有了变化,必须更改您的本地规则,使它与服务器上的规则相符。

有关创建本地规则的详细内容,请参阅第八章“创建视图”中的“在视图中更新数据”

优化国际化应用程序

如果您开发国际化应用程序,为了优化操作,您可能需要管理数据的排序序列。这一节讨论:

有效地使用排序序列

如果您的数据不包含注音符号,例如重音 (á) 或变音 (ü) ,那么使用“machine”排序序列可以提高性能。因为:

因为“machine”排序序列更快,通常用于联接和查找,而其他排序序列用于排列记录。

当您创建一个索引时,Visual FoxPro 使用 SET COLLATE 的当前设置。因此,如果您想用两种排序序列创建两个索引,您可以使用如下的命令系列:

SET COLLATE TO "MACHINE"
INDEX ON lastname TAG _lastname     && 联接/查找索引
SET COLLATE TO "GENERAL"
INDEX ON lastname TAG lastname  && 排列索引

当您想根据 lastname 字段查找、选择或联接时,请在进行这些操作之前执行 SET COLLATE TO “MACHINE” 命令。Rushmore 技术就会使用在“machine”排序序列中创建的索引,则搜索操作就会变得非常快。

用多个排序序列发送 SQL SELECT 命令

当您执行 SELECT - SQL 命令时,Visual FoxPro 将当前的排序序列用于搜索,并用于 ORDER BY 和 GROUP BY 子句。如果您想用不同的排序序列搜索和排序,可以象下面一样把 SQL 命令分为两步:

* 使用一种排序序列选择记录
SET COLLATE TO "MACHINE"
SELECT * FROM table INTO CURSOR temp1 ;
  WHERE lname = "Müller"
* 使用另一种排序序列排列记录
SET COLLATE TO "GENERAL"
SELECT * FROM temp1 INTO TABLE output ORDER BY lastname