MSSQL网页编程之Visual C++ ADO数据库编程进门(下)
但我们知道,若使用statement,并没有上述需要的数据。试想binlog中记录了一句updatetsetf1=3whereid=3。怎么恢复呢?ado|c++|visual|编程|数据|数据库10、邦定命据界说一个绑定类,将其成员变量绑定到一个指定的纪录集,以便利于会见纪录集的字段值。
(1).从CADORecordBinding派生出一个类:
classCCustomRs:publicCADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(3,adVarChar,m_szau_fname,
sizeof(m_szau_fname),lau_fnameStatus,false)
ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_szau_lname,
sizeof(m_szau_lname),lau_lnameStatus,false)
ADO_VARIABLE_LENGTH_ENTRY2(4,adVarChar,m_szphone,
sizeof(m_szphone),lphoneStatus,true)
END_ADO_BINDING()
public:
CHARm_szau_fname;
ULONGlau_fnameStatus;
CHARm_szau_lname;
ULONGlau_lnameStatus;
CHARm_szphone;
ULONGlphoneStatus;
};
个中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏联系关系起来。每一个字段对应于两个变量,一个寄存字段的值,另外一个寄存字段的形态。字段用从1入手下手的序号暗示,如1,2,3等等。
出格要注重的是:假如要绑定的字段是字符串范例,则对应的字符数组的元素个数必定要比字段长度年夜2(好比m_szau_fname,其绑定的字段au_fname的长度实践是20),不如许绑定就会失利。我剖析多出的2多是为了寄存字符串开头的空字符null和BSTR字符串开首的一个字(暗示BSTR的长度)。这个成绩关于初学者来讲多是一个意想不到的成绩。
CADORecordBinding类的界说在icrsint.h文件里,内容是:
classCADORecordBinding
{
public:
STDMETHOD_(constADO_BINDING_ENTRY*,GetADOBindingEntries)(VOID)PURE;
};
BEGIN_ADO_BINDING宏的界说也在icrsint.h文件里,内容是:
#defineBEGIN_ADO_BINDING(cls)public:
typedefclsADORowClass;
constADO_BINDING_ENTRY*STDMETHODCALLTYPEGetADOBindingEntries(){
staticconstADO_BINDING_ENTRYrgADOBindingEntries[]={
ADO_VARIABLE_LENGTH_ENTRY2宏的界说也在icrsint.h文件里:
#defineADO_VARIABLE_LENGTH_ENTRY2(Ordinal,DataType,Buffer,Size,Status,Modify)
{Ordinal,
DataType,
0,
0,
Size,
offsetof(ADORowClass,Buffer),
offsetof(ADORowClass,Status),
0,
classoffset(CADORecordBinding,ADORowClass),
Modify},
#defineEND_ADO_BINDING宏的界说也在icrsint.h文件里:
#defineEND_ADO_BINDING(){0,adEmpty,0,0,0,0,0,0,0,FALSE}};
returnrgADOBindingEntries;}
(2).绑定
_RecordsetPtrRs1;
IADORecordBinding*picRs=NULL;
CCustomRsrs;
......
Rs1->QueryInterface(__uuidof(IADORecordBinding),
(LPVOID*)&picRs));
picRs->BindToRecordset(&rs);
派生出的类必需经由过程IADORecordBinding接谈锋能绑定,挪用它的BindToRecordset办法就好了。
(3).rs中的变量便是以后纪录字段的值
//Setsortandfiltercondition:
//Step4:Manipulatethedata
Rs1->Fields->GetItem("au_lname")->Properties->GetItem("Optimize")->Value=true;
Rs1->Sort="au_lnameASC";
Rs1->Filter="phoneLIKE4155*";
Rs1->MoveFirst();
while(VARIANT_FALSE==Rs1->EndOfFile)
{
printf("Name:%s %s Phone:%s
",
(rs.lau_fnameStatus==adFldOK?rs.m_szau_fname:""),
(rs.lau_lnameStatus==adFldOK?rs.m_szau_lname:""),
(rs.lphoneStatus==adFldOK?rs.m_szphone:""));
if(rs.lphoneStatus==adFldOK)
strcpy(rs.m_szphone,"777");
TESTHR(picRs->Update(&rs));//Addchangetothebatch
Rs1->MoveNext();
}
Rs1->Filter=(long)adFilterNone;
......
if(picRs)picRs->Release();
Rs1->Close();
pConn->Close();
只需字段的形态是adFldOK,就能够会见。假如修正了字段,不要忘了先挪用picRs的Update(注重不是Recordset的Update),然后才封闭,也不要忘了开释picRs(即picRs->Release();)。
(4).此时还能够用IADORecordBinding接口增加新记录
if(FAILED(picRs->AddNew(&rs)))
......
11.会见长数据
在MicrosoftSQL中的长数据包含text、image等如许长范例的数据,作为二进制字节来看待。
能够用Field工具的GetChunk和AppendChunk办法来会见。每次能够读出或写进全体数据的一部分,它会记着前次会见的地位。可是假如两头会见了其余字段后,就又得重新来了。
请看上面的例子:
//写进一张照片到数据库:
VARIANTvarChunk;
SAFEARRAY*psa;
SAFEARRAYBOUNDrgsabound;
//VT_ARRAY│VT_UI1
CFilef("h:aaa.jpg",Cfile::modeRead);
BYTEbVal;
UINTuIsRead=0;
//CreateasafearraytostorethearrayofBYTES
while(1)
{
uIsRead=f.Read(bVal,ChunkSize);
if(uIsRead==0)break;
rgsabound.cElements=uIsRead;
rgsabound.lLbound=0;
psa=SafeArrayCreate(VT_UI1,1,rgsabound);
for(longindex=0;index<uIsRead;index++)
{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal)))
::MessageBox(NULL,"啊,又出偏差了。","提醒",MB_OK│MB_ICONWARNING);
}
varChunk.vt=VT_ARRAY│VT_UI1;
varChunk.parray=psa;
try{
m_pRecordset->Fields->GetItem("photo")->AppendChunk(varChunk);
}
catch(_com_error&e)
{
CStringstr=(char*)e.Description();
::MessageBox(NULL,str+"
又出偏差了。","提醒",MB_OK│MB_ICONWARNING);
}
::VariantClear(&varChunk);
::SafeArrayDestroyData(psa);
if(uIsRead<ChunkSize)break;
}//while(1)
f.Close();
//从数据库读一张照片:
CFilef;
f.Open("h:bb.jpg",Cfile::modeWrite│Cfile::modeCreate);
longlPhotoSize=m_pRecordset->Fields->Item["photo"]->ActualSize;
longlIsRead=0;
_variant_tvarChunk;
BYTEbuf;
while(lPhotoSize>0)
{
lIsRead=lPhotoSize>=ChunkSize?ChunkSize:lPhotoSize;
varChunk=m_pRecordset->Fields->
Item["photo"]->GetChunk(lIsRead);
for(longindex=0;index<lIsRead;index++)
{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
f.Write(buf,lIsRead);
lPhotoSize-=lIsRead;
}//while()
f.Close();
12.利用SafeArray成绩
学会利用SafeArray也是很主要的,由于在ADO编程中常常要用。它的次要目标是用于automation中的数组型参数的传送。由于在收集情况中,数组是不克不及间接传送的,而必需将其包装成SafeArray。本色上SafeArray就是将一般的数组增添一个形貌符,申明其维数、长度、界限、元素范例等信息。SafeArray也其实不独自利用,而是将其再包装到VARIANT范例的变量中,然后才作为参数传送进来。在VARIANT的vt成员的值假如包括VT_ARRAY│...,那末它所封装的就是一个SafeArray,它的parray成员便是指向SafeArray的指针。SafeArray中元素的范例能够是VARIANT能封装的任何范例,包含VARIANT范例自己。
利用SafeArray的详细步骤:
办法一:
包装一个SafeArray:
(1).界说变量,如:
VARIANTvarChunk;
SAFEARRAY*psa;
SAFEARRAYBOUNDrgsabound;
(2).创立SafeArray形貌符:
uIsRead=f.Read(bVal,ChunkSize);//readarrayfromafile.
if(uIsRead==0)break;
rgsabound.cElements=uIsRead;
rgsabound.lLbound=0;
psa=SafeArrayCreate(VT_UI1,1,rgsabound);
(3).安排数据元素到SafeArray:
for(longindex=0;index<uIsRead;index++)
{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal)))
::MessageBox(NULL,"出偏差了。","提醒",MB_OK│MB_ICONWARNING);
}
一个一个地放,挺贫苦的。
(4).封装到VARIANT内:
varChunk.vt=VT_ARRAY│VT_UI1;
varChunk.parray=psa;
如许就能够将varChunk作为参数传送进来了。
读取SafeArray中的数据的步骤:
(1).用SafeArrayGetElement一个一个地读
BYTEbuf;
for(longindex=0;index<lIsRead;index++)
{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
就读到缓冲区buf里了。
办法二:
利用SafeArrayAccessData间接读写SafeArray的缓冲区:
(1).读缓冲区:
BYTE*buf;
SafeArrayAccessData(varChunk.parray,(void**)&buf);
f.Write(buf,lIsRead);
SafeArrayUnaccessData(varChunk.parray);
(2).写缓冲区:
BYTE*buf;
::SafeArrayAccessData(psa,(void**)&buf);
for(longindex=0;index<uIsRead;index++)
{
buf=bVal;
}
::SafeArrayUnaccessData(psa);
varChunk.vt=VT_ARRAY│VT_UI1;
varChunk.parray=psa;
这类办法读写SafeArray都能够,它间接利用SafeArray的数据缓冲区,比用SafeArrayGetElement和SafeArrayPutElement速率快。出格合适于读取数据。但用完以后不要忘了挪用::SafeArrayUnaccessData(psa),不然会堕落的。
13.利用书签(bookmark)
书签能够独一标识纪录会合的一个纪录,用于疾速地将以后纪录移回到已会见过的纪录,和举行过滤等等。Provider会主动为纪录会合的每笔记录发生一个书签,我们只必要利用它就好了。我们不克不及试图显现、修正或对照书签。ADO用纪录集的Bookmark属性暗示以后纪录的书签。
用法步骤:
(1).创建一个VARIANT范例的变量
_variant_tVarBookmark;
(2).将以后纪录的书签值存进该变量
也就是纪录集的Bookmark属性确当前值。
VarBookmark=rst->Bookmark;
(3).前往到先前的纪录
将保留的书签值设置到纪录集的书签属性中:
//Checkforwhetherbookmarksetforarecord
if(VarBookmark.vt==VT_EMPTY)
printf("NoBookmarkset!
");
else
rst->Bookmark=VarBookmark;
设置完后,以后纪录即会挪动到该书签指向的纪录。
14、设置过滤前提
Recordset工具的Filter属性暗示了以后的过滤前提。它的值能够是以AND或OR毗连起来的前提表达式(不含WHERE关头字)、由书签构成的数组或ADO供应的FilterGroupEnum列举值。为Filter属性设置新值后Recordset确当前纪录指针会主动挪动到满意过滤前提的第一个纪录。比方:
rst->Filter=_bstr_t("姓名=赵薇AND性别=’女’");
在利用前提表达式时应注重以下成绩:
(1)、能够用圆括号构成庞大的表达式
比方:
rst->Filter=_bstr_t("(姓名=赵薇AND性别=’女’)ORAGE<25");
可是微软不同意在括号内用OR,然后在括号外用AND,比方:
rst->Filter=_bstr_t("(姓名=赵薇OR性别=’女’)ANDAGE<25");
必需修正为:
rst->Filter=_bstr_t("(姓名=赵薇ANDAGE<25)OR(性别=’女’ANDAGE<25)");
(2)、表达式中的对照运算符能够是LIKE
LIKE后被对照的是一个含有通配符*的字符串,星号暗示多少个恣意的字符。
字符串的首部和尾部能够同时带星号*
rst->Filter=_bstr_t("姓名LIKE*赵*");
也能够只是尾部带星号:
rst->Filter=_bstr_t("姓名LIKE赵*");
Filter属性值的范例是Variant,假如过滤前提是由书签构成的数组,则需将该数组转换为SafeArray,然后再封装到一个VARIANT或_variant_t型的变量中,再赋给Filter属性。
15、索引与排序
(1)、创建索引
当以某个字段为关头字用Find办法查找时,为了加速速率能够以该字段为关头字在纪录集外部一时创建索引。只需将该字段的Optimize属性设置为true便可,比方:
pRst->Fields->GetItem("姓名")->Properties->
GetItem("Optimize")->PutValue("True");
pRst->Find("姓名=赵薇",1,adSearchForward);
......
pRst->Fields->GetItem("姓名")->Properties->
GetItem("Optimize")->PutValue("False");
pRst->Close();
申明:Optimize属性是由Provider供应的属性(在ADO中称为静态属性),ADO自己没有此属性。
(2)、排序
要排序也很复杂,只需把要排序的关头字列表设置到Recordset工具的Sort属性里便可,比方:
pRstAuthors->CursorLocation=adUseClient;
pRstAuthors->Open("SELECT*FROMmytable",
_variant_t((IDispatch*)pConnection),
adOpenStatic,adLockReadOnly,adCmdText);
......
pRst->Sort="姓名DESC,岁数ASC";
关头字(即字段名)之间用逗号离隔,假如要以某关头字降序排序,则应在该关头字后加一空格,再加DESC(如上例)。升序时ASC加不加无所谓。本操纵是使用索引举行的,并未举行物理排序,以是效力较高。
但要注重,在翻开纪录集之前必需将纪录集的CursorLocation属性设置为adUseClient,如上例所示。Sort属性值在必要时随时能够修正。
16、事件处置
ADO中的事件处置也很复杂,只需分离在得当的地位挪用Connection工具的三个办法便可,这三个办法是:
(1)、在事件入手下手时挪用
pCnn->BeginTrans();
(2)、在事件停止并乐成时挪用
pCnn->CommitTrans();
(3)、在事件停止并失利时挪用
pCnn->RollbackTrans();
在利用事件处置时,应只管减大事务的局限,即减小处置务入手下手到停止(提交或回滚)之间的工夫距离,以便进步体系效力。必要时也可在挪用BeginTrans()办法之前,先设置Connection工具的IsolationLevel属性值,具体内容拜见MSDN中有关ADO的手艺材料。
3、利用ADO编程罕见成绩解答
以下均是针对MSSQL7.0编程时所遇成绩举行会商。
1、毗连失利大概缘故原由
EnterpriseManagemer内,翻开将服务器的属性对话框,在Security选项卡中,有一个选项Authentication。
假如该选项是WindowsNTonly,则你的程序所用的毗连字符串就必定要包括Trusted_Connection参数,而且其值必需为yes,如:
"Provider=SQLOLEDB;Server=888;Trusted_Connection=yes"
";Database=master;uid=lad;";
假如不按上述操纵,程序运转时毗连一定失利。
假如Authentication选项是SQLServerandWindowsNT,则你的程序所用的毗连字符串能够不包括Trusted_Connection参数,如:
"Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;";
由于ADO给该参数取的默许值就是no,以是能够省略。我以为仍是取默许值对照平安一些。
2、改动以后数据库的办法
利用Tansct-SQL中的USE语句便可。
3、怎样判别一个数据库是不是存在
(1)、可翻开master数据库中一个叫做SCHEMATA的视图,其内容列出了该服务器上一切的数据库称号。
(2)、更烦琐的办法是利用USE语句,乐成了就存在;不乐成,就不存在。比方:
try{
m_pConnect->Execute(_bstr_t("USEINSURANCE_2002"),NULL,
adCmdText│adExecuteNoRecords);
}
catch(_com_error&e)
{
blSuccess=FALSE;
CStringstr="数据库INSURANCE_2002不存在!
";
str+=e.Description();
::MessageBox(NULL,str,"告诫",MB_OK│MB_ICONWARNING);
}
4、判别一个表是不是存在
(1)、一样判别一个表是不是存在,也能够用是不是乐成地翻开它来判别,非常便利,比方:
try{
m_pRecordset->Open(_variant_t("mytable"),
_variant_t((IDispatch*)m_pConnection,true),adOpenKeyset,
adLockOptimistic,adCmdTable);
}
catch(_com_error&e)
{
::MessageBox(NULL,"该表不存在。","提醒",MB_OK│MB_ICONWARNING);
}
(2)、要否则能够接纳贫苦一点的举措,就是在MS-SQL服务器上的每一个数据库中都有一个名为sysobjects的表,检察此表的内容即知指定的表是不是在该数据库中。
(3)、一样,每一个数据库中都有一个名为TABLES的视图(View),检察此视图的内容即知指定的表是不是在该数据库中。
5、范例转换成绩
(1)、范例VARIANT_BOOL
范例VARIANT_BOOL等价于short范例。TheVARIANT_BOOLisequivalenttoshort.seeitsdefinitionbelow:
typdefshortVARIANT_BOOL
(2)、_com_ptr_t类的范例转换
_ConnectionPtr能够主动转换成IDspatch*范例,这是由于_ConnectionPtr实践上是_com_ptr_t类的一个实例,而这个类有此范例转换函数。
同理,_RecordsetPtr和_CommandPtr也都能够如许转换。
(3)、_bstr_t和_variant_t类
在ADO编程时,_bstr_t和_variant_t这两个类很有效,省往了很多BSTR和VARIANT范例转换的贫苦。
6、翻开纪录集时的成绩
在翻开纪录集时,在挪用Recordset的Open办法时,其最初一个参数里必定不克不及包括adAsyncExecute,不然将由于是异步操纵,在读取数据时没法读到数据。
7、非常处置成绩
对一切挪用ADO的语句必定要用try和catch语句捕获非常,不然在产生非常时,程序会非常加入。
8、利用SafeArray成绩
在初学利用中,我曾碰到一个伤头脑的成绩,必定要注重:
在界说了SAFEARRAY的指针后,假如盘算反复利用屡次,则在两头能够挪用::SafeArrayDestroyData开释数据,但决不克不及挪用::SafeArrayDestroyDescriptor,不然一定堕落,即便挪用SafeArrayCreate也不可。比方:
SAFEARRAY*psa;
......
//Whenthedataarenolongertobeused:
::SafeArrayDestroyData(psa);
我剖析在界说psa指针时,一个SAFEARRAY的实例(也就是SAFEARRAY形貌符)也同时被主动创建了。可是只需一挪用::SafeArrayDestroyDescriptor,形貌符就被烧毁了。
以是我以为::SafeArrayDestroyDescriptor能够基本就不挪用,即便挪用也必需在最初挪用。
9、反复利用命令工具成绩
一个命令工具假如要反复利用屡次(特别是带参数的命令),则在第一次实行之前,应将它的Prepared属性设置为TRUE。如许会使第一次实行减慢,但却可使今后的实行全体加速。
10、绑定字符串型字段成绩
假如要绑定的字段是字符串范例,则对应的字符数组的元素个数必定要比字段长度年夜2(好比m_szau_fname,其绑定的字段au_fname的长度实践是20),不如许绑定就会失利。
11、利用AppendChunk的成绩
当用AddNew办法方才向纪录集内增加一个新纪录以后,不克不及起首向一个长数据字段(image范例)写进数据,必需先向其他字段写进过数据以后,才干挪用AppendChunk写该字段,不然堕落。也就是说,AppendChunk不克不及紧接在AddNew以后。别的,写进其他字段后还必需紧接着挪用AppendChunk,而不克不及挪用纪录集的Update办法后,才挪用AppendChunk,不然挪用AppendChunk时也会堕落。换句话说,就是必需AppendChunk在前,Update在后。因此这个时分就不克不及利用带参数的AddNew了,由于带参数的AddNew会主动挪用纪录集的Update,以是AppendChunk就跑到Update的前面了,就只要堕落了!因而,这时候应当用不带参数的AddNew。
我推想这多是MSSQL7.0的成绩,在MSSQL2000中则不存在这些成绩,可是AppendChunk仍旧不克不及在Update以后。
4、小结
一样平常情形下,Connection和Command的Execute用于实行不发生纪录集的命令,而Recordset的Open用于发生一个纪录集,固然也不是相对的。出格Command次要是用于实行参数化的命令,能够间接由Command工具实行,也能够将Command工具传送给Recordset的Open。使为了数据安全,我们搭建了主从。但实时主从备份只能防止硬件问题,比如主库的硬盘损坏。但对于误操作,则无能为力。比如在主库误删一张表,或者一个update语句没有指定where条件,导致全表被更新。 记得在最开始使用2k的时候就要用到这个功能,可惜2k没有,现在有了作解决方案的朋友会很高兴吧。 理解了存储结构,再阅读下性能优化的章节基本上会对sqlserver有个清晰地认识 原理很简单,对要求长时间计算某一时间点的报表生成和防用户操作错误很有帮助。但是比起Oracle10g的闪回技术还是细粒度不够。可惜! SQLServer的异构移植功能个人感觉最好了。(如果对比过SQLServer的链接服务器和Oracle的透明网关的朋友会发现SQLServer的sp_addlinkedserver(openquery)异构数据库系列比Oracle真是强太多了。) 代替了原来VB式的错误判断。比Oracle高级不少。 以前的DTS轻盈简单。但是现在的SSIS虽然功能强大了很多,但是总是让人感觉太麻烦。看看论坛中询问SSIS的贴子就知道。做的功能太强大了,往往会有很多用户不会用了 也可谈一下你是怎么优化存储过程的? 始终遗憾SQLServer的登陆无法分配CPU/内存占用等指标数。如果你的SQLServer给别人分配了一个只可以读几个表的权限,而这个家伙疯狂的死循环进行连接查询,会给你的系统带来很大的负担。
页:
[1]