从程序变量大容量复制数据 (ODBC)
此示例演示如何通过 bcp_bind 和 bcp_sendrow 使用大容量复制函数将数据从程序变量大容量复制到 SQL Server。 (为简化此示例,已删除错误检查代码。)
此示例是面向 ODBC 3.0 版或更高版本开发的。
安全说明 如果可能,请使用 Windows 身份验证。 如果 Windows 身份验证不可用,请在运行时提示用户输入其凭据。 不要将凭据存储在一个文件中。 如果必须保存凭据,应当用 Win32 cryptoAPI(Win32 加密 API)加密它们。
直接在程序变量上使用大容量复制函数
分配环境句柄和连接句柄。
设置 SQL_COPT_SS_BCP 和 SQL_BCP_ON 以启用大容量复制操作。
连接到 SQL Server。
调用 bcp_init 以设置以下信息:
作为大容量复制的源或目标的表或视图的名称。
将数据文件的名称指定为 NULL。
用于接收任何大容量复制错误消息的数据文件的名称(如果不想要消息文件,则指定 NULL)。
副本的方向:DB_IN 从应用程序到视图或表,或 DB_OUT 从表或视图到应用程序。
在大容量复制中针对每一列调用 bcp_bind,以将该列绑定到程序变量。
用数据填充程序变量,并调用 bcp_sendrow 以发送一行数据。
发送完所有行之后,调用 bcp_done 以完成操作。
通过调用 bcp_colptr 和 bcp_collen,可以在大容量复制操作期间更改程序变量的位置和长度。 使用 bcp_control 可以设置多个大容量复制选项。 使用 bcp_moretext 可以将 text、ntext 和 image 数据分段发送到服务器。
示例
IA64 平台不支持此示例。
此外,还需要一个名为 AdventureWorks 的 ODBC 数据源,其默认数据库是 AdventureWorks 示例数据库。 (可以从 Microsoft SQL Server Samples and Community Projects(Microsoft SQL Server 示例和社区项目)主页下载 AdventureWorks 示例数据库。)此数据源必须基于操作系统提供的 ODBC 驱动程序(该驱动程序的名称为“SQL Server”)。 如果您要将此示例构建为在 64 位操作系统上运行的 32 位应用程序并运行该示例,则必须使用 %windir%\SysWOW64\odbcad32.exe 中的 ODBC 管理器创建 ODBC 数据源。
此示例连接到您的计算机上默认的 SQL Server 实例。 若要连接到命名实例,请更改 ODBC 数据源的定义以使用以下格式指定实例:server\namedinstance。 默认情况下,SQL Server Express 将安装在命名实例中。
执行第一个 (Transact-SQL) 代码列表,以创建该示例将使用的表。
使用 odbc32.lib 和 odbcbcp.lib 编译第二个 (C++) 代码列表。 如果用 MSBuild.exe 生成示例,请先将 Bcpfmt.fmt 和 Bcpodbc.bcp 从项目目录复制到 .exe 文件所在的目录,然后调用 .exe。
执行第三个 (Transact-SQL) 代码列表,以删除该示例使用的表。
// compile with: odbc32.lib odbcbcp.lib
use AdventureWorks
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'BCPSource')
DROP TABLE BCPSource
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'BCPTarget')
DROP TABLE BCPTarget
GO
CREATE TABLE BCPSource (cola int PRIMARY KEY, colb CHAR(10) NULL)
INSERT INTO BCPSource (cola, colb) VALUES (1, 'aaa')
INSERT INTO BCPSource (cola, colb) VALUES (2, 'bbb')
CREATE TABLE BCPTarget (cola int PRIMARY KEY, colb CHAR(10) NULL)
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <odbcss.h>
SQLHENV henv = SQL_NULL_HENV;
HDBC hdbc1 = SQL_NULL_HDBC, hdbc2 = SQL_NULL_HDBC;
SQLHSTMT hstmt2 = SQL_NULL_HSTMT;
void Cleanup() {
if (hstmt2 != SQL_NULL_HSTMT)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);
if (hdbc1 != SQL_NULL_HDBC) {
SQLDisconnect(hdbc1);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
}
if (hdbc2 != SQL_NULL_HDBC) {
SQLDisconnect(hdbc2);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc2);
}
if (henv != SQL_NULL_HENV)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
int main() {
RETCODE retcode;
// BCP variables.
char *terminator = "\0";
// bcp_done takes a different format return code because it returns number of rows bulk copied
// after the last bcp_batch call.
DBINT cRowsDone = 0;
// Set up separate return code for bcp_sendrow so it is not using the same retcode as SQLFetch.
RETCODE SendRet;
// Column variables. cbCola and cbColb must be defined right before Cola and szColb because
// they are used as bulk copy indicator variables.
struct ColaData {
int cbCola;
SQLINTEGER Cola;
} ColaInst;
struct ColbData {
int cbColb;
SQLCHAR szColb[11];
} ColbInst;
// Allocate the ODBC environment and save handle.
retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(Env) Failed\n\n");
Cleanup();
return(9);
}
// Notify ODBC that this is an ODBC 3.0 app.
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLSetEnvAttr(ODBC version) Failed\n\n");
Cleanup();
return(9);
}
// Allocate ODBC connection handle, set bulk copy mode, and connect.
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
retcode = SQLSetConnectAttr(hdbc1, SQL_COPT_SS_BCP, (void *)SQL_BCP_ON, SQL_IS_INTEGER);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLSetConnectAttr(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// sample uses Integrated Security, create the SQL Server DSN using Windows NT authentication
retcode = SQLConnect(hdbc1, (UCHAR*)"AdventureWorks", SQL_NTS, (UCHAR*)"", SQL_NTS, (UCHAR*)"", SQL_NTS);
if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
printf("SQLConnect() Failed\n\n");
Cleanup();
return(9);
}
// Initialize the bulk copy.
retcode = bcp_init(hdbc1, "AdventureWorks..BCPTarget", NULL, NULL, DB_IN);
if ( (retcode != SUCCEED) ) {
printf("bcp_init(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// Bind the program variables for the bulk copy.
retcode = bcp_bind(hdbc1, (BYTE *)&ColaInst.cbCola, 4, SQL_VARLEN_DATA, NULL, (INT)NULL, SQLINT4, 1);
if ( (retcode != SUCCEED) ) {
printf("bcp_bind(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// Could normally use strlen to calculate the bcp_bind cbTerm parameter, but this terminator
// is a null byte (\0), which gives strlen a value of 0. Explicitly give cbTerm a value of 1.
retcode = bcp_bind(hdbc1, (BYTE *)&ColbInst.cbColb, 4, 11, (UCHAR*)terminator, 1, SQLCHARACTER, 2);
if ( (retcode != SUCCEED) ) {
printf("bcp_bind(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
// Allocate second ODBC connection handle so bulk copy and cursor operations do not conflict.
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc2);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(hdbc2) Failed\n\n");
Cleanup();
return(9);
}
// Sample uses Integrated Security, create SQL Server DSN using Windows NT authentication.
retcode = SQLConnect(hdbc2, (UCHAR*)"AdventureWorks", SQL_NTS, (UCHAR*)"", SQL_NTS, (UCHAR*)"", SQL_NTS);
if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
printf("SQLConnect() Failed\n\n");
Cleanup();
return(9);
}
// Allocate ODBC statement handle.
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt2);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLAllocHandle(hstmt2) Failed\n\n");
Cleanup();
return(9);
}
SQLLEN lDataLengthA;
SQLLEN lDataLengthB;
// Bind the SELECT statement to the same program variables bound to the bulk copy operation.
retcode = SQLBindCol(hstmt2, 1, SQL_C_SLONG, &ColaInst.Cola, 0, &lDataLengthA);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLBindCol(hstmt2) Failed\n\n");
Cleanup();
return(9);
}
retcode = SQLBindCol(hstmt2, 2, SQL_C_CHAR, &ColbInst.szColb, 11, &lDataLengthB);
if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
printf("SQLBindCol(hstmt2) Failed\n\n");
Cleanup();
return(9);
}
// Execute SELECT statement to build a cursor containing data to be bulk copied to new table.
retcode = SQLExecDirect(hstmt2, (UCHAR*)"SELECT * FROM BCPSource", SQL_NTS);
if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
printf("SQLExecDirect Failed\n\n");
Cleanup();
return(9);
}
// Go into a loop fetching rows from the cursor until each row is fetched. Because the
// bcp_bind calls and SQLBindCol calls each reference the same variables, each fetch fills
// the variables used by bcp_sendrow, so all you have to do to send the data to SQL Server is
// to call bcp_sendrow.
while ( (retcode = SQLFetch(hstmt2) ) != SQL_NO_DATA) {
if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
printf("SQLFetch Failed\n\n");
Cleanup();
return(9);
}
ColaInst.cbCola = (int)lDataLengthA;
ColbInst.cbColb = (int)lDataLengthB;
if ( (SendRet = bcp_sendrow(hdbc1) ) != SUCCEED ) {
printf("bcp_sendrow(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
}
// Signal the end of the bulk copy operation.
cRowsDone = bcp_done(hdbc1);
if ( (cRowsDone == -1) ) {
printf("bcp_done(hdbc1) Failed\n\n");
Cleanup();
return(9);
}
printf("Number of rows bulk copied after last bcp_batch call = %d.\n", cRowsDone);
// Cleanup.
SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);
SQLDisconnect(hdbc1);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
SQLDisconnect(hdbc2);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc2);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
use AdventureWorks
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'BCPSource')
DROP TABLE BCPSource
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'BCPTarget')
DROP TABLE BCPTarget
GO