首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
80 阅读
2
nvim番外之将配置的插件管理器更新为lazy
58 阅读
3
2018总结与2019规划
54 阅读
4
PDF标准详解(五)——图形状态
33 阅读
5
为 MariaDB 配置远程访问权限
30 阅读
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
VimScript
Java
emacs
linux
文本编辑器
elisp
反汇编
OLEDB
数据库编程
数据结构
内核编程
Masimaro
累计撰写
308
篇文章
累计收到
27
条评论
首页
栏目
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
页面
归档
友情链接
关于
搜索到
2
篇与
的结果
2018-05-27
ATL模板库中的OLEDB与ADO
上次将OLEDB的所有内容基本上都说完了,从之前的示例上来看OLEDB中有许多变量的定义,什么结果集对象、session对象、命令对象,还有各种缓冲等等,总体上来说直接使用OLEDB写程序很麻烦,用很大的代码量带来的仅仅只是简单的功能。还要考虑各种缓冲的释放,各种对象的关闭,程序员的大量精力都浪费在无用的事情上,针对这些情况微软在OLEDB上提供了两种封装方式,一种是将其封装在ATL模板库中,一种是使用ActiveX控件来进行封装称之为ADO,这次主要写的是这两种方式ATL 模板中的OLEDB由于ATL模板是开源的,这种方式封装简洁,调试简易(毕竟源代码都给你了),各个模块相对独立,但是它的缺点很明显就是使用门槛相对较高,只有对C++中的模板十分熟悉的开发人员才能使用的得心应手。ATL中的OLEDB主要有两大模块,提供者模块和消费者模块,顾名思义,提供者模块是数据库的开发人员使用的,它主要使用这个模块实现OLEDB中的接口,对外提供相应的数据库服务;消费者模块就是使用OLEDB在程序中操作数据库。这里主要说的是消费者模块ATL主要封装的类ATL针对OLEDB封装的主要有这么几个重要的类:数据库对象CDataConnection 数据源连接类主要实现的是数据库的连接相关的功能,根据这个可以猜测出来它实际上封装的是OLEDB中的数据源对象和会话对象CDataSource:数据源对象CEnumerator: 架构结果集对象,主要用来查询数据库的相关信息,比如数据库中的表结构等信息CSession: 会话对象访问器对象:CAccessor: 常规的访问器对象CAccessorBase: 访问器对象的基类CDynamicAccessor:动态绑定的访问器CDynamicParamterAccessor:参数绑定的访问器,从之前博文的内容来看它应该是进行参数化查询等操作时使用的对象CDynamicStringAccessor:这个一般是要将查询结果显示为字符串时使用,它负责将数据库中的数据转化为字符串ALT中针对OLEDB的封装在头文件atldbcli.h中,在项目中只要包含它就行了模板的使用静态绑定针对静态绑定,VS提供了很好的向导程序帮助我们生成对应的类,方便了开发,使用的基本步骤如下:在项目上右键,选择添加类在类选择框中点击ATL并选择其中的ATL OLEDB使用者选择对应的数据源、数据库表和需要对数据库进行的操作注意如果要对数据库表进行增删改查等操作,一定要选这里的表选项点击数据源配置数据源连接的相关属性,最后点击完成。最终会在项目中生成对应的头文件这是最终生成的完整代码class Caa26Accessor { public: //value LONG m_aac031; TCHAR m_aaa146[51]; LONG m_aaa147; LONG m_aaa148; //status DBSTATUS m_dwaac031Status; DBSTATUS m_dwaaa146Status; DBSTATUS m_dwaaa147Status; DBSTATUS m_dwaaa148Status; //lenth DBLENGTH m_dwaac031Length; DBLENGTH m_dwaaa146Length; DBLENGTH m_dwaaa147Length; DBLENGTH m_dwaaa148Length; void GetRowsetProperties(CDBPropSet* pPropSet) { pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL); pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL); pPropSet->AddProperty(DBPROP_IRowsetChange, true, DBPROPOPTIONS_OPTIONAL); pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE); } HRESULT OpenDataSource() { CDataSource _db; HRESULT hr; hr = _db.OpenFromInitializationString(L"Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa; Password=XXXXXX;Initial Catalog=study;Data Source=XXXX;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=LIU-PC;Use Encryption for Data=False;Tag with column collation when possible=False"); if (FAILED(hr)) { #ifdef _DEBUG AtlTraceErrorRecords(hr); #endif return hr; } return m_session.Open(_db); } void CloseDataSource() { m_session.Close(); } operator const CSession&() { return m_session; } CSession m_session; DEFINE_COMMAND_EX(Caa26Accessor, L" \ SELECT \ aac031, \ aaa146, \ aaa147, \ aaa148 \ FROM dbo.aa26") BEGIN_COLUMN_MAP(Caa26Accessor) COLUMN_ENTRY_LENGTH_STATUS(1, m_aac031, m_dwaac031Length, m_dwaac031Status) COLUMN_ENTRY_LENGTH_STATUS(2, m_aaa146, m_dwaaa146Length, m_dwaaa146Status) COLUMN_ENTRY_LENGTH_STATUS(3, m_aaa147, m_dwaaa147Length, m_dwaaa147Status) COLUMN_ENTRY_LENGTH_STATUS(4, m_aaa148, m_dwaaa148Length, m_dwaaa148Status) END_COLUMN_MAP() }; class Caa26 : public CCommand<CAccessor<Caa26Accessor> > { public: HRESULT OpenAll() { HRESULT hr; hr = OpenDataSource(); if (FAILED(hr)) return hr; __if_exists(GetRowsetProperties) { CDBPropSet propset(DBPROPSET_ROWSET); __if_exists(HasBookmark) { if( HasBookmark() ) propset.AddProperty(DBPROP_IRowsetLocate, true); } GetRowsetProperties(&propset); return OpenRowset(&propset); } __if_not_exists(GetRowsetProperties) { __if_exists(HasBookmark) { if( HasBookmark() ) { CDBPropSet propset(DBPROPSET_ROWSET); propset.AddProperty(DBPROP_IRowsetLocate, true); return OpenRowset(&propset); } } } return OpenRowset(); } HRESULT OpenRowset(DBPROPSET *pPropSet = NULL) { HRESULT hr = Open(m_session, NULL, pPropSet); #ifdef _DEBUG if(FAILED(hr)) AtlTraceErrorRecords(hr); #endif return hr; } void CloseAll() { Close(); ReleaseCommand(); CloseDataSource(); } };从名字上来看Caa26Accessor主要是作为一个访问器,其实它的功能也是与访问器相关的,比如创建访问器和数据绑定都在最后这个映射中。而后面的Caa26类主要是用来执行sql语句并根据上面的访问器类来解析数据,其实我们使用上主要使用后面这个类,这些代码都很简单,有之前的OLEDB基础很容易就能理解它们,这里就不再在这块进行展开了int _tmain(int argc, TCHAR *argv) { CoInitialize(NULL); Caa26 aa26; HRESULT hRes = aa26.OpenDataSource(); if (FAILED(hRes)) { aa26.CloseAll(); CoUninitialize(); return -1; } hRes = aa26.OpenRowset(); if (FAILED(hRes)) { aa26.CloseAll(); CoUninitialize(); return -1; } hRes = aa26.MoveNext(); COM_USEPRINTF(); do { COM_PRINTF(_T("|%-30u|%-30s|%-30u|%-30u|\n"), aa26.m_aac031, aa26.m_aaa146, aa26.m_aaa147, aa26.m_aaa148); hRes = aa26.MoveNext(); } while (S_OK == hRes); aa26.CloseAll(); CoUninitialize(); return 0; }动态绑定动态绑定主要是使用模板对相关的类进行拼装,所以这里需要关于模板的相关知识,只有掌握了这些才能正确的拼接出合适的类。一般需要拼接的是这样几个类结果集类,在结果集类的模板中填入对应的访问器类,表示该结果集将使用对应的访问器进行解析。访问器类可以系统预定义的,也向静态绑定那样自定义。Command类,在命令对象类的模板位置填入与命令相关的类,也就是执行命令生成的结果集、以及解析结果集所用的访问器,之后就主要使用Command类来进行数据库的相关操作了下面是一个使用的示例typedef CCommand<CDynamicAccessor, CRowset, CMultipleResults> CComRowset; typedef CTable<CDynamicAccessor, CRowset> CComTable; //将所有绑定的数据类型转化为字符串 typedef CTable<CDynamicStringAccessor, CRowset> CComTableString; int _tmain(int argc, TCHAR *argv[]) { CoInitialize(NULL); COM_USEPRINTF(); //连接数据库,创建session对象 CDataSource db; db.Open(); CSession session; session.Open(db); //打开数据库表 CComTableString table; table.Open(session, OLESTR("T_DecimalDemo")); HRESULT hRes = table.MoveFirst(); if (FAILED(hRes)) { COM_PRINTF(_T("表中没有数据,退出程序\n")); goto __CLEAN_UP; } do { //这里传入的参数是列的序号,注意一下,由于有第0列的行需要存在,所以真正的数据是从第一列开始的 COM_PRINTF(_T("|%-10s|%-20s|%-20s|%-20s|\n"), table.GetString(1), table.GetString(2), table.GetString(3), table.GetString(4)); hRes = table.MoveNext(); } while (S_OK == hRes); __CLEAN_UP: CoUninitialize(); return 0; }在上面的代码中我们定义了两个模板类,Ctable和CCommand类,没有发现需要的访问器类,查看CTable类可以发现它是继承于CAccessorRowset,而CAccessorRowset继承于TAccessor和 TRowset,也就是说它提供了访问器的相关功能而且它还可以使用OpenRowset方法不执行SQL直接打开数据表,因此在这里我们选择使用它在CTable的模板中填入CDynamicStringAccessor表示将会把得到的结果集中的数据转化为字符串。在使用上先使用CDataSource类的Open方法打开数据库连接,然后调用CTable的Open打开数据表,接着调用CTable的MoveFirst的方法将行句柄移动到首行。接着在循环中调用table的GetString方法得到各个字段的字符串值,并调用MoveNext方法移动到下一行其实在代码中并没有使用CCommand类,这是由于这里只是简单的使用直接打开数据表的方式,而并没有执行SQL语句,因此不需要它,在这里定义它只是简单的展示一下ADOATL针对OLEDB封装的确是方便了不少,但是对于像我这种将C++简单的作为带对象的C来看的人来说,它使用模板实在是太不友好了,说实话现在我现在对模板的认识实在太少,在代码中我也尽量避免使用模板。所以在我看来使用ATL还不如自己根据项目封装一套。好在微软实在太为开发者着想了,又提供了ADO这种针对ActiveX的封装方式。要使用ADO组件需要先导入,导入的语句如下:#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" no_namespace rename("EOF", "EndOfFile")这个路径一般是不会变化的,而EOF在C++中一般是用在文件中的,所以这里将它rename一下ADO中的主要对象和接口有:Connect :数据库的连接对象,类似于OLEDB中的数据源对象和session对象Command:命令对象,用来执行sql语句,类似于OLEDB中的Command对象Recordset: 记录集对象,执行SQL语句返回的结果,类似于OLEDB中的结果集对象Record: 数据记录对象,一般都是从Recordset中取得,就好像OLEDB中从结果集对象通过访问器获取到具体的数据一样Field:记录中的一个字段,可以简单的看做就是一个表字段的值,一般一个记录集中有多条记录,而一条记录中有个Field对象Parameter:参数对象,一般用于参数化查询或者调用存储过程Property:属性,与之前OLEDB中的属性对应-在ADO中大量使用智能指针,所谓的智能指针是它的生命周期结束后会自动析构它所指向的对象,同时也封装了一些常见指针操作,虽然它是这个对象但是它的使用上与普通的指针基本上相同。ADO中的智能指针对象一般是在类名后加上Ptr。比如Connect对象的智能指针对象是_ConnectPtr智能指针有利也有弊,有利的地方在于它能够自动管理内存,不需要程序员进行额外的释放操作,而且它在使用上就像普通的指针,相比于使用类的普通指针更为方便,不利的地方在于为了方便它的使用一般都经过了大量的重载,因此很多地方表面上看是一个普通的寻址操作,而实际上却是一个函数调用,这样就降低了性能。所以在特别强调性能的场合要避免使用智能指针。在使用上,一般经过这样几个步骤:定义数据库连接的Connect对象调用Connect对象的Open方法连接数据库,这里使用的连接字串的方式创建Command对象并调用对象Execute方法执行SQL,并获取对应的记录集。这里执行SQL语句也可以使用Recordset对象的Open方法。循环调用Recordse对象的MoveNext不断取出对应行的行记录下面是一个使用的简单例子#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" no_namespace rename("EOF", "EndOfFile") int _tmain(int argc, TCHAR *argv[]) { CoInitialize(NULL); _ConnectionPtr conn; _RecordsetPtr rowset; _bstr_t bstrConn = _T("Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password = 123456;Initial Catalog=Study;Data Source=LIU-PC\\SQLEXPRESS;"); conn.CreateInstance(_T("ADODB.Connection")); conn->Open(bstrConn, _T("sa"), _T("123456"), adModeUnknown); if (conn->State) { COM_PRINTF(_T("连接到数据源成功\n")); }else { COM_PRINTF(_T("连接到数据源失败\n")); return 0; } rowset.CreateInstance(__uuidof(Recordset)); rowset->Open(_T("select * from aa26;"), conn.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText); while (!rowset->EndOfFile) { COM_PRINTF(_T("|%-30u|%-30s|%-30u|%-30u|\n"), rowset->Fields->GetItem(_T("aac031"))->Value.intVal, rowset->Fields->GetItem(_T("aaa146"))->Value.bstrVal, rowset->Fields->GetItem(_T("aaa147"))->Value.llVal, rowset->Fields->GetItem(_T("aaa148"))->Value.llVal ); rowset->MoveNext(); } CoUninitialize(); return 0; }ADO与OLEDB混合编程ADO相比较OLEDB来说确实方便了不少,但是它也有它的问题,比如它是封装的ActiveX控件,从效率上肯定比不上OLEDB,而且ADO中记录集是一次性将结果中的所有数据加载到内存中,如果数据表比教大时这种方式很吃内存。而OLEDB是每次调用GetNextRow时加载一条记录到内存(其实根据之前的代码可以知道它加载的时机,加载的大小是可以控制的),它相对来说比教灵活。其实上述问题使用二者的混合编程就可以很好的解决,在处理结果集时使用OLEDB,而在其他操作时使用ADO这样既保留了ADO的简洁性也使用了OLEDB灵活管理结果集内存的能力。在ADO中,可以通过_Recordset查询出ADORecordsetConstruction接口,这个接口提供了将记录集转化为OLEDB中结果集,以及将结果集转化为Recordset对象的能力下面是一个简单的例子CoInitialize(NULL); try { _bstr_t bsCnct(_T("Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password = 123456;Initial Catalog=Study;Data Source=LIU-PC\\SQLEXPRESS;")); _RecordsetPtr Rowset(__uuidof(Recordset)); Rowset->Open(_T("select * from aa26;") ,bsCnct,adOpenStatic,adLockOptimistic,adCmdText); //获取IRowset接口指针 ADORecordsetConstruction* padoRecordsetConstruct = NULL; Rowset->QueryInterface(__uuidof(ADORecordsetConstruction), (void **) &padoRecordsetConstruct); IRowset * pIRowset = NULL; padoRecordsetConstruct->get_Rowset((IUnknown **)&pIRowset); padoRecordsetConstruct->Release(); DisplayRowSet(pIRowset); pIRowset->Release(); GRS_PRINTF(_T("\n\n显示第二个结果集:\n")); //使用OLEDB方法打开一个结果集 IOpenRowset* pIOpenRowset = NULL; TCHAR* pszTableName = _T("T_State"); DBID TableID = {}; CreateDBSession(pIOpenRowset); TableID.eKind = DBKIND_NAME; TableID.uName.pwszName = (LPOLESTR)pszTableName; HRESULT hr = pIOpenRowset->OpenRowset(NULL,&TableID,NULL,IID_IRowset,0,NULL,(IUnknown**)&pIRowset); if(FAILED(hr)) { _com_raise_error(hr); } //创建一个新的ADO记录集对象 _RecordsetPtr Rowset2(__uuidof(Recordset)); Rowset2->QueryInterface(__uuidof(ADORecordsetConstruction), (void **) &padoRecordsetConstruct); //将OLEDB的结果集放置到ADO记录集对象中 padoRecordsetConstruct->put_Rowset(pIRowset); ULONG ulRow = 0; while(!Rowset2->EndOfFile) { COM_PRINTF(_T("|%10u|%10s|%-40s|\n") ,++ulRow ,Rowset2->Fields->GetItem("K_StateCode")->Value.bstrVal ,Rowset2->Fields->GetItem("F_StateName")->Value.bstrVal); Rowset2->MoveNext(); } } catch(_com_error & e) { COM_PRINTF(_T("发生错误:\n Source : %s \n Description : %s \n") ,(LPCTSTR)e.Source(),(LPCTSTR)e.Description()); } _tsystem(_T("PAUSE")); CoUninitialize(); return 0;这次就不再放上整体例子的链接了,有之前的基础应该很容易看懂这些,而且这次代码比较短,基本上将所有代码全粘贴了过来。
2018年05月27日
6 阅读
0 评论
0 点赞
2015-09-03
ADO对SQL Server 2008数据库的基础操作
最近在学习ADO与数据库的相关知识,现在我将自己学到的东西整理写出来,也算是对学习的一种复习。这篇文章主要说明如何遍历某台机器上所有的数据库服务,遍历某个服务中所有的数据库,遍历数据库中的所有表以及表中所有字段字段,最后再说明如何通过ADO操作数据库中的表。遍历所有数据库服务:遍历数据库服务我们通过函数NetServerEnum来实现,该函数可以 遍历局域网中所有的服务可以通过指定服务类型来有筛选的进行枚举,以达到遍历所有数据库服务的目的,该函数的原型如下:NET_API_STATUS NetServerEnum( _In_opt_ LPCWSTR servername, _In_ DWORD level, _Out_ LPBYTE *bufptr, _In_ DWORD prefmaxlen, _Out_ LPDWORD entriesread, _Out_ LPDWORD totalentries, _In_ DWORD servertype, _In_opt_ LPCWSTR domain, _Inout_opt_ LPDWORD resume_handle );各个参数的说明如下:servername:这个参数是系统保留的必须为NULLlevel:参数用于指明返回参数的结构体的版本,主要有100和101两个值,分别对应SERVER_INFO_100和SERVER_INFO_100;bufptr:是一个返回参数,系统在这个指针所指向的区域中填充一个SERVER_INFO_100或SERVER_INFO_100的结构体,具体使用哪一种由上一个参数指定;prefmaxlen:返回值的最大长度,以字节为单位,一般我们设置为MAX_PREFREED_LENGHT,这个参数表明,具体需要多大的缓冲由函数指定entriesread:由函数返回,表示当前获取的枚举的数量totalentries:由函数返回,表示当前机器上所有的服务的总数servertype:获取的服务的类型;(具体类型请参阅MSDN,我们这里主要用的是SV_TYPE_SQLSERVER获取数据库服务)domain:一个常量字符串,用于指定要返回服务器列表域的名称,如果这个值为NULL则域名是隐含的resume_handle:保留参数,该参数必须为NULL;以下是实现的代码,大部分是从MSDN上Copy下来的,只是修改了小部分代码:#include "stdafx.h" #include <iostream> #include <windows.h> #include <lm.h> using namespace std; #pragma comment(lib, "netapi32.lib") int _tmain(int argc, _TCHAR* argv[]) { LPSERVER_INFO_101 pBuf = NULL; LPSERVER_INFO_101 pTmpBuf; DWORD dwLevel = 101; DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwTotalCount = 0; DWORD dwServerType = SV_TYPE_SQLSERVER; // SQL SERVER DWORD dwResumeHandle = 0; NET_API_STATUS nStatus; LPWSTR pszServerName = NULL; LPWSTR pszDomainName = NULL; DWORD i; nStatus = NetServerEnum(pszServerName, dwLevel, (LPBYTE *)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries, dwServerType, pszDomainName, &dwResumeHandle); if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA)) { if ((pTmpBuf = pBuf) != NULL) { for (i = 0; i < dwEntriesRead; i++) //成功则依次输出结果 { assert(pTmpBuf != NULL); printf("\tPlatform: %d\n", pTmpBuf->sv101_platform_id); wprintf(L"\tName: %s\n", pTmpBuf->sv101_name); printf("\tVersion: %d.%d\n",pTmpBuf->sv101_version_major, pTmpBuf->sv101_version_minor); printf("\tType: sql server"); printf("\n"); wprintf(L"\tComment: %s\n\n", pTmpBuf->sv101_comment); pTmpBuf++; dwTotalCount++; } } else { printf("No servers were found\n"); printf("The buffer (bufptr) returned was NULL\n"); printf(" entriesread: %d\n", dwEntriesRead); printf(" totalentries: %d\n", dwEntriesRead); } } else//函数执行错误 { fprintf(stderr, "NetServerEnum failed with error: %d\n", nStatus); } if (pBuf != NULL) { NetApiBufferFree(pBuf);//释放内存空间 } }遍历数据库中服务器中所有数据库:通过上一步我们可以遍历局域网中所有数据库服务,这个时候我们更进一步来遍历数据库服务中所有的数据库,实现这个功能没有什么特殊的函数,主要是对系统表的应用,我们用的是系统表master.sys.database,该表中记录了服务中所有的数据库,通过ADO的方式来进行操作://初始化COM组件库 CoInitialize(NULL); _ConnectionPtr pConnect; HRESULT hErr; try { //创建Connection对象 hErr = pConnect.CreateInstance("ADODB.Connection"); if (SUCCEEDED(hErr)) { pConnect->Open("Driver={SQL Server};Server=LIUHAO-PC;Uid = sa;Pw = 1234", "", "", adModeUnknown); } } catch (_com_error &e) { cout << e.Description() << endl; } try { //创建RECORD, COMMAND对象 _RecordsetPtr pRecord; pRecord.CreateInstance("ADODB.Recordset"); _CommandPtr pCommand; pCommand.CreateInstance("ADODB.Command"); //遍历服务器中所有数据库 cout << "服务器中所有数据库:" << endl; pRecord->Open("select * from master.sys.databases", pConnect.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText); _variant_t vName; while (!pRecord->adoEOF) { vName = pRecord->GetCollect(_variant_t("name")); wcout << (TCHAR*)_bstr_t(vName) << endl; pRecord->MoveNext(); } pRecord->Close(); } catch { cout << e.Description() << endl; }遍历某个数据库中所有的表:该操作也是使用系统表的内容,代码如下://遍历test数据库中所有表 cout << "数据库中所有表:" << endl; pRecord->Open("use test SELECT name FROM sys.sysobjects WHERE type='U' ORDER BY name", pConnect.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText); while (!pRecord->adoEOF) { vName = pRecord->GetCollect(_variant_t("name")); wcout << (TCHAR*)_bstr_t(vName) << endl; pRecord->MoveNext(); } pRecord->Close();sql语句中前面的"use test"表示在test数据库中查找表后面的“type = 'U' ”表示我们遍历的是用户表此外type 还可以是一下值:U = 用户表V = 视图,TF = 表函数,P = 存储过程,L = 日志等遍历某个表中的所有字段://显示表中所有的字段名 cout << "test表中所有字段:" << endl; FieldsPtr fields;//用于保存字段信息 pRecord->Open("SELECT * FROM users", pConnect.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText); fields = pRecord->GetFields();//获取字段信息 long nCount = fields->GetCount();//获取遍历到的字段总数 for (int i = 0; i < nCount; i++) {//获取到的字段信息是放到field对象的item结构体数组中,利用GetItem可以获取该结构体数组中的某一项,利用GetName函数可以获取结构体中存储的字段名 bstr_t bstrName = (fields->GetItem(_variant_t((long)i)))->GetName(); TCHAR *pText = static_cast<TCHAR*>(bstrName);//将字段名转化为字符串输出 wcout << pText << endl; }利用以上所有代码,用户可以根据获取到局域网中所有SQL SERVER数据库服务器,遍历其中的所有数据库,根据获取到的数据库获取数据库中所有表,进到某一个表中遍历所有字段,有了字段用户就可以通过SQL语句操作某个表或者其中的某个字段了
2015年09月03日
5 阅读
0 评论
0 点赞