标题:VFP学习、开发漫谈 (五)
只看楼主
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:649
专家分:2156
注 册:2014-2-7
结帖率:96.77%
 问题点数:0 回复次数:14 
VFP学习、开发漫谈 (五)
应网友要求,今天就聊一聊在局域网环境下,如何提高客户机的访问速度。

首先,数据要与程序分离,即:数据库(表)存放在服务器上,程序安装在客户端上。无论服务器的性能有多好,终究不如从本地启动程序来得快。但有一个问题需要考虑,这就是程序的自动更新问题。

一个运行中的程序是不可能更新自身的,所以,必须从另一个程序去更新。为此,我用 VFP 开发了一个通用的小程序 Start.exe 和一个参数文件 Install.ini,用它来检测程序文件是否需要更新,更新完成后再启动主程序。在桌面上建立应用程序快捷方式时,应指向 Start.exe 而不是应用程序本身。

Install.ini 文件中保存的是应用程序的更新路径以及应用程序的名称,内容仅两行:
    UpdateDir= \\Server1\ERP$\Update
    StartExeFile= main.exe

第一行设置程序的更新路径,即要从哪里查找新程序,第二行设置应用程序的文件名。建议将该文件加密,以防止用户修改该文件、查看应用程序的访问路径。为此,我还编写了一个字符串加密函数:

FUNCTION CryptStr(tcStr,tlType)        && tcStr:目标字符串,tlType:操作类型 .t.[加密]  .f.[解密]
    LOCAL i,cRet,cChr
    i = ASC(LEFT(tcStr,1))            && 取第一个字符的 Ascii码
    IF (i>126 AND tlType) OR (i<=126 AND !tlType)    && 若 i>126,说明字符串已加密,否则,说明字符串未加密
        cRet = tcStr                && 无需加解密,返回原字符串
    ELSE
        cRet = ''
        FOR i = 1 TO LEN(tcStr)
            cChr = SUBSTR(tcStr,i,1)
            cRet = cRet + CHR(MOD(ASC(cChr)+128,256))
        NEXT
    ENDIF
    RETURN cRet
ENDFUNC

Start.exe 的源代码相对比较简单,如下所示(以下代码仅保留了核心算法,有删减):

#DEFINE CRLF CHR(13)+CHR(10)                && 定义回车换行符常量
LOCAL cStr,cUpdateDir,cStartExeFile
SET DEFAULT TO (JUSTPATH(SYS(16)))          && 设默认路径为 Start.exe 之路径
IF !FILE('Install.ini')                      && 检测本地配置文件是否存在
    MESSAGEBOX('未发现配置文件“Install.ini”!',16,'提示')
    RETURN
ENDIF
cStr = FILETOSTR('Install.ini')                         && 本地配置文件放入字符串
IF ASC(LEFT(cStr,1)) < 127                              && 本地配置文件加密
    STRTOFILE(CryptStr(cStr,.t.),'Install.ini')
ENDIF
cStr = CryptStr(cStr,.f.) + CRLF                        && 解密并添加回车换行符防止末行未回车
cUpdateDir = ADDBS(STREXTRACT(cStr,'UpdateDir=',CRLF))  && 升级路径
cStartExeFile = STREXTRACT(cStr,'StartExeFile=',CRLF)   && 主程序
IF FILE(cStartExeFile)                                  && 主程序存在时,检测文件是否需要更新
    = ADIR(aFileOld, cStartExeFile)                     && 获取本地主程序属性到数组 aFileOld[]
    = ADIR(aFileNew, cUpdateDir+cStartExeFile)          && 获取网络主程序属性到数组 aFileNew[]
    IF aFileOld[1,2] # aFileNew[1,2] OR;                && 若文件大小或更新时间不同,更新之
        aFileOld[1,3] # aFileNew[1,3] OR ;
        aFileOld[1,4] # aFileNew[1,4]
        COPY FILE (cUpdateDir+'*.*') TO *.*
    ENDIF
ELSE
    COPY FILE (cUpdateDir+'*.*') TO *.*      && 主程序不存在时,更新之
ENDIF
RUN /N &cStartExeFile                        && 启动主程序
QUIT

提高访问速度的第二个途径是,数据库的优化设计和编程方法。这个命题有点大,具体来说,可从以下几个方面考虑:

1. 数据表的记录越多,访问速度肯定越慢。在设计数据表时,要将当前数据和历史数据分开存放,对当前数据表可以添加、修改和删除,对历史数据仅能查询统计。通过定期将数据移至历史库,形成良性循环。

以我公司的材料明细账为例,每年的记录在10万条左右,每年末,我都将其复制到一个历史数据表中(如:明细账_2013.dbf),然后再将数据从原数据表中清除。为了加快对历史数据的访问,历史数据表应该包括相关表中的字段,如:材料名称、计量单位等,这样在查询时,无需与任何数据表连接,毕竟连接是需要时间的。这是典型的“以空间换速度”。另外,对经常检索的字段添加索引。

2. 对经常查询的字段建立索引标识,就不用我说了,每个用户都会想到。这里我要说的是:最好为每个表都建立一个主索引。假设工资表以“员工编号”为主索引,那么执行“UPDATE 工资表 SET 工资=8000 WHERE 员工编号=287”时,系统仅检索一条记录,若员工编号为普通索引,则系统会检索所有记录。

在设置主索引时,有一个问题需要注意:主索引不允许重复是包含逻辑删除记录的,即:未删除记录的索引值不能和逻辑删除记录的索引值相同。因此在索引条件中应该添加“FOR !DELETE()”,除非字段类型是“Integer(AutoInc)”。

3. 尽可能少用 Set Filter To 或数据环境中表的 Filter 属性,可以改用“条件索引”。因为设置了 Filter 过滤条件后,每移动一下记录指针,都需要判断过滤条件,对系统的响应速度影响很大。而采用条件索引就不存在此问题。
 
举例来说,物料主文件表有一个“类别”字段,1 表示材料,2 表示自制件。该表被 BOM 表单调用。先为该表建立两个索引标识:材料、自制件。这两个索引的表达式都是“物料代码”,但索引条件不同,分别是“类别=1”、“类别=2”。在表单的数据环境中,将“物料主文件.dbf”表添加两次,分别命名为“材料”和“自制件”,并分别指定索引标识为“材料”、“自制件”。这要比使用过滤条件访问数据快很多。

另外,将满足条件的记录放在游标中(我常使用 Select ... Into Cursor 游标名),也要比设置过滤条件好些。虽然执行查询时费些时间,但移动记录时就没有“停顿”感了。由于查询结果仅是表的一个“快照”不能实时反映新数据,因此,我一般都在表单上放置一个“刷新”按钮,用于重新查询。

4. 在“一对多”数据查询表单中,使用关联要比从子表中手工检索数据快,数据量越大,速度差异越明显。下图是采购计划表单的设计界面和数据环境:



5. 不要在打开表单时,就去执行耗时较长的查询操作,而应在用户单击某个命令按钮时再执行。比如:检验员对入库单进行审核时,如果从数据库中检索需要审核的入库单耗时较长,那么就不要试图在用户打开窗口时就显示这些入库单,而应该在表单上添加一个命令按钮,单击该按钮后才显示。

6. 适当把握记录的显示“时机”,使其“按需显示”。下图是一个材料字典窗口,初始时,仅显示材料的分类树,当单击某个分类节点时,才显示该分类的材料记录(查询材料记录的代码放在oleTree.NodeClick事件中):


7. 速度的快与慢本来就是一种主观上的操作感受,如果在用户等待过程中,能够显示操作进度或提示性文字,就会大大改善用户的使用体验。应该养成一个好的编程习惯:在用户等待过程中给出提示信息。

下面是一个显示提示信息的自定义函数,支持多行,窗口自动居中:
   
FUNCTION WaitWindow(cString,tnSeconds)
    * cString:  要显示的信息,使用CHR(10)换行
    * tnSeconds:等待的秒数,若省略则一直显示,直到发出 Wait Clear
    #DEFINE CR CHR(13)
    LOCAL nScrRows,nScrCols,nRows,nCols,i
    nScrRows = SROWS()    && 屏幕行数
    nScrCols = SCOLS()    && 屏幕列数
    * 找出显示字符串的行数(nRows)和最大列数(nCols),并将行存入数组
    nRows = OCCURS(CR,cString) + 1
    cString = CR + cString + CR
    nCols = 0
    DIMENSION aStr[nRows]
    FOR i = 1 TO nRows
        aStr[i] = STREXTRACT(cString,CR,CR,i)
        nCols = MAX(nCols,LEN(aStr[i]))
    NEXT
    * 重新生成显示字符串(每行居中)
    cString = ''
    FOR i = 1 TO nRows
        cString = cString + IIF(i=1,'',CR)+PADC(aStr[i],nCols)
    NEXT
    * 以窗口方式居中显示
    DO CASE
    CASE EMPTY(tnSeconds)
        WAIT cString WINDOW AT (nScrRows-nRows)/2+WLROW('')+4,(nScrCols-nCols)/2+WLCOL('') NOWAIT NOCLEAR
    CASE TYPE('tnSeconds') = 'N'
        WAIT cString WINDOW AT (nScrRows-nRows)/2+WLROW('')+4,(nScrCols-nCols)/2+WLCOL('') TIMEOUT tnSeconds
    OTHERWISE
        WAIT cString WINDOW AT (nScrRows-nRows)/2+WLROW('')+4,(nScrCols-nCols)/2+WLCOL('')
    ENDCASE
ENDFUNC

8. 对于频繁调用的表单,如:第 6 条中的材料字典窗口,经常被其他表单调用,频繁加载和释放表单会消耗大量的系统资源,因此,可将关闭窗口操作改为隐藏窗口,下次要显示时,将窗口 Show 一下就可以了。


[ 本帖最后由 liuxingang28 于 2014-2-28 13:04 编辑 ]
收到的鲜花
  • tlliqi2014-02-27 20:52 送鲜花  20朵   附言:谢了
  • hu9jj2014-02-28 08:09 送鲜花  50朵   附言:好文章
搜索更多相关主题的帖子: 应用程序 服务器 局域网 数据库 客户机 
2014-02-27 15:13
wengjl
Rank: 14Rank: 14Rank: 14Rank: 14
等 级:贵宾
威 望:108
帖 子:2175
专家分:3785
注 册:2007-4-27
得分:0 
好,期待后续……谢

只求每天有一丁点儿的进步就可以了
2014-02-27 15:43
hepu
Rank: 2
等 级:论坛游民
帖 子:62
专家分:27
注 册:2011-12-16
得分:0 
不错,学习了!
2014-02-27 18:29
tlliqi
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:204
帖 子:15453
专家分:65956
注 册:2006-4-27
得分:0 
学习
2014-02-27 20:55
hu9jj
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:红土地
等 级:贵宾
威 望:396
帖 子:11713
专家分:43267
注 册:2006-5-13
得分:0 
非常敬佩楼主的精神。

活到老,学到老! http://www. E-mail:hu-jj@
2014-02-28 08:09
qczx3358
Rank: 2
等 级:论坛游民
帖 子:44
专家分:15
注 册:2014-1-19
得分:0 
不错文章,支持你噢
2014-02-28 08:17
asdf_123000
Rank: 4
等 级:业余侠客
威 望:1
帖 子:262
专家分:203
注 册:2012-12-20
得分:0 
这是我们跟着本论坛学习的精髓
局域网内的问题有了提示
期待广域网相关知识也有所提示
特别象时髦的QQ对话功能
2014-02-28 09:20
taifu945
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:80
帖 子:1545
专家分:3298
注 册:2012-7-6
得分:0 
我建议版主把这一系列的讲座网址按顺序集中到一个新的索引帖中,并将这个索引帖固顶,方便大家查找和学习。
2014-02-28 12:02
sdta
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:江苏省连云港市
等 级:版主
威 望:323
帖 子:9621
专家分:26174
注 册:2012-2-5
得分:0 
本人只有3个置顶权限,望其他版主,将其它的内容置顶,谢谢了!

坚守VFP最后的阵地
2014-02-28 12:15
CHB123
Rank: 2
来 自:湖北
等 级:论坛游民
帖 子:100
专家分:68
注 册:2012-7-2
得分:0 
太棒了!!!真心谢谢!!!!
2014-02-28 12:41



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-427761-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.369024 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved