Foxpro、excel文件批量导入数据到Mysql、Oracle

CSDN论坛里时不时有朋友问起怎么把xls、dbf文件导入远程MySQL、Oracle等数据库,想当年我也为这个问题忙活了好一阵。如果十几、二十个文件,手工搞掂也就算了,上百个文件、二三十G,那怎么可能!免不了琢磨着搞个程序来做导入。

因为熟悉VFP,而且后面的工作还要用到VFP,所以整个程序就用VFP编了。

要点如下:

一、SQL数据库插入数据的标准办法是Insert

Insert Into table_name [( Fieldname1 [, FieldName2, ...])] Values ( eExpression1 [, eExpression2, ...])]

或者

Insert Into table_name [( Fieldname1 [, FieldName2, ...])] Select SelectClauses

前者先要表达式赋值,后要一行一行循环,费事。后者在SQL圈里打转,从SQL外导数到SQL内,行不通。

心里想着最好是能有一条语句,从SQL外文件导入到SQL内,就好象VFP的Append From。Append是VFP自己的命令,可以从数组也可以从文件一次过追加记录。另外,VFP对SQL命令Insert还有一个扩展:

Insert Into dbf_name From Arrary Arrayname
也支持从数组追加数据。

但这两种方法不是SQL标准,不适用于SQL环境,不能用来直接向MySQL、Oracle传送数据。

于是想到远程视图(View),变通一下,Append到视图。

一测试,发现远程视图要把数据先下载下来,这就不好了,目的是上传,下载干啥?而且VFP文件有2G的限制,视图里的数据肯定要放到个临时文件里,没上传就先超2G了。

后来到CSDN里请教,承蒙happyflystone、wanbolantian、十豆三等几位朋友出手相助、指点迷津,于是改用游标

VFP通过游标适配器(CursorAdapter)打开游标,所以要先创建一个游标适配器对象。类从默认CursorAdapter继承,通过游标适配器设计器(CursorAdapter Builder)定义。也可以通过程序定义,我初次使用游标,不熟悉,没办法,只好用设计器,跟着向导一步一步来,主要是定义远程连接、定义游标、定义数据同步和更新方式。过程就不罗嗦了,后面代码里我就直接用类创建对象了。

 

二、用VFP打开xls文件也是常有人问的问题,办法是建立一个Excel对象:

oExcel=CREATEOBJECT( 'Excel.application')

然后调用相关函数和方法,后面代码里我用到的几个。更多更详细说明请大家自己用谷歌搜索一下,能找到很多,我就是这样找的。

三、第三个关键提取导入文件夹下的所有文件名,遍历循环、导入。VFP提取文件名的函数是ADIR()。

 

下面跟着程序一步一步看过去。

&&本程序从指定文件夹读入xls或dbf文件,导入远程数据库。 *******以下参数定义数据在xls文件中的位置。 cImportSheet = 'sheet1' &&xls文件中数据所在的表 nImportStartRow = 8 &&数据在表中的起始行号 nImportOmitRow = 1 &&最后忽略的行数。所获表中,最后一行是合计数,无需导入。 sImportStartColumn = 'A' &&数据在表中的起始列号。 sImportEndColumn = 'AC' &&在表中的终止列号。

数据不见得固定在xls文件中特定位置,把数据区域参数化,有利于提高程序的灵活性。我的xls文件中第7行是字段名,数据从第8行开始,最后有一合计行。因此首尾两行又被利用来作过程监控、校验。

 

为了进一步监控导入过程,我又另建了一个importfiles.dbf文件,把导入文件名、导入行数等特征记录下来。

*******对导入文件进行记录,这八个参数表示导入文件的文件特征被记录的位置。 *******数据导入过程中难免发生意外,为此特地建立一个数组arrImport,收集导入过程中的各种状态,方便回退和调整。 *******数组arrImport各列有特定含义,用符号表示,方便记忆,方便调整。 nImportFileNameColumn = 1 &&这一列记录文件名。 nImportFileFieldColumn = 2 &&字段名。记录导入数据字段名行第一列。 nImportFileEndColumn = 3 &&尾行。记录导入表格最后一行第一列。 nImportFileRowAmount = 4 &&数据总行数,xls文件数据行数。 nImportRows = 5 &&实际插入数据库行数。 nSuccessfulOr = 6 &&导入成功标志; nRareCharactersColumn = 7 &&arrImport的这个列表示是否发生了生僻字替换。 nTargetTableColumn = 8 &&导入目标表名称。 nImportTime = 9 &&导入时间。此参数同时用于arrImport数组声明。

现在互联网上各种字符编码太多,不见得都能兼容,测试时几次遇到生僻字就被中断出来,搞得很不耐烦,好在多一个字少一个字不是大事,不想在这个问题上纠缠、浪费时间,因此干脆把生僻字置换掉。同时把是否发生过置换也记录下来。

 

提取导入文件名。

cImportFold = GETDIR( '','请选择导入文件所在文件夹','',16) &&打开文件夹选择对话窗,选择导入文件所在文件夹。 IF EMPTY( cImportFold ) THEN &&如未选择输入文件夹,输入终止 MESSAGEBOX( '您未选择任何导入文件夹,导入操作终止',48,'提示') RETURN ELSE &&进入战斗状态,开始实质导入。这个else直到程序尾才结束。 cSaveAsFold = GETDIR( '','请选择另存文件夹。文件导入成功后移到此处,xls文件另存为Excel9795格式、dbf文件维持原状。','',80 ) lIfSaveAs = !EMPTY( cSaveAsFold ) &&如果没有选择另存文件夹,视同不要求另存。 IF !lIfSaveAs THEN MESSAGEBOX( '您未选择另存文件夹,导入文件不移存。',48,'提示') ENDIF nImportFileAmount = ADIR( arrImportFileList, cImportFold + '*.*' ) &&提取所选文件夹下所有文件名。

导入过程随时都有可能中断,把已经导入的文件移开,下次继续时从没成功的开始。

我拿到的xls文件大概是2003格式,无意中发现9795格式的文件要小很多,于是决定另存时改变格式,反正也不是什么花哨表格。

 

建立Excel和游标对象。

oExcel=CREATEOBJECT( 'Excel.application') &&建立Excel对象,准备打开xls文件。 *******游标适配器通过游标设计器(CursorAdapter Builder)预定义,主要是定义远程连接、定义游标、定义同步和更新方式。 csrTarget = NEWOBJECT( 'cursor_addendum','d:/tax/classes/cursor_taxable.vcx' ) &&创建游标适配器对象。 TargetCursor = csrTarget.alias &&提取游标,数据直接往这个里面导,和数据库的同步问题有适配器解决,不需要程序处理。

 

开始遍历,

FOR nCount = 1 TO nImportFileAmount &&用文件数控制循环。 cFileName = arrImportFilelist( nCount,1 ) &&提取导入文件文件名 DECLARE arrImport( nImportTime ) &&本数组用于记录导入文件的文件特征。 arrImport( nImportFileNameColumn ) = cImportFold + cFileName &&首先收集导入文件名。 arrImport( nSuccessfulOr ) = .t. &&预设导入成功,不成功时调整本变量值。 &&dbf文件导入过程相对简单,意外可能性很小,只记录文件名和成功标志,不记录其他特征。 arrImport( nTargetTableColumn ) = TargetCursor csrTarget.cursorfill() &&游标定义时设置属性NoData=.t.,表示打开但不取数。 &&因此每次循环开始时重新打开游标,强制清空缓存,防止一次导入数据太多、缓存文件超过VFP限制。 DO CASE CASE JUSTEXT( cFileName ) = 'XLS' &&如果导入Excel文件。 oExcel.Workbooks.Open( arrImport( nImportFileNameColumn )) &&打开文件。 oExcel.Worksheets( cImportSheet ).Activate &&激活指定表格。 arrImport( nImportFileFieldColumn ) = oExcel.ActiveSheet.Cells( nImportStartRow - 1, 1 ).VALUE &&假设第一行导入数据之前是字段名行,记录第一个字段名,用于导入结束后核对、确认。 nImportEndRow = oExcel.ActiveSheet.UsedRange.ROWS.COUNT - nImportOmitRow &&确定最后行号。我的数据最后一行是合计行,所以有效数据要提前一行。 arrImport( nImportFileEndColumn ) = oExcel.ActiveSheet.Cells( nImportEndRow + 1, 1 ).VALUE &&记录导入表格最后一行第一个字段,视作结尾标志。 arrImport( nImportFileRowAmount ) = nImportEndRow - nImportStartRow + 1 &&记录导入文件总行数,与实际导入行数核对。 sRange = sImportStartColumn + ALLTRIM( STR( nImportStartRow )) + ':' + sImportEndColumn + ALLTRIM( STR( nImportEndRow )) &&生成导入区间,例如A7:G100。 &&VFP不一定兼容互联网文件字符集,会引起程序中断,导入前先剔除。 arrImport( nRareCharactersColumn ) = oExcel.ActiveSheet.Range( sRange ).replace( '韩?','韩青') &&剔除生僻字 arrImportRecords = oExcel.ActiveSheet.Range( sRange ).VALUE &&将导入数据存入数组。 INSERT INTO &TargetCursor FROM ARRAY arrImportRecords &&数据插入游标 arrImport( nImportRows ) = _Tally &&获得实际导入行数。 IF TABLEUPDATE( 1,.f., TargetCursor ) &&导入成功? IF lIfSaveAs THEN oExcel.ActiveWorkbook.SaveAs( cSaveAsFold + cFileName, 43 ) &&若已选择另存文件夹,则另存。 ENDIF oExcel.ActiveWorkbook.saved = .t. &&不保存当前EXCEL表。&&这一句也许多余。 oExcel.Workbooks.close &&关闭表。 IF lIfSaveAs THEN ERASE arrImport( nImportFileNameColumn ) RECYCLE &&删除原文件到回收站。 ENDIF ELSE arrImport( nSuccessfulOr ) = .f. &&导入不成功。 oExcel.ActiveWorkbook.saved = .t. &&不保存当前EXCEL表 oExcel.Workbooks.close ENDIF arrImport( nImportTime ) = DATETIME() &&记录处理时间。 CASE JUSTEXT( cFileName ) = 'DBF' &&如果是dbf文件 IF !USED( JUSTSTEM( arrImport( nImportFileNameColumn ))) THEN USE arrImport( nImportFileNameColumn ) IN 0 &&如果还没有打开dbf文件,则打开。 ENDIF SELECT JUSTSTEM( arrImport( nImportFileNameColumn )) &&如果已经打开,则转为当前表。VFP特色。 arrImport( nImportFileRowAmount ) = RECCOUNT() &&记录导入文件总行数。即使导入正常结束,实际导入行数也可能不等于应导入行数。 UPDATE JUSTSTEM( arrImport( nImportFileNameColumn )) SET nsr_mc = '韩青' WHERE nsr_mc like '韩%' arrImport( nRareCharactersColumn ) = ( _Tally <> 0 ) UPDATE JUSTSTEM( arrImport( nImportFileNameColumn )) SET mc = '开发区培训中心' WHERE bm ='1910000738' arrImport( nRareCharactersColumn ) = arrImport( nRareCharactersColumn ) or ( _Tally <> 0 ) USE SELECT &TargetCursor &&转游标。这里要用宏替换。 APPEND FROM arrImport( nImportFileNameColumn ) &&导入游标。 arrImport( nImportRows ) = _Tally &&获得实际导入行数。 IF TABLEUPDATE( 1,.f., TargetCursor ) IF lIfSaveAs THEN COPY file arrImport( nImportFileNameColumn ) TO ( cSaveAsFold + cFileName ) &&若已选择另存文件夹,则另存。 ERASE arrImport( nImportFileNameColumn ) RECYCLE &&删除原文件。 ENDIF ELSE arrImport( nSuccessfulOr ) = .f. &&导入不成功。 ENDIF arrImport( nImportTime ) = DATETIME() &&处理时间。 ENDCASE IF !USED( 'importfiles' ) then &&importfiles.dbf专门用作导入日志,保存导入过程中的各种状态。 USE d:/tax/xls2vfp!importfiles IN 0 ELSE SELECT importfiles ENDIF INSERT INTO importfiles FROM ARRAY arrImport &&状态数组导入日志文件。 USE RELEASE arrImport, arrImportRecords &&释放本次导入特征记录。 ENDFOR &&循环结束。

打扫战场,释放对象、清空变量。

oExcel.quit &&退出EXCEL RELEASE oExcel, csrTarget &&释放变量 MESSAGEBOX( '所有数据导入完毕,输入操作终止',64,'提示') &&本次数据导入完毕。 ENDIF RETURN

 

啰哩叭嗦一堆,大家火眼金睛,挑自己有用的拿。

欢迎切磋、交流。

 

你可能感兴趣的:(Foxpro、excel文件批量导入数据到Mysql、Oracle)