//加列的标题
for(i=0;i<(int)col_num;i++)
m_listCtrl.InsertColumn(i,ColName[i],
LVCFMT_LEFT,60,0);
//加ListCtrl的内容
m_listCtrl.DeleteAllItems();
for(i=0;i<row;i++)
{
m_listCtrl.InsertItem(LVIF_TEXT|LVIF_STATE,
i, ColVal[i][0],
(i%2)==0 ? LVIS_SELECTED : 0, LVIS_SELECTED, 0, 0);
for(int j=1;j<(int)col_num;j++)
m_listCtrl.SetItemText(i,j, ColVal[i][j]);
}
这里也是处理一个不带条件的SELECT语句,但是与4.3有所不同.首先,这个SQL语句在运行前并不知道我们要查询的表的字段个数与字段的数据类型,因此用代码5.4.1中用OCIAttrGet()函数从语句句柄stmthp中获取SELECT语句中选择的字段的数目;其次,在这里我们不仅需要字段的数据,而且还需要字段的名称,因此,我们用代码5.4.2来获取字段名称。
6.简单查询-带有条件的DQL语句
为了演示带有条件的SELECT语句在OCI中的执行过程,我们设计一个简单的查询。
首先,我们加入一个Button控件(序号5), 并且通过MFC ClassWizard为此控件添加一个“BN_CLICKED”函数――OnButQuery(),用它来执行查询命令;第二,我们加入一个Combo box控件(序号6),利用ClassWizard为这个Combo box控件加入一个Cstring类型变量m_strFieldName,用它来选择所查询的字段名称;第三,我们加入一个Combo Box(序号7),利用ClassWizard为这个Combo Box控件加入一个Cstring型的变量m_strcondition, 用它来选择查询的条件,可以选择“>”, “<”, “=”(其中,这三个符号是我们在Combo Box的属性对话框的Data项里,手工输入的);第四,我们加入一个CEdit控件(序号8),并且加入Cstring类型的变量m_strFieldVal,用它来接收所查询字段的数值。
我们在5所加的函数OnBTableselectok()中加入代码6.1,用来把我们所选择的表的字段添加到用来选择查询字段名称的ComboBox控件上,
//代码6.1
//初始化选择所查询字段的ComboBox控件
for(i=0;i<(int)col_num;i++)
m_comboCtrl.AddString(ColName[i]);
//代码6.2,获取查询条件
UpdateData(TRUE);
CString FieldName=m_strFieldName;
CString strCondition=m_strcondition;
CString strFieldVal=m_strFieldVal;
//代码6.3,处理SQL的第一步,准备SQL语句
sword status; text textSQL[1024];
wsprintf((char*)textSQL,"SELECT * FROM
%s WHERE%s %s :CONDITION",
TableName,FieldName,strCondition);
if(status=OCIStmtPrepare(stmthp,errhp,
textSQL,strlen((char*)textSQL),
OCI_NTV_SYNTAX,OCI_DEFAULT ))
{ ErrorProc(errhp,status); return; }
//代码6.4,处理SQL的第二步,绑定(binding)
text *textConValp;//条件字段变量
sb4 len;
int strlength=strFieldVal.GetLength();
textConValp=new text[strlength+1];
wsprintf((char*)textConValp,"%s",
strFieldVal);
//使得条件字段变量为以空字符结尾的字符串
textConValp[strlength]='\0';
//条件字段的长度(字符个数)
len=strlen((const char *)textConValp)+1;
//由名称来绑定
if(status=OCIBindByName(stmthp, &bidhp[0],
errhp,(text*) ":CONDITION",-1,
(ub1 *) textConValp, len, SQLT_STR,
0,0,0,0,0,OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
//代码6.5,处理SQL的第三步,执行
if(status=OCIStmtExecute(svchp,stmthp,
errhp,(ub4)0,0,NULL,NULL,OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
//代码6.6,处理SQL的第四步,描述
ub4 col_num;//存放SELECT语句选中的列数
//读取选择列表中的项数
ErrorProc(errhp,OCIAttrGet(stmthp,
OCI_HTYPE_STMT,&col_num,0,
OCI_ATTR_PARAM_COUNT,errhp));
for(int i=0;i<(int)col_num;i++)
{
//为选择项分配参数描述符
ErrorProc(errhp,OCIParamGet(stmthp,
OCI_HTYPE_STMT,errhp,
(void **)&colhp,ub4(i+1)));
//读取选择项的数据长度
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&collen[i],0,
OCI_ATTR_DATA_SIZE,errhp));
//读取选择项的数据类型
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&coltype[i],
0,OCI_ATTR_DATA_TYPE,errhp));
//若这个字段为日期型,则把其字符宽度置为30
if(coltype[i]==SQLT_DAT) collen[i]=30;
//分配缓冲区
colbuf[i]=(text*)newtext[(int)collen[i]+1];
}
//代码6.7,处理SQL的第五步,定义变量
for( i=0;i<(int)col_num;i++)
{
if(status=OCIDefineByPos(stmthp,
&defhp[i],errhp,i+1,(ub1*)colbuf[i],
collen[i]+1,SQLT_STR,&ind[i],
0,0,OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
}
我们在程序中并没有调用OCIHandleAlloc()函数来分配定义句柄(OCIDefine),其实,定义句柄是由OCIDefineByPos()函数自动分配的。这样的句柄还有环境句柄(OCIEnv)与绑定句柄(OCIBind)。
//代码6.8,处理SQL的第六步,取值
int row=0;
while((OCIStmtFetch(stmthp,errhp,1,
OCI_FETCH_NEXT,OCI_DEFAULT))!=OCI_NO_DATA)
{
for(i=0;i<(int)col_num;i++)
{//把获取的用户的基表的数据
//赋予字段数值数组
ColVal[row][i]="";//清空字段值数组
if(ind[i]==-1)//对空值的处理
ColVal[row][i]="";
else
ColVal[row][i]=colbuf[i];
}
row=row+1;
}
//删除所分配的缓冲区
for(i=0;i<(int)col_num;i++)delete colbuf[i];
//代码6.9,刷新ListCtrl控件
m_listCtrl.DeleteAllItems();
for(i=0;i<row;i++)
{ m_listCtrl.InsertItem(
LVIF_TEXT|LVIF_STATE, i,
ColVal[i][0],
(i%2)==0?LVIS_SELECTED:0,
LVIS_SELECTED, 0, 0);
for(int j=1;j<(int)col_num;j++)
m_listCtrl.SetItemText(i,j, ColVal[i][j]);
}
delete textConValp;
这里要说明的是代码6.4,即OCI处理SQL语句的第二步:绑定(binding)。OCI有按照位置绑定函数:OCIBindByPos ()与按照名称绑定函数OCIBindByName()。 我们在程序中应用了后者。OCI的绑定中有一个概念叫Placeholder――占位符。对于在SQL语句中有输入数数据的SQL,为了实现交互式程序,我们在设计程序阶段并不能确定所要输入的数据,因此我们在这个阶段就用占位符来表示输入数据,例如在代码6.4我们用指针变量textConValp来表示输入数据(textConValp在SQL语句中的位置由":CONDITION"来表示)。而在程序运行阶段,则将占位符与程序变量地址结合,执行语句时,变量的数值就赋予这个SQL语句。从而实现交互式SQL。
带有输入的SQL语句的执行还有另外一种方式,即把输入数据以字符串的形式置于SQL语句中,比如:CString strSQL;
srtSQL.Format(“SELECT * FROM tablename WHERE fieldname=%s”,strInputVal)。这种方式仅仅需要两步:准备SQL与执行SQL,若是UPDATE或者INSERT则在执行成功后调用提交以便保持数据到数据库端。不过,我们需要判读输入数据的类型,比如数字型与字符型输入数据就有区别,若是字符型则需要在输入数据两边加单引号,熟悉SQL*PLUS的用户都清楚这一点。
7.删除记录――DELETE语句的实现
为实现这个功能我们在对话框中加入一个Cbutton控件(序号9),为它加一个“BN_CLICKED”函数――OnButDelete()来执行删除命令。我们在该函数中加入如下代码:
//代码
7.1,处理SQL的第一步,准备SQL语句
UpdateData(TRUE); sword status;
text textSQL[1024];
wsprintf((char*)textSQL,"DELETE FROM %s WHERE
%s%s :DelFieldVal”,
TableName,m_strFieldName,m_strcondition);
if(status=OCIStmtPrepare(stmthp,errhp,
textSQL,strlen((char*)textSQL),
OCI_NTV_SYNTAX,OCI_DEFAULT ))
{ ErrorProc(errhp,status); return; }
//代码
7.2,处理SQL的第二步,绑定(binding)
text textColVal[20];//条件字段的数值
sb4 len;
int strlength=m_strFieldVal.GetLength();
wsprintf((char*)textColVal,”%s”,
m_strFieldVal);
textColVal[strlength]=’\0’;
len=strlen((const char *)textColVal)+1;
//由名称来绑定
if(status=OCIBindByName(stmthp, &bidhp[0],
errhp, (text *) “:DelFieldVal”,-1,
(ub1 *) &textColVal, len, SQLT_STR,
0,(ub2 *)0,(ub2*)0,(ub4) 0,(ub4 *) 0, OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
//代码
7.3,处理SQL的第三步,执行
if(status=OCIStmtExecute(svchp,stmthp,
errhp,(ub4)1,0,NULL,NULL,OCI_DEFAULT))
{ErrorProc(errhp,status);return;}
//刷新ListCtrl控件
OnBTableselectok();
这个SQL语句的特点是:仅仅有输入数据而没有返回数据,因此我们处理这个SQL语句仅用来三步。
8.修改记录-UPDATE语句的实现
要实现这个功能,我们需要让程序知道我们想修改哪条记录的哪个字段以及这个字段的新值。因此,我们加入一些控件来输入这些信息。我们在类COCIExample的对话框中首先加入一个ComboBox控件(序号10),并给其加入Cstring类型的变量m_strConFName与Control类型的变量m_comConFNameCtrl;其次,加入一个CEdit控件(序号11),并且加入Cstring类型的变量m_strConFVal;用它们来确定所要修改的记录。其次,加入Cbutton控件(序号12),为它加一个“BN_CLICKED”函数――OnButUpdate()来执行修改命令。
首先,我们在函数OnButConnectdb()(在4.1中说明)中加入初始化刚才加入的ComboBox,代码如下:
//代码
8.1初始化修改字段时的ComboBox控件
for(I=0;I<(int)col_num;I++) m_comConFNameCtrl.AddString(ColName[I]);
我们在函数OnButUpdate()中加入如下代码:
//代码
8.2处理SQL的第一步,准备SQL语句
UpdateData(TRUE); sword status;
text textSQL[1024];
wsprintf((char*)textSQL,"UPDATE %s SET %s = :UpDFieldVal WHERE %s=:KeyFieldVal", TableName,m_strFieldName,m_strConFName);
if(status=OCIStmtPrepare(stmthp,errhp,
textSQL,strlen((cha*)textSQL),
OCI_NTV_SYNTAX,OCI_DEFAULT ))
{ ErrorProc(errhp,status);return;}
//代码
8.3处理SQL的第二步,绑定(binding)
text textColVal[20];//所修改字段的数值
sb4 len;
int strlength=m_strFieldVal.GetLength();
wsprintf((char*)textColVal,"%s",
m_strFieldVal);
textColVal[strlength]='\0';
len=strlen((const char *)textColVal)+1;
//由名称来绑定所要修改的字段的占位符
if(status=OCIBindByName(stmthp, &bidhp[0], errhp, (text*) ":UpDFieldVal",-1,
(ub1 *) &textColVal, len, SQLT_STR,
0, 0, 0, 0, 0, OCI_DEFAULT))
{ ErrorProc(errhp,status);return;}
//由名称来绑定作为修改时条件的字段的占位符
text textConColVal[20];
strlength=m_strConFVal.GetLength();
wsprintf((char*)textConColVal,"%s",
m_strConFVal);
textConColVal[strlength]='\0';
len=strlen((const char *)textConColVal)+1;
if(status=OCIBindByName(stmthp, &bidhp[1], errhp, (text *) ":KeyFieldVal",-1,
(ub1 *) &textConColVal, len, SQLT_STR,
0, 0, 0, 0, 0, OCI_DEFAULT))
{ErrorProc(errhp,status);return;}
//代码
8.4处理SQL的第三步,执行
if(status=OCIStmtExecute(svchp,stmthp,
errhp,(ub4)1,0,NULL,NULL,OCI_DEFAULT))
{ErrorProc(errhp,status);return;}
//提交
status=OCITransCommit(svchp, errhp, (ub4)0);
//刷新ListCtrl控件
OnBTableselectok();
需要说明的是UPDATE成功后,还需要调用函数OCITransCommit(),以便把所修改的数据保持到数据库中。
9.增加记录-INSERT语句
在OCI中处理INSERT语句所需的步骤与UPDATE和DELETE语句相同,即需要准备SQL语句、绑定(binding)、执行,若执行成功则调用OCITransCommit()提交(commit)来保持所增加的记录到数据库端。由于一方面,演示灵活的INSERT语句需要很多笔墨,另一方面,若你正学习OCI编程,那么,不妨把这作为一个练习(笔者在写这篇文章时,已经实现了对结构不同的表的增加记录)。
四、总结
通过上面的学习,我们不仅学习了OCI接口的优点、程序结构与处理SQL语句的步骤,而且学习了在VC++中OCI处理各种SQL语句的方法。
当然,这只是一个初步的了解,若想用OCI来开发项目,则需要进一步地学习。如果上面还有不明白的地方,或者想交流OCI编程的经验与技巧,那么请与我联系(Email:mister_6090@sina.com)。
参考文献:
racle Documentatin Library (Release 8.1.7)
Oracle Corporation
注:本文第三部分的实例在Visual V++6.0 & Oracle(8-1-6)环境下通过调试。
程序实例界
表4
序号 |
对象 |
ID |
Caption
(或者变量) |
1 |
Button |
IDC_BUT_CONNECTDB |
连接数据源 |
2 |
List Box |
IDC_LIST_TABLENAME |
|
3 |
Button |
IDC_B_TABLESELECTOK |
列举所选表内容 |
4 |
ListCtrl |
IDC_LIST_TABLEDATA |
m_listCtrl |
5 |
Button |
IDC_BUT_QUERY |
查询 |
6 |
Combo Box |
IDC_COBFNAME |
m_strFieldName |
7 |
Combo Box |
IDC_COMB_CONDITION |
m_strcondition |
8 |
CEdit |
IDC_EFFIELDVAL |
m_strFieldVal |
9 |
CButton |
IDC_BUT_DELETE |
删除记录 |
10 |
Combo Box |
IDC_CO_CONFNAME |
|
11 |
Combo Box |
IDC_CO_CONFVAL |
|
12 |
CButton |
IDC_BUT_UPDATE |
更新记录 |
|