열 조회
이전 포스팅에서는 직접 변수 타입을 지정해 테이블의
열 타입과 바인딩해서 쿼리문을 사용했다.
확장성을 위해 초기에 테이블을 파싱을 해서 어떤 열 정보를 갖고 있는지
파악 후, 열의 타입을 알아내 변수를 바인딩하는 작업이 바람직하다.
그러기 위해서 SQLDescribeCol 함수가 있다.
그전에 열 정보를 얻기 위해서는 해당 열의 인덱스가 필요하므로
SQLNumResultCols() 함수로 열의 개수를 반환받는다.
1 2 3 | SQLRETURN SQLNumResultCols( SQLHSTMT StatementHandle, //명령핸들 SQLSMALLINT * ColumnCountPtr); // 반환 버퍼 포인터 | cs |
총 열의 개수를 알아냈으니 열 인덱스에 맞게 열의 정보를 얻어오면 된다.
1 2 3 4 5 6 7 8 | //열 총 사이즈 SQLNumResultCols(handle_stmt, &총 열수); for (int 열위치 = 1; 열위치<= 총 열수; 열위치++) { SQLDescribeCol(열위치) } | cs |
위 방식으로 조회하면 된다. (의사 코드)
SQLDescribeCol
Select문에 의해 생성된 결과로 표시된 열에 대한 결과, 정보를 반환한다.
중요한 것은 이 SQLDescribeCol를 호출하기 전에 SQLPrepare(),
또는 SQLExecDirect() 함수를 호출해야 된다는 것이다.
1 2 3 4 5 6 7 8 9 10 | cㅊSQLRETURN SQLDescribeCol( SQLHSTMT StatementHandle, //명령문 핸들 SQLUSMALLINT ColumnNumber, //1부터 시작하는 데이터의 열수 SQLCHAR * ColumnName, //null로 끝나는 열 이름 버퍼 SQLSMALLINT BufferLength,//버퍼, 문자의 길이 SQLSMALLINT * NameLengthPtr,//문자 수를 반환할 버퍼의 포인터 SQLSMALLINT * DataTypePtr, //데이터 형식 반환 포인터 SQLULEN * ColumnSizePtr, // 열 크기 포인터 SQLSMALLINT * DecimalDigitsPtr, //열의 소수 자릿수 포인터 SQLSMALLINT * NullablePtr); //출력 열이 Null 허용 여부 | cs |
명령 핸들과 포인터를 매개변수로 열에 대한 여러 정보를 반환해준다.
이러한 정보와 갖은 내용의 구조체를 만들어서
그 구조체를 vector로 저장하도록 했다.
데이터베이스의 인덱스는 0이 아닌 1부터 시작한다. 반복문도
이 점을 유념해서 사용해야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | bool KODBC::Execute_TableSet(const TCHAR* tablename) { Table_Info table; table.table_name = tablename; std::wstring statement = L"select * from "; statement += tablename; SQLRETURN ret = SQLExecDirect(handle_stmt, (SQLTCHAR*)statement.c_str(), SQL_NTS); if (ret != SQL_SUCCESS) { Check(); return false; } //결과집합의 콜 열수를 반환함 SQLNumResultCols(handle_stmt, &table.table_col_num); //데이터 베이스는 행이 0이 아닌 1부터 시작함 for (int icol = 1; icol <= table.table_col_num; icol++) { //열 정보 구조체 생성 Table_ColumnInfo column; //행 수 column.col_num = icol; //열 이름 만큼 사이즈 int col_size = _countof(column.col_name); /*SQLDescribeCol 결과 집합의 한 열에 대해 결과 설명자- 열 이름, 형식, 열 크기, 10 진수 숫자 및 null 허용 여부를 반환 합니다. */ SQLDescribeCol(handle_stmt, icol, column.col_name, col_size, &column.col_name_ptr, &column.col_data_type,// 데이터형 &column.col_size_ptr, &column.col_decimal_ptr, // 10진수 자리수 &column.col_nullable);// NULL 허용여부 table.table_data.push_back(column); } } | cs |
이렇게 해서 ODBC의 SQLDescribeCol() 를 사용해
모든 데이터베이스의 테이블의 열의 정보를 알아내는 방법을 알아보았다.
왜 이 함수를 사용하냐면,
Select와 같은 sql 쿼리문 명령어를 사용할 때 필요한 변수 타입 바인딩을
사전에 알아낸 열의 정보를 활용해 바인딩을 한다면,
추후에 변경사항이 있어도 맞는 변수 타입으로 바인딩해주는,
유지보수성이 있는 프로그래밍이 가능하다.
아래는 C타입 데이터 타입과 SQL 데이터 타입 비교표이다.
SQL 타입 ID
|
SQL 데이터 타입 | C 타입 ID | ODBC C typedef | C 데이터 타입 |
SQL_CHAR | CHAR(n) | SQL_C_CHAR | SQLCHAR * | unsigned char * |
SQL_VARCHAR | VARCHAR(n) | SQL_C_CHAR | ||
SQL_WCHAR | WCHAR(n) | SQL_C_WCHAR | SQLWCHAR * | wchar_t * |
SQL_WVARCHAR | VARWCHAR(n) | SQL_C_WCHAR | ||
SQL_C_TCHAR | SQLTCHAR * | |||
SQL_BIGINT | BIGINT | SQL_C_SBIGINT | SQLBIGINT | _ _int64 |
SQL_INTEGER | INTEGER | SQL_C_SLONG | SQLINTEGER | long int |
SQL_SMALLINT | SMALLINT | SQL_C_SSHORT | SQLSMALLINT | short int |
SQL_TINYINT | TINYINT | SQL_C_UTINYINT | SQLCHAR | unsigned char |
SQL_REAL | REAL | SQL_C_FLOAT | SQLREAL | float |
SQL_FLOAT | FLOAT(p) | SQL_C_DOUBLE | SQLFLOAT | double |
SQL_DOUBLE | DOUBLE | SQLDOUBLE | ||
SQL_BINARY | BINARY(n) | SQL_C_BINARY | SQLCHAR * | unsigned char * |
SQL_VARBINARY | VARBINARY(n) | SQL_C_BINARY | ||
SQL_TYPE_DATE | DATE | SQL_C_TYPE_DATE | SQL_DATE_STRUCT | struct tagDATE_STRUCT |
SQL_TYPE_TIME | TIME(p) | SQL_C_TYPE_TIME | SQL_TIME_STRUCT | struct tagTIME_STRUCT |
SQL_TYPE_TIMESTAMP | TIMESTAMP(p) | SQL_C_TYPE_TIMESTAMP | SQL_TIMESTAMP_STRUCT | struct tagTIMESTAMP_STRUCT |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | } //변수 타입 바인딩 SQLLEN lTemp = 0; TCHAR szData[100][21] = { 0, }; int iData[100]; Table_Record record_data; //모든 열만큼 돌면서 변수 바인딩 for (int iBind = 0; iBind < table.table_data.size(); iBind++) { switch (table.table_data[iBind].col_data_type) { case SQL_WCHAR://문자열 타입일때, case SQL_WVARCHAR: { Table_Field data; data.field_data_type = SQL_UNICODE; ret = SQLBindCol(handle_stmt, iBind + 1, SQL_UNICODE, szData[iBind], sizeof(szData[iBind]), &lTemp); if (ret != SQL_SUCCESS) { Check(); return false; } record_data.record.push_back(data); }break; case SQL_INTEGER: { Table_Field data; data.field_data_type = SQL_INTEGER; ret = SQLBindCol(handle_stmt, iBind + 1, SQL_INTEGER, &iData[iBind], 0, &lTemp); if (ret != SQL_SUCCESS) { Check(); return false; } record_data.record.push_back(data); }break; case SQL_REAL: { Table_Field data; data.field_data_type = SQL_C_FLOAT; ret = SQLBindCol(handle_stmt, iBind + 1, SQL_C_FLOAT, &iData[iBind], 0, &lTemp); if (ret != SQL_SUCCESS) { Check(); return false; } record_data.record.push_back(data); }; } } std::cout << "" << std::endl; } | cs |
문자, 정수, 실수 타입를 스위치문을 사용해
맞는 타입으로 바인딩 후에 벡터에 저장하는 모습이다.