프로그램 변수에서 데이터 대량 복사(ODBC)

이 예제에서는 bcp_bind 및 bcp_sendrow를 사용하여 프로그램 변수에서 SQL Server로 데이터를 대량 복사하는 대량 복사 함수의 사용 방법을 보여 줍니다. 이 예제를 간소화하기 위해 오류 검사 코드는 제거했습니다.

이 예제는 ODBC 버전 3.0 이상용으로 개발되었습니다.

보안 정보   가능하면 Windows 인증을 사용하십시오. Windows 인증을 사용할 수 없으면 런타임에 사용자에게 자격 증명을 입력하라는 메시지를 표시합니다. 자격 증명은 파일에 저장하지 않는 것이 좋습니다. 자격 증명을 유지하려면 Win32 cryptoAPI를 사용하여 자격 증명을 암호화해야 합니다.

프로그램 변수에서 직접 대량 복사 함수를 사용하려면

  1. 환경 핸들 및 연결 핸들을 할당합니다.

  2. 대량 복사 작업을 사용하도록 SQL_COPT_SS_BCP 및 SQL_BCP_ON을 설정합니다.

  3. SQL Server에 연결합니다.

  4. bcp_init를 호출하여 다음 정보를 설정합니다.

    • 대량 복사를 수행할 원본 또는 대상 테이블/뷰의 이름을 지정합니다.

    • 데이터 파일의 이름에 NULL을 지정합니다.

    • 대량 복사 오류 메시지를 받을 데이터 파일의 이름을 지정합니다. 메시지 파일이 필요하지 않으면 NULL을 지정합니다.

    • 복사 방향을 지정합니다. 응용 프로그램에서 뷰 또는 테이블로 복사하려는 경우 DB_IN, 테이블 또는 뷰에서 응용 프로그램으로 복사하려는 경우 DB_OUT입니다.

  5. 대량 복사에서 각 열에 대해 bcp_bind를 호출하여 열을 프로그램 변수에 바인딩합니다.

  6. 프로그램 변수에 데이터를 채우고 bcp_sendrow를 호출하여 데이터 행을 보냅니다.

  7. 여러 행을 보낸 후 bcp_batch를 호출하여 이미 보낸 행의 검사점을 설정합니다. bcp_batch를 1000개 행당 한 번 이상 호출하는 것이 좋습니다.

  8. 모든 행을 보낸 후 bcp_done을 호출하여 작업을 완료합니다.

대량 복사 작업 동안 bcp_colptrbcp_collen을 호출하여 프로그램 변수의 위치와 길이를 변경할 수 있습니다. bcp_control을 사용하면 여러 대량 복사 옵션을 설정할 수 있습니다. bcp_moretext를 사용하면 text, ntext 및 image 데이터를 분할된 형태로 서버에 보낼 수 있습니다.

이 예제는 IA64에서 지원되지 않습니다.

AdventureWorks 예제 데이터베이스를 기본 데이터베이스로 사용하는 AdventureWorks라는 ODBC 데이터 원본이 필요합니다. AdventureWorks 예제 데이터베이스는 Microsoft SQL Server 예제 및 커뮤니티 프로젝트(Microsoft SQL Server Samples and Community Projects) 홈 페이지에서 다운로드할 수 있습니다. 이 데이터 원본은 운영 체제에서 제공하는 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

참고 항목

개념

프로그램 변수에서 대량 복사

관련 자료

SQL Server ODBC 드라이버를 사용한 대량 복사 방법 도움말 항목(ODBC)