一、前言
在项目研发过程中,需要开发一个数据库批量操作的动态链接库(DLL),以前的实现主要是程序中直接调用bcp.exe,这种方式由应用程序创建子进程,不好控制批量操作过程,失败跟踪难度比较大,因此想利用bcp.exe调用的函数来实现操作过程。本人通过分析bcp.exe程序,得到了批量操作的DB LIBRARY API函数,再查阅API函数的资料得以实现该动态链接库。
二、实现
批量操作动态链接库只实现了一个输出函数, 应用程序通过动态加载DLL,再获取函数地址,便可调用函数实现批量操作。
输出函数定义如下:
LIBBCP_API BOOL BCP_Transfer_2(const char *task, const char *step, const char *config, long *copiedrow);在动态链接库中定义了两个类:CInteriorGlobal和CSYBBCP。CInteriorGlobal完成全局的初始化操作,CSYBBCP实现数据库的批量操作。
在调用Sybase数据库的DB LIBRARY API函数进行数据库的相关操作时,首先需要调用dbsetversion函数设置版本信息,这个函数只能调用一次,如果再次调用则会报错。而类CSYBBCP在BCP_Transfer_2函数中动态创建和释放,如果在CSYBBCP中直接调用dbsetversion会导致多次调用出错。因此需要采用一种机制让dbsetversion只能调用一次,这里使用了设计模式中的SingleTom模式,SingleTom模式就是确保实例唯一,本人利用该类仅做一次实例化操作来初始化Sybase客户端版本信息。
下面是CInteriorGlobal的定义:
class CInteriorGlobal { public: static CInteriorGlobal *Instance(); private: CInteriorGlobal(); private: static CInteriorGlobal *_instance; }; |
CInteriorGlobal的实现,在构造函数中设置版本信息:
CInteriorGlobal::CInteriorGlobal() { dbsetversion(DBVERSION_100); } CInteriorGlobal *CInteriorGlobal::_instance = 0; CInteriorGlobal * CInteriorGlobal::Instance() { if(0 == _instance) _instance = new CInteriorGlobal; return _instance; } |
为了完成批量操作,定义类CSYBBCP,具体定义如下:
class CSYBBCP { public: CSYBBCP(); ~CSYBBCP(); BOOL DoConnect(int taskindex, int stepindex, char *server, char *database, char *username, char *password, char *charset, char *language); BOOL DoQuery(char *sql, char **buf, int *rowcount, int *fieldcount); BOOL DoUpdate(char *sql, char *database = NULL); BOOL BCP_Connect(int taskindex, int stepindex, char *server, char *database, char *username, char *password, char *charset, char *language); BOOL BCP_Transfer_db(char *sql, char *fldterminator, char *rowterminator, int direction, char *datafile, char *errfile, long *copiedrow); private: BOOL m_isbcpout; int m_stepindex; int m_taskindex; char m_viewname[MAX_STRING_NUM]; char m_database[MAX_STRING_NUM]; DBPROCESS *m_dbproc; private: int GetTableFieldNums(char *table); BOOL DoDisconnect(); }; |
在类CSYBBCP中,主要是函数BCP_Transfer_db进行数据库大批量数据的导入和导出,要完成数据传输操作,需要如下几个步骤:
// 初始化:指定表明和数据文件 if(bcp_init(m_dbproc, tablename, datafile, NULL, direction) == FAIL) { return FALSE; } // 设置批量操作的控制参数,这里设置的每批记录数 if(bcp_control(m_dbproc, BCPBATCH, (DBINT) 1000) == FAIL) { return FALSE; } // 设置列数 if(bcp_columns(m_dbproc, cCols) == FAIL) { return FALSE; } // 设置列格式 for(ii = 1; ii < cCols; ii++) { if(bcp_colfmt(m_dbproc, ii, SYBCHAR, 0, -1, (UINT8 *) fldterminator, _strlen(fldterminator), ii) == FAIL) { return FALSE; } } if(bcp_colfmt(m_dbproc, ii, SYBCHAR, 0, -1, (UINT8 *) rowterminator, _strlen(rowterminator), ii) == FAIL) { return FALSE; } // 执行批量操作 while(bcp_exec(m_dbproc, & cRows) == FAIL) { return FALSE; } // 批量操作结束 retcode = bcp_done(m_dbproc); |
在使用Sybase12.5客户端之前,程序未调用bcp_control函数,在执行bcp_exec函数时不是使用while,而是使用if判断,代码如下:
if(bcp_exec(m_dbproc, & cRows) == FAIL) { return FALSE; } |
程序能正常完成功能,当使用Sybase12.5客户端后,在执行时发现程序突然退出,异常处理也未能记录日志,后跟踪发现程序是在执行bcp_exec时退出,但是未能查出原因,咨询Sybase公司技术人员,也没能解决问题。后来在一次测试中偶然发现有时能导入数据,于是测试数据文件在什么情况下能导入,实验其临界点,多次测试后发现文件1000条记录为临界点,超过则出现问题。于是本人在程序中调用bcp_control函数,设置批量记录为1000,如果数据文件记录多于1000,则需要bcp_exec执行多次才能完成,所以采用while,而不是if,这样问题解决。
三、结束
在上面的论述中,还仅仅涉及DB LIBRARY,对于Sybase客户端编程,还有CT LIBRARY方式,目前CT已经支持导出,但不支持导入。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
趣解数据库市场竞争格局
数据库咨询顾问Curt Monash对数据库技术的发展格局进行了趣味解读,包括如Oracle、IBM、Sybase鞥厂商之间的竞争关系。
-
SQL批量复制命令的六个陷阱
批量复制工具(BCP)是SQL Server主要的命令行工具之一,使用非常方便,它也是SQL Server导入导出海量数据的方式。
-
SAP利用大数据处理能力,延伸实时数据平台
SAP公司日前发布了针对Hadoop环境的高级支持与集成,其中包括“大数据”合作伙伴理事会和多个用户展示及基于SAP实时数据平台的相关“大数据”实施。
-
内存数据库巡礼之Oracle TimesTen
内存数据库可以是一个独立的数据库管理系统,如Oracle的TimesTen,或者从属于DBMS的一个特殊数据库,如SAP Sybase Adaptive Server Enterprise (ASE)。