SQL Server是现阶段应用程序最普遍采用的数据库技术,在数据库中对图像、视频和声音等数据的存取需要越来越频繁。普通数据可直接在用户定义的字段上存取,而这类被称为二进制大对象(Binary Large Object)的大数据块由于其数据量太大,不是作为数据记录的一部分被存储在数据记录页中,而是存储在其所属的一组专用页中,数据表的对应字段中仅是一个十六进制的指针,该指针指向存放该记录的页面。由于SQL语言是一种面向集合运算的描述语言,其本身不具有过程性结构,使用SQL Server中的Image类型字段存放图像文件时,Image类型字段是只存储位串,SQL Server不对它进行编译,而是由应用程序完成解释。又因Image类型的字段内容在SQL Server 2000中不能使用INSERT和UPDATA等语句进行插入和更新,所以需编写专门的过程来处理图像字段。尽管在SQL Server 2005可以使用有关的函数解决这一问题,但由于实际的应用系统是非常复杂的,数据库访问只是其中一个部分,有些用户交互、图形化输入输出数据只能用高级语言实现。利用高级语言的过程性结构来弥补SQL语言实现复杂应用方面的不足, 将SQL语言嵌入到C语言的嵌入式编程将有广泛的应用前景。
1 嵌入式SQL及语法要素
1.1 C语言嵌入式语句[1]
SQL语言可以嵌入到C语言中使用,数据库管理系统一般采用的处理方法是预处理方法。预处理方法就是从含有主程序C语言和SQL语句的程序开始的。首先,把程序提交给一个专门用于C语言和SQL实现方式的预编译器,预编译器从C语言代码中剥离SQL语句,把SQL语句转换成C语言调用语句,以使用C语言编译程序,并将整个编译成目标代码,链接到各数据库并形成可执行文件。在C语言中嵌入SQL语句产生应用程序的过程为[2]:编辑C源代码+嵌入式SQL→SQL预编译器→C编译器→链接程序(DLL导入库)→应用程序(数据库)。
1.2 嵌入式SQL的语法要素
(1)创建嵌入式语句。将SQL嵌入到C语言中混合编程,程序中会含有两种不同计算模型的语句。为了区别SQL语句与C语句,须在所有的SQL语句前加前缀EXEC SQL,且以分号结束,一般形式为“EXEC SQL ”。嵌入式语句分为可执行语句和说明性语句,可执行语句用来完成在交互式环境下的SQL语句中的数据定义、数据操纵和数据控制任务,说明性语句用于声明通信区和SQL变量等。
(2)SQL通信区。SQL Server数据库管理系统提供了一个通信区SQLCA,用于存储SQL语句运行时DBMS反馈给应用程序的状态,这些信息主要描述系统当前工作状态以及运行环境等。应用程序从SQLCA中取出这些状态信息,以决定接下来执行的语句。在C语言中定义为SQL通信区的语句为:EXEC SQL INCLUDE SQLCA。
(3)主变量。嵌入式SQL语句中可以使用C语言的程序变量及主变量来输入或输出数据。一个主变量可以附带一个任选的指示变量用来指示返回给主变量的值是否为NULL值以及返回给主变量的字符串是否发生了截断。输入主变量出现于SQL语句中时,可在前面加冒号(:)以区别表字段名。而定义输出变量用指示变量的方法,是在SQL语句EXEC SQL BEGIN DECLARE SECTION与EXEC SQL END DECLARE SECTION之间进行说明。
(4)链接数据库。C程序的主函数中应包含一条登录语句,向预编译和程序提供用户名和口令,以实现与SQL Server数据库管理系统建立链接。其命令语法为:EXEC SQL CONNECT TO 服务器名.数据库名AS链接名 USER 用户名.口令。
2 SQL Server中图像数据处理技术
2.1 SQL Server 2000图像存储方法
SQL Server 2000中,对于小于8 000 B的图像数据可以使用二进制数据类型(binary、varbinary)表示,但通常要保存的图像都大于8 000 B,对于这类大对像数据,系统提供了Image数据类型。Image数据类型不同于其他数据类型,该字段内容不能使用标准INSERT和UPDATE等语句进行插入和更新,因此用C语言程序[3]来编写处理图像文件,需要先将图像文件等数据转换成十六进制后存储,再将Image列中的数据存储为位串。SQL Server不能对它进行解释,Image列数据的解释必须由应用程序完成,这与普通的数据存储方式是不同的。Image列所做的全部工作就是提供一个位置用来存储组成图像数据值的位流,这需用到SQL Server中的TEXTPTR、WRITETEXT和UPDATETEXT等函数进行图形的添加和修改。以下是一个C语言与SQL Server 2000的嵌入式编程及图像处理的实例,程序给出的是解决该问题的核心部分。
Demo1.sqc
#include
#include
#define Buffer_Size 1024
EXEC SQL INCLUDE USERCA;
//filein为.jpg文件,fileout为.txt文件
void imagetofield(char *filein,char * fileout)
{
EXEC SQL BEGIN DECLARE SECTION;
FILE *fin,*fout; int j, Len;
unsigned char buffer1[Buffer_Size+1];
unsigned char buffer2[Buffer_Size*3+1];
unsigned char *Temp;
EXEC SQL END DECLARE SECTION;
if(!(fin=fopen(filein, "rb ")))
return;
if(!(fout=fopen(fileout", "w")))
{fclose(fin);return;}
Len=fread(buffer1, sizeof(char), Buffer_Size, fin);
Temp=buffer2;
while(Len==Buffer_Size)
{ for(j=0;j
{ fprintf(fout,"%X",buffer1[j]);
Temp+= 3; };
//这样就把二进制的数据转换成为16进制的字符串,
并且存储到buffer1当中了
fwrite(buffer1, sizeof(char), nLen*3, fout);
Len=fread(buffer1, sizeof(char), Buffer_Size, fin); }
fclose(fin);
fclose(fout);
……
EXEC SQL CONNECT Serverl:mydb USER sa.sa;
EXEC SQL CREATE TEALE(sno char(9), sname char
(20), resume nvarchar(max),photo image);
//表中插入一条记录,并初始化photo字段
EXEC SQL INSERT INTO teacher VALUES (’1001’,
’Arlen’,’’,0xfffffff)
//往表中写入图像信息
EXEC SQL DECLARE @@tmp_data varbinary(16)
EXEC SQL SELECT @@tmp_data = TEXTPTR(photo)
FROM teacher WHERE sno= '1001 '
EXEC SQL WRITETEXT teacher.photo @@tmp_data:*fileout
//:*fileout图像文件转为十六进制文件指针
……
EXEC SQL COMMIT ;
EXEC SQL DISCONNECT ALL;}
2.2 SQL Server 2005图像存储实现方法[4]
SQL Server 2005 的 OPENROWSET 函数能够一次完成远程数据的连接与存取。可以在 SELECT 语句的 FROM 子句中使用 OPENROWSET 函数,也可以在 INSERT、UPDATE 或 DELETE 语句中把 OPENROWSET 函数当成目标数据表来使用。值得一提的是,SQL Server 2005在OPENROWSET函数中加入了BULK数据列集, 这样可以直接从文字文件、xml 文件以及图像等文件中读取数据。这就意味着可以在一条非常简单的 INSERT INTO…SELECT 语句中使用OPENROWSET函数将内含大量数据的文件内容存入数据表的对应字段(如简历、图像等)中。OPENROWSET BULK选项自变量可以精确控制数据读取的开始与结束位置、如何处理错误以及数据的解释,例如将数据文件读取成 varbinary、varchar 或 nvarchar 类型的字段的数据列集。以下是一个C语言与SQL Server 2005的嵌入式编程及图像处理的实例,程序给出的是解决这一问题的核心部分。
Demo2.sqc
#include
#include
EXEC SQL BEGIN DECLARE SECTION;
char sno[10],sname[10];int ssex;
EXEC SQL END DECLARE SECTION ;
EXEC SQL INCLUDE USERCA;
viod imagetech ()
{ EXEC SQL CONNECT TO Server:mydb USER sa.sa;
EXEC SQL CREATE TABLE Teacher
(sno char(9), sname char(20), ssex int,
resume nvarchar(max),
//创建可输入大数据块文本的简历字段
photo image);
printf( "input sno: "); scanf( "%s",sno);
printf( "input sname: "); scanf( "%s",sname);
printf( "input?ssex: "); scanf( "%d",&ssex);
EXEC SQL INSERT INTO Teacher(sno,sname,ssex,resume,
photo)
SELECT :sno, :sname, :ssex,
WSET(BULK ’C:resume1.txt’, SINGLE_NCLOB),
OPENROWSET(BULK ’C:photo1.jpg’, SINGLE_BLOB);
//一种大数据块及图像存储的解决方案
EXEC SQL COMMIT TRANSACTION;
EXEC SQL DISCONNECT ALL;
}
2.3 预编译
C语言编译程序不能识别应用程序中的SQL语句,需要经过预处理程序将其转换成C语句。经过嵌入式SQL的预编译之后,原有的嵌入式SQL会被转换成一系列函数调用。因此,系统还提供一些列函数库,以确保能够把代码中的函数调用与对应的实现链接起来。SQL Server的预处理程序是nsqlprep.exe。通过在操作系统命令窗口运行命令cd c:program filesMicrosoft SQL ServerMSSQLBinn,进入到Binn文件夹,调用预编译程序nsqlprep.exe程序,执行nsqlprep Demo2,如果成功就会将Demo2.sqc文件预编译成Demo2.c。接着运行主语言编译程序VC++,打开Demo2.c进行编译,VC++会提示要建立工程,点击“是”建立工程文件。还需在工程/设置/link中的对象/库模块中添加SQLakw32.lib和Caw32.lib才能够正确链接,最后生成目标程序和可执行文件。
当一个程序既要访问数据库,又要处理数据时,把SQL语言嵌入到宿主语言中,将SQL语言访问数据库的功能和宿主语言的数据处理功能相结合是解决该问题的有效途径。图像处理作为一种信息表达手段已被人们所熟悉,在数据库信息管理系统的开发中[5]需要用到图像等数据时,将有关信息与指定的图像对应,无疑对图像数据处理的科研、生产和管理等部门有着重要的实用参考价值。因此,在嵌入式SQL中用数据库管理系统的预编译器技术,无限利用高级语言通过数据库管理系统的接口存取和检索数据,来提高对数据库操作的效率提高。