如何快速导出SYBASE的存储过程徐茂红 2002-7-25 14:14:28 问题的提出 在一个应用系统中,如果编写了大量的Sybase存储过程,对存储过程进行系统、有效地备份是必须的。而我们通常用的办法是使用Sybase提供的Sybase Central工具先选定存储过程,然后通过鼠标右键选择Generate DDL的方式把存储过程备份下来。使用这种方式,不仅备份时间特别长(备份文件为6M左右时,达几个小时之久),更要命的是使用该方式备份下来的存储过程文本往往无法直接使用,因为在每个存储过程体后自动加的关键字go,有时就直接放在过程体最后一行尾部,没有空格,这样要使用该文本重新建过程时,需要花大量的时间进行修正。因此,找一种备份速度既快备份后内容又规整的方法来替代该工具是完全有必要的。 笔者在实际工作中,交叉使用以下两种方法,对存储过程进行备份。方法一:通过编写嵌入sql的C程序,实现整库存储过程的快速导出。方法二:通过defncopy快速导出指定的存储过程。现分别详细介绍如下: 方法一:通过cpre实现快速导出 以下程序通过sybase的cpre预编译处理生成可执行文件后,可以实现存储过程的快速导出,在数据库空闲时,可以在一分钟之内导出所有的存储过程(生成的文件有6M左右,而Sybase Central需要以小时计)。 该程序运行时格式如下(设可执行文件为exportproc): sybase> exprotproc 文件名 执行完后,所有的存储过程体就存放在该文件中,文件的内容也保存了存储过程的书写格式,并在每个存储过程后面加一个单独的行go。为了使程序灵活,在正式导之前根据提示需要输入用户名、口令、联机串、数据库名。 #include <stdio.h> #include <unistd.h> EXEC SQL INCLUDE SQLCA; void Sql_Error(); main( argc, argv) int argc; char **argv; { FILE *fp; char useName[11], usePasswd[11], dbString[16]; char *fpass; long oldId; EXEC SQL BEGIN DECLARE SECTION; char textLine[256]; long id; char dbName[20]; EXEC SQL END DECLARE SECTION; /*输入参数不够,给出使用提示*/ if (argc!=2){ printf("Usage As %s <fileName>/n", argv[0]); exit(0); } if( (fp=fopen(argv[1],"w+"))==NULL){ printf("Open file %s error/n",argv[1]); exit(0); } /*输入联库用户名*/ printf("Please Input user name:"); gets(useName); /*输入用户口令,在输入时屏幕不显示输入内容*/ fpass=getpass("Please Input Passwd:"); strcpy(usePasswd,fpass); /*输入联库字符串*/ printf("Please Input Database string:"); gets(dbString); /*输入要导出其中存储过程的库名*/ printf("Please Input Database Name:"); gets(dbName); EXEC SQL WHENEVER SQLERROR CALL Sql_Error(); if( ConnectDB(useName, usePasswd, dbString) !=0){ /*连接数据库*/ printf("Can't connect database/n"); exit(0); } else printf("Connect database ok!/n"); printf("Begin to export PROCEDURE Please wait.../n"); EXEC SQL use :dbName; EXEC SQL commit; /*声明游标,找出该库中所有的存储过程对应的内容*/ EXEC SQL declare pro_cur cursor for SELECT id, text from syscomments where id in (select id from sysobjects where type = 'P') and texttype=0 and text is not null order by id, number,colid2,colid; EXEC SQL OPEN pro_cur; EXEC SQL FETCH pro_cur into :id, :textLine; oldId=-9999L; while(!sqlca.sqlcode){ if( id!=oldId && oldId!=-9999L){ /*当一个存储过程结束后,在其过程体后加入新行GO*/ fprintf(fp,"/nGO/n/n"); } oldId=id; fprintf(fp,"%s",textLine); EXEC SQL FETCH pro_cur INTO :id, :textLine; } EXEC SQL CLOSE pro_cur; fprintf(fp,"/nGO/n/n"); fclose(fp); printf("End export PROCEDURE !/n"); /*断开于数据库的连接*/ DisConnectDB(); printf("Disconnect DataBase success!/n"); } void Sql_Error() { EXEC SQL WHENEVER SQLERROR CONTINUE; printf("Error./n"); printf("%s/n",sqlca.sqlerrm.sqlerrmc); exit(0); } /*连接数据库函数*/ int ConnectDB( username, password, dbstring) EXEC SQL BEGIN DECLARE SECTION; CS_CHAR *username,*password,*dbstring; EXEC SQL END DECLARE SECTION; { EXEC SQL SET CHAINED OFF; EXEC SQL CONNECT :username IDENTIFIED BY :password USING :dbstring; return(sqlca.sqlcode); } /*断开数据库函数*/ int DisConnectDB() { EXEC SQL DISCONNECT ALL; return 0; } 该程序在IBM AIX4.3、hp-unix 11.0、tru64 unix5.0平台上测试通过。 方法二:通过defncopy实现快速导出 在需要对一些存储过程单独进行备份时,往往使用defncopy通过拼串的方式进行。具体步骤如下: 1.根据需要备份的存储过程,先编写此crtprocout.sql文件,假设导出所有以PR_JF开头的存储过程,内容如下: select "defncopy -U用户名 -P口令 -S联机串名 out "+name+".sql 库名 "+name from sysobjects where type='P' and name like "PR_JF%" 2.利用上述文件,生成导过程脚本 isql -U用户名 -P口令 -S联机串名 –I crtprocout.sql –o procout 3.改变文件procout的权限 chmod +x procout 4.执行脚本导出过程,每个过程脚本名为:过程名+后缀”.sql” ./procout 小结:以上两种办法通过修改sql语句里的where条件相互之间是可以代替的。但笔者认为,前者适合对整库的过程进行备份,而后者适合对指定的几个过程进行备份。因为前者会对所有的过程脚本生成到一个文件里,适合面向多个过程的管理和备份;而后者一个过程脚本生成一个文件,适合面向单个过程的管理和备份。以上两种方法通过简单修改也可进行触发器等的导出。