C++ ODBC로 DBMS에 연결하기 - 기본 : How to Connect to DBMS with C++ ODBC - Basic
떡 먹고 싶네요. 팥죽 끼얹은 팥앙떡...
이 글은 C++로 ODBC를 이용해 DBMS에 연결하는 과정을 다루고 있습니다. 혹시 다른 언어를 사용하더라도 이 글을 읽으면 ODBC를 통한 연결이 어떤 과정으로 이뤄지는지 대략적으로 알게되리라 생각합니다.
혹시 쉽고 빠르게 ODBC를 이용하고자 하는 분은 C#을 고려하는 것도 괜찮을 것 같습니다. C++은 ODBC를 사용하려면 이래저래 설정해야하는데 C#은 비교적 간단하게 구현할 수 있는 것으로 알고 있습니다. 혹시 사용해보신 분은 어떤지 알려주세요 <3
ODBC는 Open DataBase Connectivity의 약자로, DBMS에 연결하기 위한 소프트웨어의 표준 규격입니다. 표준 규격이기 때문에 ODBC에 정해진 순서를 따르면 연결하려는 DBMS에 크게 구애받지 않고 코딩을 할 수 있습니다.
< ODBC의 위치 > |
일단 DBMS는 PostgreSQL을 사용할 예정입니다만, 같은 코드로 MSSQL이나 MySQL 등 다른 DBMS에도 연결할 수 있으니 사용 언어가 다른 것이 아니라면 그냥 따라오셔도 괜찮습니다.
----- ODBC용 클래스 -----
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <windows.h>
#include <sqlext.h>
#include <stdio.h>
class PODBC {
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLRETURN retcode;
// For specific error
SQLSMALLINT length;
SQLINTEGER rec = 0, native;
SQLCHAR state[7], message[256];
public:
void AllocateHandles();
void ConnectDataSource();
void ExecuteStatementDirect(SQLCHAR* sql);
void PrepareStatement(SQLCHAR* sql);
void ExecuteStatement();
void RetrieveResult();
void DisconnectDataSource();
};
| cs |
ODBC를 이용하기 위해 정의할 클래스입니다. PODBC의 P는 PostgreSQL의 약자입니다.
정의된 멤버 변수를 보면 'SQLH~'와 같은 형식의 데이터 타입을 볼 수 있습니다. 여기서 SQL은 그 SQL이 맞고, H는 핸들(Handle)을 의미합니다. ODBC와 관련된 것을 다룰 때 이 핸들을 사용하게 됩니다. SQLRETURN 타입으로 선언된 변수는 ODBC 메소드를 호출했을때 반환하는 결과값을 저장합니다.
그 아래는 에러가 났을 경우, 원인을 밝혀내기 위해 필요한 변수입니다. ODBC 메소드를 호출해 message에 에러 발생 이유를 받아옵니다.
- AllocateHandles()는 ODBC 핸들 변수를 초기화해주는 함수입니다.
- ConnectDataSource()는 연결할 DBMS에 접속하는 함수입니다.
- ExecuteStatementDirect()는 쿼리문을 인자로 받아 쿼리문 준비 과정없이 실행시키는 함수입니다.
- PrepareStatement()는 쿼리문을 실행시키기 위해 따로 준비 과정을 거치는 함수입니다. 준비 과정을 거치면 한 번 실행시킨 쿼리문을 다시 실행시킬 수 있기 때문에 여러 번 사용할 경우라면 준비 과정을 거치는 것이 좋습니다.
- ExecuteStatement()는 실행 준비된 쿼리문을 실행시키는 함수입니다.
- RetrieveResult()는 쿼리문의 결과를 읽는 함수입니다. 예제용 테이블을 하나 만들고 그에 맞춰 코드를 작성했기 때문에 자신에게 맞춰 조금씩 수정해 사용해야합니다. 조금 더 자세한 내용은 아래에서 설명합니다.
- DisconnectDataSource()는 할당했던 핸들을 모두 해제하는 함수입니다.
대충 어떻게 흘러가는지 짐작이 가나요? 여기서 더 구현하기 위해선 먼저 설정해야할 것이 2가지 있습니다.
----- 설정 1, 예제용 테이블 -----
1
2
3
4
5
|
CREATE TABLE choco (
choco_id INTEGER,
choco_name CHAR(32),
choco_cal FLOAT
)
| cs |
이제 예제 테이블을 하나 만들어줍니다. 이름은 어느 것이든 괜찮습니다. 저는 간단하게 컬럼 세 개만 넣었습니다. 그리고 값도 넣어주도록 합니다.
1;"Ghana";128
2;"Pream";128.6
3;"Ghool";1241.1
4;"Amawd";1235.112
5;"Qniap";10505.1
6;"Opoew";9510
7;"Uupre";16043
8;"AMDQW";2798
9;"AMGPVO";34662
10;"DSPFB";2568
11;"SRBPMSR";8445
12;"Mapdom";73547
13;"sdmafa";24573457
14;"MASPa";35678
15;"asdfa";24567
16;"amsdop";26588625
17;"Qmapd";24578
18;"MXodmf";967567
19;"PMDas";45683
20;"amPSO";476582
21;"sdpgmds";45687.458
22;"DSPFOmdf";35.68967
23;"DFpdmf";9780
24;"DFmpod57";8945
25;"dsfompFD683";61.58
26;"dfDMPDS5";84769
27;"dfspm46";71.139
28;"DMFPO";467963
29;"dsfopmop";46879
30;"QPOF";46.121213
31;"q[qfEF74";9467
32;"wmpmogE7946";7943232
122;"ASD";213
153;"asd";645
1234;"asdQ";5274
999;"421";124.1
| cs |
먼저, 위 내용을 텍스트 파일로 저장해주세요. 혹시 값을 한 두개만 넣어도 괜찮다면 굳이 텍스트 파일로 만들 필요 없이 바로 아래 쿼리문을 던져주면 됩니다.
1
|
INSERT INTO 테이블_이름 VALUES (1, "SWEET THING", 999.999);
| cs |
그렇지만 값이 많은 쪽이 재밌으니까 저는 모두 넣겠습니다. 텍스트 파일로 만들었다면 아래 쿼리문을 던져주세요.
1
|
COPY 테이블_이름 FROM '파일_경로' (DELIMITER(';'));
| cs |
사실 큰따옴표를 적어놓으면 문자열로 인식돼서 테이블에 같이 들어가게 됩니다만,, 크게 상관없으니 넘어가도록 합니다.
그 사실 ODBC를 사용하려는 사람이라면 이 정도 쿼리문은 알 것이란 생각이 들지만 일단 적었습니다..
----- 설정 2, DSN 설정 -----
테이블 준비는 끝났고, 이제 ODBC 관리자로 DSN을 생성할 차례입니다. DSN은 Data Source Name의 약자로, 이 예제의 경우 연결할 DBMS, 즉 PostgreSQL의 정보 집합을 대표하는 이름으로 보면 됩니다. 해보는게 이해가 더 빠를 것이라 생각합니다.
https://www.postgresql.org/ftp/odbc/versions/
위 링크를 타면 PostgreSQL ODBC 드라이버를 다운로드 받을 수 있습니다. README 파일에 각 폴더에 대한 정보가 적혀 있으니 참고하면 됩니다. 참고로 저는 msi 폴더에 있는 파일을 다운로드 받았습니다. Windows 사용자는 보통 이 폴더로 갑니다.
설치가 끝나면 ODBC 관리자를 실행시킵니다.
< ODBC 관리자 > |
저는 이전에도 몇 번 사용해서 이것저것 있습니다.
- 사용자 DSN은 추가시 현재 사용자(User)만 사용 가능합니다.
- 시스템 DSN은 추가시 모든 사용자가 사용 가능합니다.
- 파일 DSN은 추가시 모든 사용자가 사용 가능하며, DSN 정보를 컴퓨터에 파일로 저장합니다.
전 굳이 시스템 DSN으로 할 필요가 없어서 사용자 DSN을 사용할 예정입니다. 과정은 모두 같으니 원하는 대로 설정하시면 됩니다. 추가 버튼을 눌러줍니다.
< ODBC DSN 추가 창 > |
아까 다운받은 PostgreSQL의 ODBC 드라이버를 확인할 수 있습니다. ANSI와 UNICODE 중 적절한 인코딩을 선택하고 마침을 눌러주세요.
< ODBC PostgreSQL DSN 추가 창 > |
- Data Source 항목에는 Data Source의 이름을 적어줍니다. 후에 이 이름을 이용해 DBMS에 접근합니다. 저는 'postgres'라고 적었습니다.
- Description 항목에는 이 DSN에 대한 설명을 적어줍니다. 딱히 적지 않아도 무관하므로 저는 넘기겠습니다.
- Database 항목에는 기본적으로 사용할 데이터베이스의 이름을 적어줍니다. 여기서 데이터베이스란 DBMS에서 'CREATE DATABASE ~'나 'USE ~'와 같은 쿼리문을 던질 때 의미하는 데이터베이스를 말합니다. 저는 'test'라는 이름의 데이터베이스를 사용하기 때문에 'test'라고 적었습니다.
- SSL Mode 항목은 SSL 통신 사용의 여부입니다. 일단 저는 사용해본적이 없기 때문에 disable로 했습니다. 나중에 기회가 된다면 SSL Mode도 다뤄볼 예정입니다.
- Server 항목에는 DBMS의 서버 주소를 적어줍니다. 저는 로컬에서 작업하기 때문에 'localhost'라고 적었습니다.
- Port 항목에는 PostgreSQL이 사용 중인 포트 번호를 적어줍니다. 몇 번을 사용하는지 모르시는 분은 pgAdmin에서 Server Groups > Servers > 자신이 사용 중인 서버를 클릭하고 Properties 탭을 확인하면 알 수 있습니다. 저는 PostgreSQL의 기본 포트인 '5432' 를 적었습니다.
- User Name 항목에는 PostgreSQL 서버에 접속할 때 사용하는 사용자 이름을 적어줍니다.
- Password 항목에는 PostgreSQL 서버에 접속할 때 사용하는 비밀번호를 적어줍니다.
< ODBC PostgreSQL DSN 추가 창> |
입력을 끝낸 모습입니다. 이제 Test 버튼을 눌러 제대로 값을 입력했는지 확인해봅니다.
< ODBC PostgreSQL DSN 연결 성공 창> |
올바른 값을 입력했다면 이렇게 성공 알림창이 뜹니다. 제대로 입력했으니 확인과 Save 버튼을 차례대로 누르고 DSN 추가를 완료해줍니다.
< DSN 추가 후 ODBC 관리자 > |
PostgreSQL용 DSN이 생긴 것을 확인할 수 있습니다.
----- DBMS 연결 -----
예제용 테이블도 만들었고, DSN도 추가했으니 이제야말로 ODBC를 이용해서 DBMS에 연결할 차례입니다. 아까 PODBC 클래스부터 기억을 이으시면 됩니다.
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
|
void PODBC::AllocateHandles() {
// 환경 핸들러 할당
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
// ODBC 드라이버 버전 명시
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
// 연결 핸들러 할당
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
printf("Allocate Success\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
else {
SQLGetDiagRec(SQL_HANDLE_ENV, henv, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
else {
SQLGetDiagRec(SQL_HANDLE_ENV, henv, rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
| cs |
먼저, 핸들을 할당하는 함수입니다.
ODBC를 사용하면서 다루게 될 핸들은 환경 핸들(Environment Handle), 연결 핸들(Connection Handle), 명령 핸들(Statement Handle)이 대표적입니다.
환경 핸들은 ODBC 환경 설정 값과 관련된 정보를, 연결 핸들은 DBMS 연결 정보를, 명령 핸들은 쿼리 관련 정보를 저장합니다.
이 함수에선 환경 핸들과 연결 핸들만 할당하고, 명령 핸들은 뒤에 쿼리문을 던지거나 준비할 때 할당할 예정입니다.
핵심 코드는 주황색으로 밑줄을 친 부분입니다.
1
2
|
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
| cs |
SQLAllocHandle 함수를 통해 핸들을 할당할 수 있습니다.
1
|
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
| cs |
SQLSetEnvAttr는 환경 값을 설정하는 함수로, 다른 값은 상관이 없지만 ODBC 드라이버 버전은 반드시 명시해주어야 합니다. ODBC는 2.x와 3.x로 나뉘는데, 이 둘 사이에 차이점이 있기 때문입니다. 자세한 내용은 아래 링크를 타시면 확인할 수 있습니다.
1
|
SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, ++rec, state, &native, message, sizeof(message), &length);
| cs |
SQLGetDiagRec 함수를 통해 함수가 'SQL_SUCCESS'가 아닌 다른 값을 반환했을 경우 그 원인을 알 수 있습니다.
--
1
2
3
4
|
void PODBC::ConnectDataSource() {
// 32-bit, 64-bit 주의
retcode = SQLConnect(hdbc, (SQLCHAR*)"DSN", SQL_NTS, (SQLCHAR*)"사용자_이름", SQL_NTS, (SQLCHAR*)"비밀번호", SQL_NTS);
}
| cs |
DBMS에 연결하는 함수입니다.
간단한 예제라 ODBC 메소드 하나만 호출하고 끝납니다. 보통은 환경 파일(.ini)에 정보를 저장하고 거기서 값을 읽어오지만, 이 코드는 예제용이므로 직접 작성해두었습니다.
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
void PODBC::ExecuteStatementDirect(SQLCHAR* sql) {
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
printf("Connect Success\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
retcode = SQLExecDirect(hstmt, sql, SQL_NTS);
if (retcode == SQL_SUCCESS) {
printf("Query Seuccess\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
| cs |
쿼리문을 인자로 받고, 준비 과정없이 바로 실행시키는 함수입니다.
앞서 말한대로, 쿼리문을 바로 실행시키는 함수인 SQLExecDirect 함수를 호출하기 전에 명령 핸들을 할당하는 것을 볼 수 있습니다.
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
void PODBC::PrepareStatement(SQLCHAR* sql) {
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
printf("Connect Success\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
retcode = SQLPrepare(hstmt, sql, SQL_NTS);
if (retcode == SQL_SUCCESS) {
printf("\nQuery Prepare Success\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, ++rec, state, &native, message, sizeof(message), &length);
printf("\n%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
| cs |
쿼리를 준비시키는 함수입니다
SQLExecDirect 함수를 호출한 다음 쿼리문을 준비할 일은 없기 때문에 여기서도 다시 명령 핸들을 할당해줍니다.
1
|
retcode = SQLPrepare(hstmt, sql, SQL_NTS);
| cs |
SQLPrepare 함수를 통해 쿼리문을 준비시킵니다.
--
1
2
3
4
5
6
7
8
9
10
11
|
void PODBC::ExecuteStatement() {
retcode = SQLExecute(hstmt);
if (retcode == SQL_SUCCESS) {
printf("Query Execute Success\n");
}
else {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
}
}
| cs |
준비 과정을 거친 쿼리문을 실행시키는 함수입니다.
SQLExecuteDirect가 아닌 SQLExecute 함수를 호출해 쿼리문을 실행시킵니다.
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
void PODBC::RetrieveResult() {
short choco_id;
char choco_name[16];
float choco_cal;
SQLLEN cid, cna, cal;
SQLBindCol(hstmt, 1, SQL_C_SHORT, &choco_id, sizeof(choco_id), &cid);
SQLBindCol(hstmt, 2, SQL_C_CHAR, &choco_name, sizeof(choco_name), &cna);
SQLBindCol(hstmt, 3, SQL_C_FLOAT, &choco_cal, sizeof(choco_cal), &cal);
printf("id\tname\t\tcal\t\tstars");
do {
retcode = SQLFetch(hstmt);
printf("\n%d\t%s\t%f", choco_id, choco_name, choco_cal);
} while (retcode != SQL_NO_DATA);
SQLFreeStmt(hstmt, SQL_UNBIND);
}
| cs |
실행한 쿼리문의 결과를 검색하는 함수입니다.
SELECT한 값을 받아오기 위해선 (제가 아는 한에선) 두 가지 방법이 있습니다. 첫 번째는 위와 같이 각 컬럼별로 변수를 선언한 다음 바인딩해 값을 받아오거나, 커서를 선언해 (여러 행을 블록으로) 받아오는 방식이 있습니다. 커서를 사용하는 방법은 다른 글에서 다룰 예정입니다.
1
|
SQLBindCol(hstmt, 1, SQL_C_SHORT, &choco_id, sizeof(choco_id), &cid);
| cs |
SQLBindCol은 컬럼의 값을 변수에 받아오기 위해 변수를 바인딩하는 함수입니다.
값을 제대로 받아오려면 PostgreSQL 컬럼의 데이터 타입과 C++ 변수의 데이터 타입이 호환되어야 합니다. 이와 관련된 데이터 타입 매핑 표를 아래 링크를 타면 볼 수 있으니 참고하시기 바랍니다.
세 번째 인자로 주는 값은 ODBC 데이터 타입인데, 아래 링크를 타면 PostgreSQL과 ODBC의 데이터 타입 매핑 표가 있으니 참고하시기 바랍니다.
1
|
retcode = SQLFetch(hstmt);
| cs |
쿼리 결과 값을 읽어오는 함수입니다.
한 커서 스크롤이 아니라면 한 줄씩 읽어오게 되고, 각 컬럼의 값은 바인딩했던 변수에 저장됩니다.
함수를 호출할 때마다 다음 로우를 읽어오며, 마지막 로우까지 읽은 상태에서 한 번 더 호출한다면 'SQL_NO_DATA'를 반환합니다.
1
|
SQLFreeStmt(hstmt, SQL_UNBIND);
| cs |
명령문 관련 정보를 해제하는 함수입니다.
모든 결과 값을 받아왔으므로 더 이상 남겨둘 필요가 없습니다. 해제하도록 합니다.
두 번째 인자는 옵션으로 SQL_CLOSE, SQL_DROP, SQL_UNBIND, SQL_RESET_PARAMS가 있습니다.
- SQL_CLOSE는 현재 명령 핸들에 저장된 정보와 관련된 커서를 닫습니다. 이 대신 SQLCloseCursor 함수를 사용해도 커서를 닫을 수 있습니다.
- SQL_DROP은 이제 더 이상 지원하지 않는(deprecated) 옵션입니다. 이 옵션 대신 SQLFreeHandle을 사용할 수 있습니다.
- SQL_UNBIND는 SQLBindCol 함수로 바인딩했던 버퍼(변수)를 모두 해제(releasing)합니다.
- SQL_RESET_PARAMS는 SQLBindParameter 함수로 바인딩했던 파라미터를 모두 해제합니다. 파라미터 바인딩은 쿼리문에 값 대신 '?'를 넣고 명령문을 실행할 때 변수에 있는 값으로 대체할 수 있도록 하는 과정입니다. 후에 다른 글에서 다룰 예정입니다.
이 코드로는 아까 만든 choco 테이블이나 그와 같은 구조의
테이블을 SELECT하는 쿼리문밖에 처리하지 못합니다. 쿼리문을 더 유연하게 처리하고 싶다면, 먼저 쿼리문에서 SELECT 할 컬럼 목록과 데이터 타입을 뽑아내고, 이에 따라 변수를 선언해서 바인딩한 뒤, 값을 받아오는 식으로 해야합니다. 아니면... 후에 작성할 글에서 다룰 예정이니 참고하시기 바랍니다.
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void PODBC::DisconnectDataSource() {
if (hstmt) {
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
hstmt = NULL;
}
SQLDisconnect(hdbc);
if (hdbc) {
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
hdbc = NULL;
}
if (henv) {
SQLFreeHandle(SQL_HANDLE_ENV, henv);
henv = NULL;
}
}
| cs |
할당했던 핸들을 모두 해제하는 함수입니다.
앞서 호출했던 SQLFreeStmt 함수는 핸들을 해제하는 것이 아니기 때문에 여기서 해제해주어야 합니다.
1
|
SQLDisconnect(hdbc);
| cs |
이 함수는 연결을 해제하는 함수입니다. 연결 핸들을 해제하기 전에 호출해서 연결을 먼저 해제하도록 합니다.
----- 코드 실행 -----
필요한 함수를 모두 작성했으니 이제 테스트를 해봅시다.
1
2
3
4
5
6
7
8
9
|
int main() {
PODBC podbc;
podbc.AllocateHandles();
podbc.ConnectDataSource();
podbc.ExecuteStatementDirect((SQLCHAR*)"SELECT * FROM choco");
podbc.RetrieveResult();
podbc.DisconnectDataSource();
}
| cs |
PODBC 객체를 생성하고 선언했던 함수를 순서대로 호출했습니다.
< ODBC 연결 결과 > |
아까 테이블에 넣었던 값이 출력되는 것을 볼 수 있습니다. 아래로 값이 몇 개 더 있지만 길이상 잘렸습니다.
코드를 실행시킬 때 솔루션 플랫폼을 주의해야 합니다. 만약 ODBC 관리자 (32비트)로 DSN을 생성했다면 x86 플랫폼을, ODBC 관리자 (64비트)로 DSN을 생성했다면 x64 플랫폼을 선택하셔야 합니다... 너무 당연했나요?
< 솔루션 플랫폼 > |
위 사진에서 가운데 x64라고 써있는 부분이 솔루션 플랫폼입니다.
----------
ODBC 연결의 기본은 이렇습니다. 잘 따라오셨는지요..?
다른 DBMS를 원한다면 코드는 그대로 두고, ODBC 드라이버만 그에 해당하는 것으로 설치한 뒤 DSN을 추가하시면 됩니다.
혹시 글이 어려우셨나요? 아니면 내용에 오류가 있나요? 코드에 버그가 있나요? 부족한 부분이 있나요? 댓글로 꼭 피드백을 남겨주세요. 더 나은 글을 작성하는데 밑거름이 됩니다.
- JSeol 올림
On Writing...
사용자 DSN에서 Server, Database, User Name, Password, Port
답글삭제모두 제대로 입력한거 같고 방화벽이랑 인바운드 설정까지 다한거 같은데
아래와 같은 오류 문구가 나타나네요ㅠ 어떻게 해결하는 것이 좋을까요?ㅠ
server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.