PROC++批量导入导出ORACLE数据库表

最近在开发一个项目中,为了解决数据库IO瓶颈,不得不把数据库中的数据导出为文本文件。文本传到客户端后又要导入到数据库。本人用C++ Builder 嵌入 PROC++ 写了一个导入导出的 DLL 。如果对你有用深感荣幸!详细内容如下:

一、准备工作

计算机环境: Win 2000 PRO,ORACLE 9i,C++ Builder 5.5

引入必要的 ORACLE 内部函数:要用的函数在 $(ORACEL_HOME)\bin\sqlora9.dll 链接库中。为了能在 C++ Builder 中使用,先得生成 LIB : implib sqlora9.lib sqlora9.dll

二、源文件分析

//-------------------------------------------------------------------------

// 加入必要的头文件

#include

  1<vcl.h> #include<windows.h> #include<stdio.h> #include<stdlib.h> #include<string.h>
  2
  3#include<time.h> #include<math.h> #include<fcntl.h> #include<io.h> #include<sys\stat.h>
  4
  5//  说明  DLL  的输出函数 
  6
  7extern "C" _declspec(dllexport) int _stdcall ConnectDB(const char *Username, 
  8
  9const char *Password, const char *Dbname); 
 10
 11extern "C" _declspec(dllexport) int _stdcall ImportTxtfile(TList *LengthArray, 
 12
 13String *FieldArray, const char *TableName, 
 14
 15const char *FileName); 
 16
 17extern "C" _declspec(dllexport) int _stdcall ExportTxtfile(const char *Sql, 
 18
 19const char *FileName); 
 20
 21#pragma hdrstop 
 22
 23//---------------------------------------------------------------------------- 
 24
 25#define MAX_ITEMS 20  //  定义最大字段数 
 26
 27#define MAX_VNAME_LEN 30  //  定义选择表项最大长度 
 28
 29#define MAX_INAME_LEN 30  //  定义指示器变量名字的最大长度 
 30
 31EXEC SQL INCLUDE sqlca;  //  说明  SQL  通讯区 
 32
 33EXEC SQL INCLUDE oraca;  //  说明  ORACLE  通讯区 
 34
 35EXEC SQL INCLUDE sqlda;  //  说明  SQL  语句描述结构  /*SQLDA  结构体请查相关资料  */ 
 36
 37EXEC ORACLE OPTION (ORACA = YES); 
 38
 39EXEC ORACLE OPTION (RELEASE_CURSOR = YES); 
 40
 41//  说明  ORACLE  外部函数 
 42
 43extern "C" _declspec(dllimport) void _stdcall sqlclu(SQLDA*); 
 44
 45extern "C" _declspec(dllimport) void _stdcall sqlnul(short*, short*, int*); 
 46
 47extern "C" _declspec(dllimport) void _stdcall sqlprc(int*, int*, int*); 
 48
 49extern "C" _declspec(dllimport) struct SQLDA * _stdcall sqlald(int, unsigned int, unsigned int); 
 50
 51SQLDA *SelectUnit;  //  定义选择项描述 
 52
 53SQLDA *BindUnit;  //  定义输入项空间 
 54
 55//  定义变量,以存放连接数据库的参数 
 56
 57EXEC SQL BEGIN DECLARE SECTION; 
 58
 59char User[20];//  用户名 
 60
 61char Pwd[20];//  密码 
 62
 63char DB[20];//  数据库服务名 
 64
 65EXEC SQL END DECLARE SECTION; 
 66
 67bool bConnect = false;//  是否连接标志 
 68
 69#pragma hdrstop 
 70
 71#pragma argsused 
 72
 73//C++ Builder DLL  的主函数 
 74
 75BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved) 
 76
 77{ 
 78
 79return 1; 
 80
 81} 
 82
 83/*--------------------------------------------------------------------------- 
 84
 85连接数据库 
 86
 87\---------------------------------------------------------------------------*/ 
 88
 89int _stdcall ConnectDB(const char *Username, const char *Password, 
 90
 91const char *Dbname) 
 92
 93{ 
 94
 95strcpy(User, Username); 
 96
 97strcpy(Pwd, Password); 
 98
 99strcpy(DB, Dbname); 
100
101EXEC SQL CONNECT :User IDENTIFIED BY :Pwd USING :DB; 
102
103if (sqlca.sqlcode &lt; 0) 
104
105return -1; 
106
107bConnect = true; 
108
109return 0; 
110
111} 
112
113/*--------------------------------------------------------------------------- 
114
115导出文本函数 
116
117因为不确定  SELECT  语句的表及字段,所以我使用动态语句  (ORACLE DYNAMIC SQL)  的  //  第四种方式。动态  SQL  方法四是在不确定  SQL  语句的选择项与输入项,且不知个数与数据类型的情况下使用的一种复杂程序设计技术。 
118
119\---------------------------------------------------------------------------*/ 
120
121int _stdcall ExportTxtfile(const char *Sql/*SQL  选择语句  */, const char **FileName/** *  导出目标文本文件名  */) 
122
123{ 
124
125int null_ok, precision, scale; 
126
127int handle; 
128
129if ((handle = open(FileName, O_CREAT|O_TEXT|O_APPEND|O_RDWR, S_IREAD|S_IWRITE)) == -1) 
130
131{ 
132
133//  文件打开出错 
134
135return -1; 
136
137} 
138
139//  定义变量,以存放  SQL  语句 
140
141EXEC SQL BEGIN DECLARE SECTION; 
142
143char sqlstr[256]; 
144
145EXEC SQL END DECLARE SECTION; 
146
147//  检查是否连接数据库 
148
149if (bConnect == false) return -2; 
150
151strcpy(sqlstr/*.arr*/, Sql); 
152
153//  sqlstr.len = strlen(sql); 
154
155//  给描述区分配空间 
156
157if ((SelectUnit = sqlald(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN)) == (SQLDA *)NULL) 
158
159{ 
160
161//  空间分配失败 
162
163return -3; 
164
165} 
166
167if ((BindUnit = sqlald(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN)) == (SQLDA *)NULL) 
168
169{ 
170
171//  空间分配失败 
172
173return -3; 
174
175} 
176
177//  给查询返回值存储区分配空间 
178
179SelectUnit-&gt;N = MAX_ITEMS; 
180
181for (int i=0; i &lt; MAX_ITEMS; i++) 
182
183{ 
184
185BindUnit-&gt;I[i] = (short *)malloc(sizeof(short *)); 
186
187BindUnit-&gt;V[i] = (char *)malloc(MAX_VNAME_LEN); 
188
189} 
190
191for (int i=0; i &lt; MAX_ITEMS; i++) 
192
193{ 
194
195SelectUnit-&gt;I[i] = (short *)malloc(sizeof(short *)); 
196
197SelectUnit-&gt;V[i] = (char *)malloc(MAX_VNAME_LEN); 
198
199} 
200
201EXEC SQL WHENEVER SQLERROR GOTO sqlerr;//DO sql_error("  导出出错  "); 
202
203//  设置SQL语句 
204
205EXEC SQL PREPARE SQLSA FROM :sqlstr; 
206
207EXEC SQL DECLARE Cursorbase CURSOR FOR SQLSA; 
208
209//  输入描述处理 
210
211BindUnit-&gt;N = MAX_ITEMS; 
212
213EXEC SQL DESCRIBE BIND VARIABLES for SQLSA INTO BindUnit; 
214
215if (BindUnit-&gt;F &lt; 0) 
216
217{ 
218
219return -4; 
220
221//  输入项过多 
222
223} 
224
225BindUnit-&gt;N = BindUnit-&gt;F; 
226
227//  打开光标 
228
229EXEC SQL OPEN Cursorbase USING DESCRIPTOR BindUnit; 
230
231//  选择项处理 
232
233EXEC SQL DESCRIBE SELECT LIST for SQLSA INTO SelectUnit; 
234
235if (SelectUnit-&gt;F &lt; 0) 
236
237{ 
238
239return -4; 
240
241//  选择表项过多 
242
243} 
244
245SelectUnit-&gt;N = SelectUnit-&gt;F; 
246
247//  因为所有格式,类型都是不确定的,所以要得到正确的返回值就要处理格式 
248
249for (int i=0; i &lt; SelectUnit-&gt;F; i++) 
250
251{ 
252
253sqlnul(&amp;(SelectUnit-&gt;T[i]), &amp;(SelectUnit-&gt;T[i]), &amp;null_ok); 
254
255switch (SelectUnit-&gt;T[i]) 
256
257{ 
258
259case 1://CHAR 
260
261break; 
262
263case 2://NUMBER 
264
265sqlprc(&amp;(SelectUnit-&gt;L[i]), &amp;precision, &amp;scale); 
266
267if (precision == 0) 
268
269precision = 40; 
270
271SelectUnit-&gt;L[i] = precision + 2; 
272
273break; 
274
275case 8://LONG 
276
277SelectUnit-&gt;L[i] = 240; 
278
279break; 
280
281case 11://ROWID 
282
283SelectUnit-&gt;L[i] = 18; 
284
285break; 
286
287case 12://DATE 
288
289SelectUnit-&gt;L[i] = 9; 
290
291break; 
292
293case 23://RAW 
294
295break; 
296
297case 24://LONGRAW 
298
299SelectUnit-&gt;L[i] = 240; 
300
301break; 
302
303} 
304
305SelectUnit-&gt;V[i] = (char *)realloc(SelectUnit-&gt;V[i], SelectUnit-&gt;L[i]+1); 
306
307SelectUnit-&gt;T[i] = 1;//  把所有类型转换为字符型 
308
309} 
310
311EXEC SQL WHENEVER NOT FOUND goto EndFor; 
312
313for (;;) 
314
315{ 
316
317EXEC SQL FETCH Cursorbase USING DESCRIPTOR SelectUnit; 
318
319//  输出各字段 
320
321for (int i=0; i &lt; SelectUnit-&gt;F; i++) 
322
323{ 
324
325char buffer[256]; 
326
327if (i != SelectUnit-&gt;F-1) 
328
329sprintf(buffer, "%s", SelectUnit-&gt;V[i]); 
330
331else sprintf(buffer, "%s\r\n", SelectUnit-&gt;V[i]); 
332
333int length = strlen(buffer); 
334
335if (write(handle, buffer, length) != length) 
336
337{ 
338
339return -5; 
340
341//  写文件失败  exit(1); 
342
343} 
344
345} 
346
347} 
348
349EndFor: 
350
351close(handle); 
352
353for (int i=0; i &lt; MAX_ITEMS; i++) 
354
355{ 
356
357if (SelectUnit-&gt;V[i] != (char *)NULL) 
358
359free(SelectUnit-&gt;V[i]); 
360
361free(SelectUnit-&gt;I[i]); 
362
363} 
364
365for (int j=0; j &lt; MAX_ITEMS; j++) 
366
367{ 
368
369if (BindUnit-&gt;V[j] != (char *)NULL) 
370
371free(BindUnit-&gt;V[j]); 
372
373free(BindUnit-&gt;I[j]); 
374
375} 
376
377sqlclu(SelectUnit); 
378
379sqlclu(BindUnit); 
380
381EXEC SQL CLOSE Cursorbase; 
382
383return 0; 
384
385sqlerr: 
386
387return -6; 
388
389} 
390
391/*---------------------------------------------------------------------------- 
392
393导入文本 
394
395为了批量导入,在此我调用的  sqlldr  工具 
396
397首先生成  SQL*Loader  控制文件,后运行  sqlldr 
398
399\----------------------------------------------------------------------------*/ 
400
401int _stdcall ImportTxtfile(TList **LengthArray/** *  导入文本的字段长度链表  */, 
402
403String *FieldArray/*  数据库表的了段名数组  */, const char **TableName/*** 导入的目标表  */, const char **FileName/** *  导入的源文本文件  */) 
404
405{ 
406
407//  产生  SQL*Loader  控制文件 
408
409FILE *fout, *fp; 
410
411char Execommand[256]; 
412
413char sqlload[] = ".\\\sqlload.ctl"; 
414
415//  检查是否连接数据库 
416
417if (bConnect == false) return -2; 
418
419if ((fout=fopen(sqlload, "w")) == NULL) 
420
421{ 
422
423//  建立控制文件出错 
424
425return -1 ; 
426
427} 
428
429fprintf(fout, "LOAD DATA\n"); 
430
431fprintf(fout, "INFILE '%s'\n", FileName); 
432
433fprintf(fout, "APPEND INTO TABLE %s (\n", TableName); 
434
435int iStart = 1; 
436
437for(int i=0; i &lt; LengthArray-&gt;Count; i++) 
438
439{ 
440
441fprintf(fout, "%11s POSITION(%d:%d)", FieldArray[i], iStart, *(int*)LengthArray-&gt;Items[i]+iStart-1); 
442
443iStart += *(int*)LengthArray-&gt;Items[i]; 
444
445fprintf(fout, " CHAR"); 
446
447if(i &lt; LengthArray-&gt;Count-1) 
448
449fprintf(fout, ",\n"); 
450
451} 
452
453fprintf(fout, ")\n"); 
454
455fclose(fout); 
456
457sprintf(Execommand, "sqlldr.exe userid=%s/%s@%s control=%s", 
458
459User, Pwd, DB, sqlload); 
460
461if (system(Execommand) == -1) 
462
463{ 
464
465//SQL*Loader  执行错误 
466
467return -1; 
468
469} 
470
471return 0 ; 
472
473} 
474
475//---------------------------------------------------------------------------- 
476
477**三、编译**
478
479用  ORACLE  的  PROC  预编译器预编后,放入  C++ Builder  中联编。联编时需加入前面生成的  sqlora9.lib  。联编时还要注意,所有  PROC  生成的  ORACLE  内部函数调用都要说明为  extern "C" _declspec(dllexport) TYPE _stdcall  类型。 
480
481水平有限还请见谅!!!请多多指点。QQ:  5005647</sys\stat.h></io.h></fcntl.h></math.h></time.h></string.h></stdlib.h></stdio.h></windows.h></vcl.h>
Published At
Categories with 数据库类
comments powered by Disqus