나만의 작은 도서관
[TIL][C++] 250901 MMO 서버 개발 91일차: ODBC API 사용시 다중 행 처리하는 법 본문
주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
ODBC API 사용 시 다중 행 처리하는 법
- UPDATE문을 여러 번 해야 하는 경우 for문을 이용하여 UPDATE를 해야 하는 행 수만큼 반복하는 것은 네트워크 왕복(round-trip) 횟수를 늘리는 주원인이 될 수 있기 때문에 그리 효율적인 방법이 아니다. 그렇다면 어떻게 해야 여러 행, 즉, 다중행을 한 번에 처리할 수 있을까?
- C++ ODBC API에는 ODBC 드라이버가 한 번의 실행으로 여러 행을 처리할 수 있도록 해주는 매개변수 배열이 있다. 사용하는 방법은 아래와 같다.
1) 매개변수 집합 크기 지정
- SQLSetStmtAttr함수를 사용하여 SQL문에 사용될 매개변수 배열의 크기를 지정한다. 이때 SQL_ATTR_PARAMSET_SIZE와 SQL_ATTR_PARAMS_PROCESSED_PTR를 설정해야 하는데 각각의 의미는 아래와 같다.
- SQL_ATTR_PARAMSET_SIZE: 현재 SQL문에 대한 매개변수 집합의 수를 나타낸다
- SQL_ATTR_PARAMS_PROCESSED_PTR: 드라이버가 처리한 매개변수 집합의 수를 반환할 변수의 포인터를 설정
// 매개변수 집합의 크기를 5로 설정
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)5, 0);
// 처리된 매개변수 집합의 수를 저장할 변수 선언
SQLINTEGER nParamsProcessed;
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &nParamsProcessed, 0);
// 시나리오 1. 집합의 크기만큼 데이터를 채워서 SQL문 실행 -> 전부 성공
// => nParamsProcessed = 5
// 시나리오 1. 집합의 크기만큼 데이터를 채워서 SQL문 실행 -> 3개만 성공
// => nParamsProcessed = 3
2) 매개변수 바인딩
- 매개변수 집합 크기를 지정했다면 그 다음은 기존 방식과 동일하다. 매개변수를 바인딩하고 SQL문을 실행하면 된다.
- 처리한 행의 개수를 알고 싶다면 SQL_ATTR_PARAMS_PROCESSED_PTR을 통해 nParamsProcessed 변수를 확인하여 드라이버가 처리한 집합의 개수를 알 수 있다.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, paramArray1, 0, lengthArray1);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, paramArray2, 0, lengthArray2);
// ... 필요한 만큼 추가 파라미터 바인딩
SQL_ATTR_PARAMSET_SIZE와 실제 배열 크기는 반드시 같아야 된다.
- 실제 배열 크기가 더 작은 경우 → 메모리 접근 위반(Access Violation)
- 실제 배열 크기가 더 큰 경우 → 실제 배열에 넣은 데이터 중 뒷부분이 잘림
파라미터 배열을 사용해도 배열의 길이와 상관없이 각 매개변수에 대해 SQLBindParameter는 단 한 번씩만 바인딩하면 된다.
const int ARRAY_SIZE = 100;
// 1. 배열 크기 설정 (한 번)
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ARRAY_SIZE, 0);
// 2. 각 파라미터마다 한 번씩만 바인딩
std::vector<SQLINTEGER> ids(ARRAY_SIZE);
std::vector<SQLCHAR[50]> names(ARRAY_SIZE);
std::vector<SQLINTEGER> ages(ARRAY_SIZE);
// 파라미터 1: 이름 배열 (한 번만 바인딩)
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
50, 0, names.data(), 50, nullptr);
// 파라미터 2: 나이 배열 (한 번만 바인딩)
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
0, 0, ages.data(), 0, nullptr);
// 파라미터 3: ID 배열 (한 번만 바인딩)
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
0, 0, ids.data(), 0, nullptr);
// 3. 한 번의 실행으로 모든 배열 데이터 처리
SQLExecute(hstmt);
