| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 7408 人关注过本帖, 1 人收藏
标题:发现1次EVALUATE()无法代替宏的,DBF转EXCEL
取消只看楼主 加入收藏
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
结帖率:98.98%
收藏(1)
已结贴  问题点数:20 回复次数:13 
发现1次EVALUATE()无法代替宏的,DBF转EXCEL
******************************************************************
** 经过吹斑竹的指导,改善后的代码在 19楼
** 在次优化后23楼,速度有点提高
******************************************************************

发现1次EVALUATE()无法代替宏的,见红字部分,不知道是不是我们的字符不对
** 建立测试数据
CREATE CURSOR 测试表(入库单号 C(7),入库日期 D,物料名称 C(20),数量 I,单价 N(6,2),单位 C(8),规格 C(40),付款 L,日期时间 T)
FOR i=1 To 100
    INSERT INTO 测试表(入库单号,入库日期,物料名称,数量,单价,单位,规格,付款,日期时间) VALUES ('C'+PADL(i,6,'0'),{^2018-03-01}+INT(RAND()*10),'物料'+PADL(i,3,'0'),INT(RAND()*1000),INT(RAND()*100)*1.00,'个','',IIF(i%2=0,.t.,.f.),DATETIME())
ENDFOR  
GO RECCOUNT() - 1
REPLACE 入库日期 WITH {},单价 WITH 9.01 && 修改倒数2记录的日期为空,单价为小数
 
s1=SECONDS()
SELECT 测试表
GO 1
 
** 下面4个参数,可以作为函数参数,在VFP9.0测试通过,10W调记录大概20秒
lnReccRows = RECCOUNT()  && 总记录数
lnPageSize = 50          && 每页记录   
lnPageRows = 2           && 行标题行位置
lnPageCols = 2           && 第1列的位置
lnFormat = 1             && 是否根据字段设置格式
 
SET MARK TO "-"
 
LOCAL lnPageCount,oExcel,lnWorkCount,lnPage,lnSheet,lnRows,lnFieldCount,lnStartRows,lnEndRows,lnStartCols,lnEndCols,lcStartCols,lcEndCols,lcRange
 
lnFieldCount = AFIELDS(AryField)             && 字段数量和字段内容
lnPageCount = CEILING(lnReccRows/lnPageSize) && 计算需要总页数
lnStartRows = lnPageRows                     && 初始化单元格"开始行数"
lnStartCols = lnPageCols                     && 初始化单元格"开始列数"
lnEndRows = lnPageSize + lnPageRows          && 初始化单元格"结束行数"
lnEndCols = lnFieldCount + lnPageCols        && 初始化单元格"结束列数"
 
oExcel=Createobject('excel.application') && 创建电子表格
oExcel.DisplayAlerts = .F.               && 关闭 Excle 提示对话框
oExcel.Visible=.f.                       && 使电子表格可见 .t.
oExcel.WorkBooks.Add                     && 创建工作簿
oExcel.ActiveWindow.WindowState = 2      && 最大化工作部
oExcel.Caption='数据导出'                && Excel标题
 
 
**-------------------------------------------------------------------------------------------
** 工作表不够,增加工作表
lnWorkCount = oExcel.Worksheets.Count
IF lnPageCount > lnWorkCount
   FOR lnPage = lnWorkCount + 1 To lnPageCount Step 1
       oExcel.Sheets.Add
   ENDFOR
ENDIF  
 
**-------------------------------------------------------------------------------------------
** 设置工作表名称
For lnPage = 1 To lnPageCount STEP 1
    oExcel.sheets(lnPage).Name = '第' + ALLTRIM(STR(lnPage)) + '页'
Endfor
 
**-------------------------------------------------------------------------------------------
** 利用剪贴板把DBF内容复制到EXCEL表
FOR lnSheet = 1 TO lnPageCount STEP 1 && 分别打开各个工作表
    oExcel.sheets(lnSheet).Activate
    lnRows = (lnSheet-1) * lnPageSize + 1 && 当前页数的第1个记录号
    lnPageMaxSize = IIF(lnPageSize * lnSheet > lnReccRows ,lnReccRows + lnPageSize - lnPageSize * lnSheet,lnPageSize) && 计算最后不满1页的记录数
    lnEndRows = lnPageMaxSize + lnPageRows
 
    ** 当前位置开始,复制一页的记录作为文本复制到剪贴板上,3使用制表符分隔字段
    GO lnRows
    _vfp.DataToClip(ALIAS(),lnPageMaxSize,3)
     
    ** 计算开始和结束的列标,如 A列,AB列,最后组合出区间,如 Range("A1:H50").Select 的格式.
    lcStartCols = IIF(INT(lnStartCols/26)*MOD(lnStartCols,26)>0,CHR(64+INT(lnStartCols/26)),IIF(INT(lnStartCols/26)>1,CHR(64+INT(lnStartCols/26)-1),"")) + ;
                  IIF(MOD(lnStartCols,26)=0,CHR(64+26),CHR(64+MOD(lnStartCols,26)))
    lcEndCols = IIF(INT(lnEndCols/26)*MOD(lnEndCols,26)>0,CHR(64+INT(lnEndCols/26)),IIF(INT(lnEndCols/26)>1,CHR(64+INT(lnEndCols/26)-1),"")) + ;
                IIF(MOD(lnEndCols,26)=0,CHR(64+26),CHR(64+MOD(lnEndCols,26)))
    lcRange = [oExcel.Range("] + lcStartCols + TRANSFORM(lnStartRows) + [:] + lcEndCols + TRANSFORM(lnEndRows) + [").Select]
 
    ** 在VFP中,我测试这样的格式 oExcel.Range( Cells(1,1), Cells(51, 6)).Select 时报错吴,说找不到 Cells.prg,但在EXCEL的VBA中是可以运行的,如果可以就不需要上面的计算列标.
    ** 为了找出计算列标的方法,花了我2小时的时间,先在EXCEL表中列出对应的值,然后用EXCEL公式逐行计算,得到上面的计算方法.
    ** lcRange = [oExcel.Range(Cells(] + TRANSFORM(lnStartRows) + [,] + TRANSFORM(lnStartCols) +[),Cells(] + TRANSFORM(lnEndRows) + [,] + TRANSFORM(lnEndCols) +[)).Select]
     
    &lcRange   && 选择区域,也可以用 EVALUATE(lcRange)
     
    oExcel.ActiveSheet.Paste && 粘贴数据
 
    oExcel.Columns.AutoFit
 
    ** 设置格式
    IF lnFormat>0 && 等于0时,不进行格式设置
       LOCAL lcCols,lnCols,lcSelect,lcFormat,lcFormatNumber
       FOR i=1 TO lnFieldCount
           lnCols = lnPageCols + i - 1
           lcCols = IIF(INT(lnCols/26)*MOD(lnCols,26)>0,CHR(64+INT(lnCols/26)),IIF(INT(lnCols/26)>1,CHR(64+INT(lnCols/26)-1),"")) + ;
                    IIF(MOD(lnCols,26)=0,CHR(64+26),CHR(64+MOD(lnCols,26)))
           lcSelect = [oExcel.Range("] + lcCols + TRANSFORM(lnPageRows+1) + [:] + lcCols + TRANSFORM(lnPageMaxSize+lnPageRows) + [").Select]
         
           &lcSelect  && 选择设置格式区域,也可以用 EVALUATE(lcSelect)
         
           DO CASE  
              CASE INLIST(AryField(i,2),"C","V")
                   lcFormat = "@"
              CASE AryField(i,2)="I"
                   lcFormat = "0_ "
              CASE INLIST(AryField(i,2),"B","N","F")
                   lcFormat = IIF(AryField(i,4) = 0,"0_ ","0."+REPLICATE("0",AryField(i,4))+"_ ")
              CASE AryField(i,2)="D"
                   lcFormat = "yyyy-m-d"
              CASE AryField(i,2)="T"
                   lcFormat = "yyyy-m-d hh:mm:ss"
              OTHERWISE  
                   lcFormat = "G/通用格式"
           ENDCASE  
         
           lcFormatNumber = [oExcel.Selection.NumberFormatLocal = ] + ["] + lcFormat + ["]
           &lcFormatNumber  &&设置EXCEL格式,但在这里用这个 EVALUATE(lcFormatNumber) 不出错,设置没效果,不知道什么原因.
           * EVALUATE(lcFormatNumber)
    ENDFOR
    ENDIF
     
ENDFOR  
oExcel.Visible=.T.
MESSAGEBOX(TRANSFORM(SECONDS()-S1))
 
 
* oExcel.Columns.AutoFit  
*!* oExcelApp.ActiveWorkbook.SaveAs(cExcelFile)       && 另存为
*!* oExcel.ActiveWorkbook.Save                        && 自动列宽
*!* oExcel.Workbooks.Close                            && 关闭表
*!* oExcel.Quit                                       && 退出Excel
*!* Release oExcel  &&关闭进程

[此贴子已经被作者于2018-3-23 13:54编辑过]

搜索更多相关主题的帖子: EXCEL 日期 INT 格式 计算 
2018-03-22 10:02
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
刚测试了,在红色位置哪里,用 EXECSCRIPT  也不可以,,,但用宏就可以
2018-03-22 10:35
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
以下是引用吹水佬在2018-3-22 10:53:47的发言:


正好试试 用 EXECSCRIPT 与 & 的执行效率看看。

可以了,是定义 PRIVATE oExcel 后就可以了.
2018-03-22 11:03
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
以下是引用csyx在2018-3-22 10:49:36的发言:

宏替换的基本常识,小数点代表宏结束!
试试这样: lcFormatNumber = [oExcel..Selection.NumberFormatLocal = ] + ["] + lcFormat + ["]

我的问题是说,用 宏& 在红色位置,是可以正常使用,但用EVALUATE(),就不可以,
刚才吹斑说,用EXECSCRIPT,但定义oExcel对象时,不能用 local来定义
2018-03-22 11:06
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
以下是引用吹水佬在2018-3-22 10:53:47的发言:


正好试试 用 EXECSCRIPT 与 & 的执行效率看看。

另外,这个问题
在VFP中,我测试这样的格式 oExcel.Range( Cells(1,1), Cells(51, 6)).Select 时报错吴,说找不到 Cells.prg,
但在EXCEL的VBA中是可以运行的
为什么会出现这个的
2018-03-22 11:27
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
这个不是数组,是EXCEL中选单元格的函数Cells(1,1), 这样表示选择第1行第1列
下面2句,在VBA中,运行的效果一样的.
图片附件: 游客没有浏览图片的权限,请 登录注册


[此贴子已经被作者于2018-3-22 11:47编辑过]

2018-03-22 11:42
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
oExcel.Cells[1,1].Value ="值" &&这个是可以,
但下面的这样就是不可以
刚才测试了,改为[]也不行,也提示找不到 Cells.prg
oExcel.Range( Cells[1,1], Cells[51, 6]).Select
如果,可以这样,就不用辛苦地去转换列了,如 27列,就不用转成AA

[此贴子已经被作者于2018-3-22 13:04编辑过]

2018-03-22 12:54
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
谢谢,原来,要这样才可以。。
2018-03-22 22:59
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
** I5 3470 CPU,WIN7 64系统,8G内存
** 设置格式时,20W条记录,用时 17秒,不设置格式时,用时16秒

CREATE CURSOR 测试表(入库单号 C(7),入库日期 D,物料名称 C(20),数量 I,单价 N(6,2),单位 C(8),规格 C(40),付款 L,日期时间 T)
FOR i=1 To 200001
    INSERT INTO 测试表(入库单号,入库日期,物料名称,数量,单价,单位,规格,付款,日期时间) VALUES ('C'+PADL(i,6,'0'),{^2018-03-01}+INT(RAND()*10),'物料'+PADL(i,3,'0'),INT(RAND()*1000),INT(RAND()*100)*1.00,'个','',IIF(i%2=0,.t.,.f.),DATETIME())
ENDFOR
GO RECCOUNT() - 1
REPLACE 入库日期 WITH {},单价 WITH 9.01 && 修改倒数2记录的日期为空,单价为小数

s1=SECONDS()
SELECT 测试表
GO 1

** 下面4个参数,可以作为函数参数,在VFP9.0测试通过,10W调记录大概20秒
lnReccRows = RECCOUNT()  && 总记录数
lnPageSize = 20000       && 每页记录  
lnPageRows = 2           && 行标题行位置
lnPageCols = 2           && 第1列的位置
lnFormat = 0             && 是否根据字段设置格式

SET MARK TO "-"

PRIVATE oExcel,lnPageCount,lnWorkCount,lnPage,lnSheet,lnRows,lnFieldCount,lnStartRows,lnEndRows,lnStartCols,lnEndCols
PRIVATE lnCols,lcSelect,lcRange,lcFormat,lcFormatNumber &&设置格式变量

lnFieldCount = AFIELDS(AryField)             && 字段数量和字段内容
lnPageCount = CEILING(lnReccRows/lnPageSize) && 计算需要总页数
lnStartRows = lnPageRows                     && 初始化单元格"开始行数"
lnStartCols = lnPageCols                     && 初始化单元格"开始列数"
lnEndRows = lnPageSize + lnPageRows          && 初始化单元格"结束行数"
lnEndCols = lnFieldCount + lnPageCols        && 初始化单元格"结束列数"

oExcel=Createobject('excel.application') && 创建电子表格
oExcel.DisplayAlerts = .F.               && 关闭 Excle 提示对话框
oExcel.Visible=.f.                       && 使电子表格可见 .t.
oExcel.WorkBooks.Add                     && 创建工作簿
oExcel.ActiveWindow.WindowState = 2      && 最大化工作部
oExcel.Caption='数据导出'                && Excel标题


** -----------------------------------------------------------------------------------------------------------
** 工作表不够,增加工作表
lnWorkCount = oExcel.Worksheets.Count
IF lnPageCount > lnWorkCount
   FOR lnPage = lnWorkCount + 1 To lnPageCount Step 1
       oExcel.Sheets.Add
   ENDFOR
ENDIF

** -----------------------------------------------------------------------------------------------------------
** 设置工作表名称
For lnPage = 1 To lnPageCount STEP 1
    oExcel.sheets(lnPage).Name = '第' + ALLTRIM(STR(lnPage)) + '页'
Endfor

** -----------------------------------------------------------------------------------------------------------
** 利用剪贴板把DBF内容复制到EXCEL表
FOR lnSheet = 1 TO lnPageCount STEP 1 && 分别打开各个工作表
    oExcel.sheets(lnSheet).Activate
    lnRows = (lnSheet-1) * lnPageSize + 1 && 当前页数的第1个记录号
    lnPageMaxSize = IIF(lnPageSize * lnSheet > lnReccRows ,lnReccRows + lnPageSize - lnPageSize * lnSheet,lnPageSize) && 计算最后不满1页的记录数
    lnEndRows = lnPageMaxSize + lnPageRows

** -----------------------------------------------------------------------------------------------------------
    ** 设置格式
    IF lnFormat>0 && 等于0时,不进行格式设置
       FOR i=1 TO lnFieldCount
           lnCols = lnPageCols + i - 1
           ** 设置格式,如运行 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,2)).Select
           lcSelect = [oExcel.Range(oExcel.Cells(] + TRANSFORM(lnPageRows+1) + [,] + TRANSFORM(lnCols) +[),oExcel.Cells(] + TRANSFORM(lnEndRows) + [,] + TRANSFORM(lnCols) +[)).Select]

*          &lcSelect  && 宏执行类似 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,2)).Select
           EXECSCRIPT(lcSelect) && 执行类似 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,2)).Select

           DO CASE
              CASE INLIST(AryField(i,2),"C","V")     && 文本格式
                   lcFormat = "@"
              CASE AryField(i,2)="I"                 && 整数格式
                   lcFormat = "0_ "
              CASE INLIST(AryField(i,2),"B","N","F") && 小数格式
                   lcFormat = IIF(AryField(i,4) = 0,"0_ ","0."+REPLICATE("0",AryField(i,4))+"_ ")
              CASE AryField(i,2)="D"                 && 日期格式
                   lcFormat = "yyyy-m-d"
              CASE AryField(i,2)="T"                 && 日期时间格式
                   lcFormat = "yyyy-m-d hh:mm:ss"
              OTHERWISE                              && 其他通用格式
                   lcFormat = "G/通用格式"
           ENDCASE
        
           lcFormatNumber = [oExcel.Selection.NumberFormatLocal = ] + ["] + lcFormat + ["]
*          &lcFormatNumber  && 宏执行类似 oExcel.Selection.NumberFormatLocal = "@"
           EXECSCRIPT(lcFormatNumber)  && 执行类似 oExcel.Selection.NumberFormatLocal = "@"
    ENDFOR
    ENDIF

** -----------------------------------------------------------------------------------------------------------   
    ** 从当前位置开始,复制一页的记录作为文本复制到剪贴板上,3使用制表符分隔字段
    GO lnRows
    _vfp.DataToClip(ALIAS(),lnPageMaxSize,3)

    ** 选择区域,如 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,10)).Select
    lcRange = [oExcel.Range(oExcel.Cells(] + TRANSFORM(lnStartRows) + [,] + TRANSFORM(lnStartCols) +[),oExcel.Cells(] + TRANSFORM(lnEndRows) + [,] + TRANSFORM(lnEndCols) +[)).Select]

*   &lcRange                 && 宏执行类似 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,10)).Select
    EXECSCRIPT(lcRange)      && 执行类似 oExcel.Range(oExcel.Cells( 2,2) ,oExcel.Cells(52,10)).Select
    oExcel.ActiveSheet.Paste && 粘贴数据

    oExcel.Columns.AutoFit
   
ENDFOR

MESSAGEBOX(TRANSFORM(SECONDS()-S1))
oExcel.Visible=.T.

*!* oExcelApp.ActiveWorkbook.SaveAs(cExcelFile)       && 另存为
*!* oExcel.ActiveWorkbook.Save                        && 自动列宽
*!* oExcel.Workbooks.Close                            && 关闭表
*!* oExcel.Quit                                       && 退出Excel
*!* Release oExcel  &&关闭进程
2018-03-23 00:02
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
是的,在以前的基础上修改的,以前是字符,所以要拼接...
今天在公式,运行19楼代码,竟然用了 53秒...( 酷睿双核2.4,4G,WIN7 32系统)
又发现1个现象:
1.把设置格式大代码放在粘贴数据代码前面时,有设置格式的时候,运行速度比没设置格式的运行速度要.有设置时,45秒,没设置时,51秒
2.把设置格式大代码放在粘贴数据代码后面时,有设置格式的时候,运行速度比没设置格式的运行速度要.有设置时,57秒,没设置时,51秒


[此贴子已经被作者于2018-3-23 14:01编辑过]

2018-03-23 12:41
快速回复:发现1次EVALUATE()无法代替宏的,DBF转EXCEL
数据加载中...
 
   



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

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