從程式變數大量複製資料 (ODBC)
適用於: SQL Server Azure SQL 資料庫 Azure SQL 受控執行個體 Azure Synapse Analytics Analytics Platform System (PDW)
此範例示範如何使用大量複製函式,使用 bcp_bind 和 bcp_sendrow將數據從程式變數大量複製到 SQL Server。 (已移除錯誤檢查程式代碼以簡化此範例。)
此範例是針對 ODBC 3.0 版或更新版本所開發。
安全性注意事項 可能的話,請使用 Windows 驗證。 如果無法使用 Windows 驗證,請提示使用者在執行階段輸入認證。 請避免將認證儲存在檔案中。 如果您必須保存認證,則應該用 Win32 cryptoAPI 加密這些認證。
直接在程式變數上使用大量複製函式
配置環境句柄和連接句柄。
設定SQL_COPT_SS_BCP和SQL_BCP_ON以啟用大量複製作業。
連接至 SQL Server。
呼叫 bcp_init 以設定下列資訊:
要大量複製或複製到的數據表或檢視表名稱。
指定數據檔名稱的 NULL。
要接收任何大量複製錯誤訊息的數據檔名稱(如果您不想要訊息檔,請指定 NULL)。
複製的方向:從應用程式DB_IN至檢視或數據表,或從數據表或檢視表DB_OUT至應用程式。
針對大量複製中的每個數據行呼叫 bcp_bind ,將數據行系結至程式變數。
使用數據填入程式變數,並呼叫 bcp_sendrow 以傳送數據列。
傳送數個數據列之後,請呼叫 bcp_batch 至已傳送的數據列檢查點。 最好每 1000 個數據列至少呼叫 bcp_batch 一次。
傳送所有數據列之後,請呼叫 bcp_done 來完成作業。
您可以呼叫bcp_colptr和bcp_collen,在大量複製作業期間變更程式變數的位置和長度。 使用 bcp_control 來設定各種大量複製選項。 使用 bcp_moretext,將區段中的 text、ntext 和 image 數據傳送至伺服器。
範例
IA64 不支援此範例。
您將需要名為 AdventureWorks 的 ODBC 數據源,其預設資料庫是 AdventureWorks 範例資料庫。 (您可以從 下載 AdventureWorks 範例資料庫 Microsoft SQL Server 範例和社群專案 首頁。)此數據源必須以操作系統提供的 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 AdventureWorks2022;
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 AdventureWorks2022;
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