最近在开发一个项目中,为了解决数据库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 < 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->N = MAX_ITEMS;
180
181for (int i=0; i < MAX_ITEMS; i++)
182
183{
184
185BindUnit->I[i] = (short *)malloc(sizeof(short *));
186
187BindUnit->V[i] = (char *)malloc(MAX_VNAME_LEN);
188
189}
190
191for (int i=0; i < MAX_ITEMS; i++)
192
193{
194
195SelectUnit->I[i] = (short *)malloc(sizeof(short *));
196
197SelectUnit->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->N = MAX_ITEMS;
212
213EXEC SQL DESCRIBE BIND VARIABLES for SQLSA INTO BindUnit;
214
215if (BindUnit->F < 0)
216
217{
218
219return -4;
220
221// 输入项过多
222
223}
224
225BindUnit->N = BindUnit->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->F < 0)
236
237{
238
239return -4;
240
241// 选择表项过多
242
243}
244
245SelectUnit->N = SelectUnit->F;
246
247// 因为所有格式,类型都是不确定的,所以要得到正确的返回值就要处理格式
248
249for (int i=0; i < SelectUnit->F; i++)
250
251{
252
253sqlnul(&(SelectUnit->T[i]), &(SelectUnit->T[i]), &null_ok);
254
255switch (SelectUnit->T[i])
256
257{
258
259case 1://CHAR
260
261break;
262
263case 2://NUMBER
264
265sqlprc(&(SelectUnit->L[i]), &precision, &scale);
266
267if (precision == 0)
268
269precision = 40;
270
271SelectUnit->L[i] = precision + 2;
272
273break;
274
275case 8://LONG
276
277SelectUnit->L[i] = 240;
278
279break;
280
281case 11://ROWID
282
283SelectUnit->L[i] = 18;
284
285break;
286
287case 12://DATE
288
289SelectUnit->L[i] = 9;
290
291break;
292
293case 23://RAW
294
295break;
296
297case 24://LONGRAW
298
299SelectUnit->L[i] = 240;
300
301break;
302
303}
304
305SelectUnit->V[i] = (char *)realloc(SelectUnit->V[i], SelectUnit->L[i]+1);
306
307SelectUnit->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 < SelectUnit->F; i++)
322
323{
324
325char buffer[256];
326
327if (i != SelectUnit->F-1)
328
329sprintf(buffer, "%s", SelectUnit->V[i]);
330
331else sprintf(buffer, "%s\r\n", SelectUnit->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 < MAX_ITEMS; i++)
354
355{
356
357if (SelectUnit->V[i] != (char *)NULL)
358
359free(SelectUnit->V[i]);
360
361free(SelectUnit->I[i]);
362
363}
364
365for (int j=0; j < MAX_ITEMS; j++)
366
367{
368
369if (BindUnit->V[j] != (char *)NULL)
370
371free(BindUnit->V[j]);
372
373free(BindUnit->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 < LengthArray->Count; i++)
438
439{
440
441fprintf(fout, "%11s POSITION(%d:%d)", FieldArray[i], iStart, *(int*)LengthArray->Items[i]+iStart-1);
442
443iStart += *(int*)LengthArray->Items[i];
444
445fprintf(fout, " CHAR");
446
447if(i < LengthArray->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>