时间:2021-07-01 10:21:17 帮助过:17人阅读
这段代码详细的展示了如何执行SQL语句获取结果集并设置COMMANDUI对象的属性。
结果集一般是执行完SQL语句后返回的一个代表二维结构化数组的对象。这个结构化对象可以理解为一个与数据表定义相同的一个结构体。而结果集中保存了这个结构体的指针
下面是结果集对象的详细定义
CoType TRowset {
[mandatory] interface IAccessor;
[mandatory] interface IColumnsInfo;
[mandatory] interface IConvertType;
[mandatory] interface IRowset;
[mandatory] interface IRowsetInfo;
[optional] interface IChapteredRowset;
[optional] interface IColumnsInfo2;
[optional] interface IColumnsRowset;
[optional] interface IConnectionPointContainer;
[optional] interface IDBAsynchStatus;
[optional] interface IGetRow;
[optional] interface IRowsetChange;
[optional] interface IRowsetChapterMember;
[optional] interface IRowsetCurrentIndex;
[optional] interface IRowsetFind;
[optional] interface IRowsetIdentity;
[optional] interface IRowsetIndex;
[optional] interface IRowsetLocate;
[optional] interface IRowsetRefresh;
[optional] interface IRowsetScroll;
[optional] interface IRowsetUpdate;
[optional] interface IRowsetView;
[optional] interface ISupportErrorInfo;
[optional] interface IRowsetBookmark;
}
得到结果集后,它的使用步骤一般如下:
取得结果集对象后,紧接着的操作一般就是获取结果集的结构信息,也就是获取结果集的列信息(有些材料中称为字段信息)要获取列信息,就需要QueryInterface出结果集对象的IColumnsInfo接口,并调用IColumnsInfo::GetColumnInfo方法获得一个称为DBCOLUMNINFO结构体的数组该结构体中反映了列的逻辑结构信息(抽象数据类型)和物理结构信息(内存需求大小等信息)
函数GetColumnInfo定义如下:
HRESULT GetColumnInfo (
DBORDINAL *pcColumns,
DBCOLUMNINFO **prgInfo,
OLECHAR **ppStringsBuffer);
第一个参数表示总共有多少列,第二个参数是一个DBCOLUMNINFO,返回一个列信息的数组指针,第三个参数返回一个字符串指针,这个字符串中保存的是个列的名称,每个名称间以\0\0分割。但是我们一般不使用它来获取列名,我们一般使用DBCOLUMNINFO结构的pwszName成员。
DBCOLUMNINFO定义如下:
typedef struct tagDBCOLUMNINFO {
LPOLESTR pwszName; //列名
ITypeInfo *pTypeInfo; //列的类型信息
DBORDINAL iOrdinal; //列序号
DBCOLUMNFLAGS dwFlags; //列的相关标识
DBLENGTH ulColumnSize; //列最大可能的大小,对于字符串来说,它表示的是字符个数
DBTYPE wType; //列类型
BYTE bPrecision; //精度(它表示小数点后面的位数)
BYTE bScale; //表示该列的比例,目前没有用处,一般给的是0
DBID columnid; //列信息在数据字典表中存储的ID
} DBCOLUMNINFO;
对于columnid成员,DBMS系统一般会有多个系统表来表示众多的信息,比如用户信息,数据库信息,数据表信息等等,其中针对每个表中的列的相关信息DBMS系统使用特定的系统表来存储,而查询这个系统表来获取列信息时使用的就是这个columnid值。
一般绑定需要两步,1是获取列信息的DBCOLUMNINFO结构,接着就是根据列信息来填充DBBINDING数据结构。
有的时候可能会觉得绑定好麻烦啊,还不如直接返回一个缓冲,将所有结果放入里面,应用程序根据需求自己去解析它,这样岂不是更方便。之所以需要绑定,有下面一个理由:
绑定结构的定义如下:
typedef struct tagDBBINDING
{
DBORDINAL iOrdinal; //列号
DBBYTEOFFSET obValue;
DBBYTEOFFSET obLength;
DBBYTEOFFSET obStatus;
ITypeInfo *pTypeInfo;
DBOBJECT *pObject;
DBBINDEXT *pBindExt;
DBPART dwPart;
DBMEMOWNER dwMemOwner;
DBPARAMIO eParamIO;
DBLENGTH cbMaxLen; //数据的最大长度,一般给我们为这个列的数据准备的缓冲的长度
DWORD dwFlags;
DBTYPE wType; //将该列转化为何种数据类型展示
BYTE bPrecision;
BYTE bScale;
}DBBINDING;
参数的详细说明:
wType:将数据源中的原始数据做何种类型的转化,比如原来数据库中存储的是整数的123456,而这个值是DBTYPE_WSTR的话,数据源中的结果会被转化为字符串的"123456",放入到缓冲中。
DBCOLUMNINFO反映的是二维结果集的原始列结构信息而DBBINDING则反映的是二维结果集数据最终按要求摆放在内存中的样式
下面是一个针对绑定的列子:
```cpp
ExecSql(pIOpenRowset, pIRowset);
//创建IColumnsInfo接口
HRESULT hRes = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
COM_SUCCESS(hRes, _T("查询接口IComclumnsInfo,错误码:%08x\n"), hRes);
//获取结果集的详细信息
hRes = pIColumnsInfo->GetColumnInfo(&cClumns, &rgColumnInfo, &lpClumnsName);
COM_SUCCESS(hRes, _T("获取列信息失败,错误码:%08x\n"), hRes);
//绑定
pDBBindings = (DBBINDING)MALLOC(sizeof(DBBINDING) cClumns);
for (int iRow = 0; iRow < cClumns; iRow++)
{
pDBBindings[iRow].bPrecision = rgColumnInfo[iRow].bPrecision;
pDBBindings[iRow].bScale = rgColumnInfo[iRow].bScale;
pDBBindings[iRow].cbMaxLen = rgColumnInfo[iRow].ulColumnSize * sizeof(WCHAR);
if (rgColumnInfo[iRow].wType == DBTYPE_I4)
{
//数据库中行政单位的长度最大为6位
pDBBindings[iRow].cbMaxLen = 7 * sizeof(WCHAR);
}
pDBBindings[iRow].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pDBBindings[iRow].dwPart = DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE;
pDBBindings[iRow].eParamIO = DBPARAMIO_NOTPARAM;
pDBBindings[iRow].iOrdinal = rgColumnInfo[iRow].iOrdinal;
pDBBindings[iRow].obStatus = dwOffset;
pDBBindings[iRow].obLength = dwOffset + sizeof(DBSTATUS);
pDBBindings[iRow].obValue = dwOffset + sizeof(DBSTATUS) + sizeof(ULONG);
pDBBindings[iRow].wType = DBTYPE_WSTR;
dwOffset = dwOffset + sizeof(DBSTATUS) + sizeof(ULONG) + pDBBindings[iRow].cbMaxLen * sizeof(WCHAR);
dwOffset = COM_ROUNDUP(dwOffset); //进行内存对齐
}
//创建访问器
hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
COM_SUCCESS(hRes, _T("查询IAccessor接口失败错误码:%08x\n"), hRes);
hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cClumns, pDBBindings, 0, &hAccessor, NULL);
COM_SUCCESS(hRes, _T("创建访问器失败错误码:%08x\n"), hRes);
//输出列名信息
DisplayColumnName(rgColumnInfo, cClumns);
//分配对应的内存
pData = MALLOC(dwOffset * cRows);
while (TRUE)
{
hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, cRows, &uRowsObtained, &phRows);
if (hRes != S_OK && uRowsObtained != 0)
{
break;
}
ZeroMemory(pData, dwOffset * cRows);
//显示数据
for (int i = 0; i < uRowsObtained; i++)
{
pCurrData = (BYTE)pData + dwOffset i;
pIRowset->GetData(phRows[i], hAccessor, pCurrData);
DisplayData(pDBBindings, cClumns, pCurrData);
}
//清理hRows
pIRowset->ReleaseRows(uRowsObtained, phRows, NULL, NULL, NULL);
CoTaskMemFree(phRows);
phRows = NULL;
}
//显示列名称
void DisplayColumnName(DBCOLUMNINFO *pdbColumnInfo, DBCOUNTITEM iDbCount)
{
COM_DECLARE_BUFFER();
for (int iColumn = 0; iColumn < iDbCount; iColumn++)
{
COM_CLEAR_BUFFER();
TCHAR wszColumnName[MAX_DISPLAY_SIZE + 1] =_T("");
size_t dwSize = 0;
StringCchLength(pdbColumnInfo[iColumn].pwszName, MAX_DISPLAY_SIZE, &dwSize);
dwSize = min(dwSize, MAX_DISPLAY_SIZE);
StringCchCopy(wszColumnName, MAX_DISPLAY_SIZE, pdbColumnInfo[iColumn].pwszName);
COM_PRINTF(wszColumnName);
COM_PRINTF(_T("\t"))
}
COM_PRINTF(_T("\n"));
}
//显示数据
void DisplayData(DBBINDING pdbBindings, DBCOUNTITEM iDbColCnt, void pData)
{
COM_DECLARE_BUFFER();
for (int i = 0; i < iDbColCnt; i++)
{
COM_CLEAR_BUFFER();
DBSTATUS status = (DBSTATUS)((PBYTE)pData + pdbBindings[i].obStatus);
ULONG uSize = ((ULONG)((PBYTE)pData + pdbBindings[i].obLength)) / sizeof(WCHAR);
PWSTR pCurr = (PWSTR)((PBYTE)pData + pdbBindings[i].obValue);
switch (status)
{
case DBSTATUS_S_OK:
case DBSTATUS_S_TRUNCATED:
COM_PRINTF(_T("%s\t"), pCurr);
break;
case DBSTATUS_S_ISNULL:
COM_PRINTF(_T("%s\t"), _T("(null)"));
break;
default:
break;
}
}
COM_PRINTF(_T("\n"));
}
```
在使用前一个列子中的方法设置对应的属性并执行SQL语句后,得到一个结果集,然后调用对应的Query方法,得到一个pIColumnsInfo接口,接着调用接口的GetColumnsInfo方法,获取结构的具体信息。
最需要注意的是绑定部分的代码,根据返回的具体列数,我们定义了一个对应的绑定结构的数组,将每个赋值,赋值的时候定义了一个dwOffset结构来记录当前使用内存的情况,这样每次在循环执行一次后,它的位置永远在上一个列信息缓冲的尾部,这样我们可以很方便的进行偏移的计算。
绑定完成后这个dwOffset的值就是所有列使用的内存的总大小,因此在后面利用这个值分配一个对应长度的内存。然后循环调用GetNextRows、GetData方法依次获取每行、每列的数据。最后调用相应的函数来进行显示,至此就完成了数据的读取操作。
最后,我发现码云上的代码片段简直就是为保存平时例子代码而生的,所以后面的代码将不再在GitHub上更新了,而换到码云上面。
源代码查看
SQL语句执行与结果集的获取
标签:告诉 uid key 详细信息 clear 固定 结构化 而且 区分