PostgreSQL ODBC 查询表数据,使用游标分批返回数据
通过 ODBC 连接 PostgreSQL 数据库查询表数据,如果表中数据量非常大,一次性全部取出会占用大量的内存,可能导致程序崩溃。此时可以考虑采用游标分批返回数据的方式来实现。
ODBC 使用游标分批返回数据需要先在 odbc.ini 配置文件中配置参数 UseDeclareFetch 和 Fetch。
- UseDeclareFetch,表示启用数据库服务器的游标分批返回数据
- Fetch,表示使用数据库服务器的游标时,结果集在客户端缓存的最大行数,默认值为 100
配置 odbc.ini,如下:
[pg] Description = PostgreSQL ODBC Driver = PostgreSQL Database = postgres Servername = 127.0.0.1 UserName = admin Password = 123456 Port = 36099 ReadOnly = -1 ConnSettings = set client_encoding to UTF8 UseDeclareFetch = 1 Fetch = 10000
测试程序代码,查询表 t,如下:
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#define NAME_LEN 64
#define STMT_LEN 128
#define CHECK_ERROR(e, s, h, t) ({\
if (e!=SQL_SUCCESS && e != SQL_SUCCESS_WITH_INFO) {extract_error(s, h, t); goto exit;} \
})
void extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type)
{
SQLINTEGER i = 0;
SQLINTEGER NativeError;
SQLCHAR SQLState[ 7 ];
SQLCHAR MessageText[256];
SQLSMALLINT TextLength;
SQLRETURN ret;
fprintf(stderr, "\nThe driver reported the following error %s\n", fn);
do
{
ret = SQLGetDiagRec(type, handle, ++i, SQLState, &NativeError,
MessageText, sizeof(MessageText), &TextLength);
if (SQL_SUCCEEDED(ret)) {
printf("%s:%ld:%ld:%s\n",
SQLState, (long) i, (long) NativeError, MessageText);
}
}
while( ret == SQL_SUCCESS );
}
int main () {
SQLHENV henv = SQL_NULL_HENV; // Environment
SQLHDBC hdbc = SQL_NULL_HDBC; // Connection handle
SQLHSTMT hstmt = SQL_NULL_HSTMT; // Statement handle
SQLRETURN retcode;
long len;
char buf[1024];
char stmt[] = "select * from t";
// Allocate an environment handle
retcode=SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
henv, SQL_HANDLE_ENV);
// We want ODBC 3 support
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(void *) SQL_OV_ODBC3, 0);
CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
hdbc, SQL_HANDLE_DBC);
// Allocate a connection handle
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
CHECK_ERROR(retcode, "SQLAllocHandle", hdbc, SQL_HANDLE_DBC);
// Connect to the DSN
retcode=SQLDriverConnect(hdbc, NULL, "DSN=pg;", SQL_NTS,
NULL, 0, NULL, SQL_DRIVER_COMPLETE);
CHECK_ERROR(retcode, "SQLDriverConnect(DSN=DATASOURCE;)",
hdbc, SQL_HANDLE_DBC);
// Allocate a statement handle
retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
CHECK_ERROR(retcode, "SQLAllocHandle(STMT)", hstmt, SQL_HANDLE_STMT);
retcode = SQLExecDirect(hstmt, stmt, SQL_NTS);
if ( SQL_SUCCEEDED( retcode ))
{
while(SQL_SUCCEEDED(retcode = SQLFetch(hstmt)))
{
SQLGetData(hstmt,1,SQL_C_CHAR, buf, 50, &len);
printf("%s\n", buf);
}
}
else
{
CHECK_ERROR(retcode, "SQLExecDirect(STMT)", hstmt, SQL_HANDLE_STMT);
}
exit:
printf ("\nComplete.\n");
// Free handles
// Statement
if (hstmt != SQL_NULL_HSTMT)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
// Connection
if (hdbc != SQL_NULL_HDBC) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
// Environment
if (henv != SQL_NULL_HENV)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return 0;
}
编译执行,如下:
[zhang@localhost odbc]$ gcc select.c -o select -I/data/zhang/app/unixODBC/include -L/data/zhang/app/unixODBC/lib -lodbc [zhang@localhost odbc]$ ./select aaaaa bbbbb ccccc ddddd eeeee Complete.
设置 PostgreSQL 的参数 log_statement = 'all',记录所有执行的 SQL,会发现如下的日志记录:
BEGIN; declare "SQL_CUR0x2f92b5d0" cursor with hold for select * from t; fetch 10000 in "SQL_CUR0xa0905d0"; close "SQL_CUR0xa0905d0"; commit;
文章评论
共0条评论