Cursor是光标的意思,指的是在计算机屏幕上闪烁的一个亮块或一条竖线,如果在一个文字编辑的屏幕上就更好理解了,光标会随着键盘的上下左右键相应的移动。这个词在数据库查询的结果集中使用时就翻译成了游标,与光标的区别是它只能上下移动,代表在不同的数据行间选择。
在一条查询语句被执行后,就生成了一个结果集,这时游标处在第一行数据之前,所以取回(fetch)下一条数据(SQL_FETCH_NEXT)就是取回第一条数据。调用一次fetch函数,游标就停在fetch回的最后一条数据上,如果只取回一条,那么游标就在这条数据上,如果取回多条(使用数组取回),那么游标就停在最后一条数据上。
前面我们用到的SQLFetch()函数只能取回下一条数据,使用的是一种只能向前滚动的游标,如果要取回当前数据之前的某条数据就会比较麻烦,只能重新执行查询语句,然后再fetch到想要的那条数据,即浪费时间,有操作复杂。为解决这样的问题,ODBC提供了滚动游标(scrollable cursor),这种游标可以从任意位置取回结果集中的数据行。要使用滚动游标,需要设置语句属性SQL_ATTR_CURSOR_SCROLLABLE,属性值为SQL_SCROLLABLE,设置这个属性的位置有些特别,要在分配语句句柄后设置,在SQLPrepare()或SQLExecDirect()函数调用之前,否则会出错。
在执行语句后生成了结果集,这时就可以使用滚动游标了,取回数据的函数要使用SQLFetchScroll(),而不是SQLFetch()。我们看一下SQLFetchScroll()的函数原型和参数。
SQLRETURN SQLFetchScroll(
SQLHSTMT StatementHandle,
SQLSMALLINT FetchOrientation,
SQLLEN FetchOffset);
StatementHandle是一个输入参数,查询语句的句柄。
FetchOrientation是一个输入参数,取回数据的方向值。取值如下:
SQL_FETCH_NEXT(取下一条)
SQL_FETCH_PRIOR(取当前行的前一条)
SQL_FETCH_FIRST(取第一行)
SQL_FETCH_LAST(取最后一行)
SQL_FETCH_ABSOLUTE(按绝对行号取数据)
SQL_FETCH_RELATIVE(按相对行号取数据)
SQL_FETCH_BOOKMARK(按书签取数据)
FetchOffset是一个输入参数,取数据的行号偏移量。对于绝对行号取数据,偏移量就是某个行号,表示取结果集中的第几行。对于相对行号取数据,偏移量就是相对于当前行偏移几行,如果是正数,就是当前行加偏移行的行号,如果是负数,就是当前行减去偏移行的行号。
我们看一个实际的例子,怎样设置滚动游标属性,怎样使用SQLFetchScroll()函数。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sql.h"
#include "sqlext.h"
#include "sqltypes.h"
SQLHANDLE envh; /* env handle */
SQLHANDLE dbch; /* connect handle */
SQLHANDLE stmth; /* statement handle */
int main(int argc, char *argv[])
{
int i;
int conn = 0;
SQLRETURN rc;
SQLLEN len_ind1;
SQLLEN len_ind2;
SQLLEN len_ind3;
SQLLEN len_ind4;
SQLLEN len_ind5;
SQLINTEGER id;
char dsn_str[32];
char usrname[32];
char passwd[32];
char sqltxt[128];
char f1[32];
char f2[32];
char f3[32];
char f4[32];
if (argc < 3) {
fprintf(stderr, "usage: %s dsn username password\n", argv[0]);
return (-1);
}
strncpy(dsn_str, argv[1], 32);
dsn_str[31] = '\0';
strncpy(usrname, argv[2], 32);
usrname[31] = '\0';
strncpy(passwd, argv[3], 32);
passwd[31] = '\0';
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &envh);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Allocate environment handle error.\n");
return (-1);
}
rc = SQLSetEnvAttr(envh, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Set ODBC version error.\n");
goto free_exit;
}
rc = SQLAllocHandle(SQL_HANDLE_DBC, envh, &dbch);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Allocate DB connection handle error.\n");
goto free_exit;
}
rc = SQLSetConnectAttr(dbch, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)10, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Set connection timeout value error.\n");
goto free_exit;
}
rc = SQLConnect(dbch, (SQLCHAR *)dsn_str, SQL_NTS, (SQLCHAR *)usrname, SQL_NTS,
(SQLCHAR *)passwd, SQL_NTS);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Connect to DB error.\n");
goto free_exit;
}
conn = 1;
fprintf(stdout, "connect DB ok ......\n");
rc = SQLAllocHandle(SQL_HANDLE_STMT, dbch, &stmth);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Allocate statment handle error.\n");
goto free_exit;
}
/* 设置结果集使用滚动游标 */
rc = SQLSetStmtAttr(stmth, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER)SQL_SCROLLABLE, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Set statment attribute error.\n");
goto free_exit;
}
/* 准备语句 */
sprintf(sqltxt, "select id, f1, f2, f3, f4 from my_tredo1");
rc = SQLPrepare(stmth, (SQLCHAR *)sqltxt, SQL_NTS);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Prepare statment error.\n");
goto free_exit;
}
rc = SQLBindCol(stmth, 1, SQL_C_ULONG, (SQLPOINTER)&id, 0, &len_ind1);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Bind column 1 error.\n");
goto free_exit;
}
rc = SQLBindCol(stmth, 2, SQL_C_CHAR, (SQLPOINTER)f1, 32, &len_ind2);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Bind column 2 error.\n");
goto free_exit;
}
rc = SQLBindCol(stmth, 3, SQL_C_CHAR, (SQLPOINTER)f2, 32, &len_ind3);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Bind column 3 error.\n");
goto free_exit;
}
rc = SQLBindCol(stmth, 4, SQL_C_CHAR, (SQLPOINTER)f3, 32, &len_ind4);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Bind column 4 error.\n");
goto free_exit;
}
rc = SQLBindCol(stmth, 5, SQL_C_CHAR, (SQLPOINTER)f4, 32, &len_ind5);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Bind column 4 error.\n");
goto free_exit;
}
rc = SQLExecute(stmth);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Execute statment error.\n");
goto free_exit;
}
/* 先使用SQL_FETCH_NEXT参数,把结果集中的数据行都打印出来 */
for (i=1; ;i++) {
rc = SQLFetchScroll(stmth, SQL_FETCH_NEXT, 0);
if (rc == SQL_NO_DATA) {
fprintf(stderr, "no data in result set, break.\n");
break;
} else if (rc == SQL_ERROR) {
fprintf(stderr, "Fetch data error.\n");
goto free_exit;
}
fprintf(stdout, "row[%02d] id=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
i, id, f1, f2, f3, f4);
}
/* 取回第一条数据,打印显示 */
rc = SQLFetchScroll(stmth, SQL_FETCH_FIRST, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Fetch the first row error.\n");
goto free_exit;
}
fprintf(stderr, "The first row:\nid=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
id, f1, f2, f3, f4);
/* 取回最后一条数据,打印显示 */
rc = SQLFetchScroll(stmth, SQL_FETCH_LAST, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Fetch the last row error.\n");
goto free_exit;
}
fprintf(stderr, "The last row:\nid=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
id, f1, f2, f3, f4);
/* 取回倒数第二行数据,打印显示 */
rc = SQLFetchScroll(stmth, SQL_FETCH_PRIOR, 0);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Fetch the second to last row error.\n");
goto free_exit;
}
fprintf(stderr, "The second to last row:\nid=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
id, f1, f2, f3, f4);
/* 使用相对行号,取回倒数第二条的前一条,就是倒数第三行的数据 */
rc = SQLFetchScroll(stmth, SQL_FETCH_RELATIVE, -1);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Fetch the third to last row error.\n");
goto free_exit;
}
fprintf(stderr, "The third to last row:\nid=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
id, f1, f2, f3, f4);
/* 使用绝对行号,取回第五行的数据 */
rc = SQLFetchScroll(stmth, SQL_FETCH_ABSOLUTE, 5);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Fetch the fifth row error.\n");
goto free_exit;
}
fprintf(stderr, "The fifth row:\nid=%d, f1=%s, f2=%s, f3=%s, f4=%s\n",
id, f1, f2, f3, f4);
/* 关闭游标 */
SQLCloseCursor(stmth);
SQLFreeHandle(SQL_HANDLE_STMT, stmth);
SQLDisconnect(dbch);
SQLFreeHandle(SQL_HANDLE_DBC, dbch);
SQLFreeHandle(SQL_HANDLE_ENV, envh);
return (0);
free_exit:
if (stmth != NULL) {
SQLFreeHandle(SQL_HANDLE_STMT, stmth);
}
if (conn) {
SQLDisconnect(dbch);
}
if (dbch != NULL) {
SQLFreeHandle(SQL_HANDLE_DBC, dbch);
}
if (envh != NULL) {
SQLFreeHandle(SQL_HANDLE_ENV, envh);
}
return (-1);
}
访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容。