::: 명령 과 준비 :::
이번 장에서는 ODBC를 이용해서 데이터소스에 연결하고 실제 명령을 수행하는
과정에 대하여 알아보도록 하겠다.
이전장에서, 데이터 소스에 어떻게 연결하는 지에 대해서 알아보았었다.
연결은 데이터소스와 여러분이 어떻게 연결되는지에 대한 정보를 선언한 것이다.
이러한 정보를 기초로 데이터소스에 여러가지 명령을 전달하고 결과값을 반환
받을 수 있는 것이다. 이러한 명령들은 반드시 "SQL"문으로 작성 되어야한다.
데이터를 검색/추가/제거/수정 등의 작업을 쿼리로 작성 할 수 있다.
이러한 명령을 수행하기 위해서는 다음과 같은 과정을 거친다.
1. 명령핸들 생성
2. SQL문 작성
3. 명령실행
4. 명령 해제
::: 명령핸들 생성 :::
SQLAllocHandle 프로시져를 호출해서 명령핸들을 생성할 수 있다.이 전장에서
설명했으므로, 간단한 코드 샘플을 살펴보자.
.data?
hStmt dd ?
.code
......
invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
::: SQL문 작성 :::
이 부분은 본인이 스스로 해결해야한다. SQL문은 어셈블리와는 상관없는 분야이다.
SQL 92 호환 문법을 한번 살펴보기 바란다. DDL / DML 등이 있다.
::: 명령실행 :::
명령을 실행하기 위해서는 크게 4가지 방법이 존재한다. 데이터베이스 엔진에서
어떻게 처리되는지에 따라 알맞은 프로시져를 사용하기 바란다.
Direct Execution | 프로그램에서 작성한 SQL문을 실행시에 직접 컴파일 되고 수행된다. |
Prepared Execution |
직접 실행되는 구조와 유사하지만, SQL문을 준비하는 과정과 실행 |
Procedures | 데이터 소스에 SQL문이 미리 준비되고 저장되어 있는 구조이다. 실행시에서는 미리저장된 프로시져를 호출 하기만 하면 된다. |
Catalog | ODBC드라이버에 미리 SQL문이 내장되어 있는 형태이다. 결과는 내정된 결과셋을 반환하게 되고, 데이터베이스이름과 테이블명 등을 알아낼 수 있다. 카다로그에는 데이터소스에서 알아낼 수 있는 모든것이 포함되어 있다. 실행시에는 단지 그 이름을 호출해서 사용하기만 하면 된다. |
상기의 4가지 방법은 각각 장단점을 가지고 있다. 직접실행방식은 SQL의 종류가 다르고
한번만 실행할 경우 편리하다. 준비실행은 처음실행할때는 속도가 늦지만, 같은
명령을 다시 실행할 경우는 속도가 빨라지는 장점을 가지고 있다. 동일한 SQL문을
반복적으로 사용할 경우에는 탁월한 선택이 될 것이다.프로시져는 가장 강력한 방법
이 될 수 있다. 모든 실행은 데이터소스에서 이루어져있고, 미리 준비가 되어있는
형태이므로 속도도 빠르고, 오류발생 빈도도 현저히 낮게 된다. 하지만, 모든 데이터
소스에서 프로시져가 지원되는것은 아니므로 알맞게 선택해야한다. 카다로그방식은
데이터소스의 모든정보를 미리 가지고 있기때문에 단지 사용하기만 하면 된다.
본 장에서는, 직접실행과 준비실행에 대해서 알아볼 것이다. 프로시져는 데이터베이스
매뉴얼을 살펴보기 바란다. 나중에 카다로그에 대해서도 잠시 알아볼 것이다.
::: 직접실행 :::
SQL문을 직접 데이터소스에게 실행하도록 지시하는 프로시져이다.
SQLExecDirect 프로시져를 호출하면 된다. 프로토타입은 다음과 같다.
SQLExecDirect proto StatementHandle:DWORD,
pStatementText:DWORD,
TextLength:DWORD
- StatementHandle. 사용하기 원하는 명령핸들을 지정한다.
- pStatementText. 실행하기 위한 SQL문이 저장된 곳의 포인터
- TextLength. SQL문의 길이를 지정(Sizeof 연산자 사용)
이 프로시져는 다음과 같은 값을 반환한다.
SQL_SUCCESS | 작업이 성공적으로 수행됨. |
SQL_SUCCESS_WITH_INFO | 치명적인 에러가 발생하지 않고, 경고정보를 포함하는 성공적인 수행시 반환. |
SQL_ERROR | 작업이 실패할 경우 반환. |
SQL_INVALID_HANDLE | 적절한 핸들을 사용하지 않았을 경우 반환됨. |
SQL_NEED_DATA | 만약에, SQL문에서 한가지 이상의 인자를 요구하고, 적절히전달하지 않을경우 본 반환값으로 알려주게 된다. 이러한 인자는 SQLParamData / SQLPutData등의 프로시져를 사용해서 추가로 제공할 수 있게 된다. |
SQL_NO_DATA | SQL문이 결과셋을 반환하지 않을 경우를 의미한다. 일반적인 쿼리문을 의미한다. 이값을 통해서 명령이 성공적으로 수행되었는지를 알 수 있다. Insert / Update / Delete 문등을 수행할 경우 사용할 수 있다. |
SQL_STILL_EXECUTING | 만약에 비동기적으로 명령을 수행할 경우에, SQLExecDirect 프로시져는 이값을 곧바로 반환해 주게 된다. 즉, 처리중이라는 의미로 해석 할 수 있다. 기본적으로 ODBC 드라이버는 동기적으로 실행된다. 멀티쓰레딩 OS에서는 비동기적으로 실행할 수 있기 때문에 이 반환값을 적절히 사용해서 사용할 수 있는 것이다. 이런 비동기적 실행은 SQLSetStmtAttr 프로시져를 참고하기 바란다. |
코드샘플
.data
SQLStmt db "select * from Sales",0
.data?
hStmt dd ?
.code
.....
invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
.if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
invoke SQLExecDirect, hStmt, addr SQLStmt, sizeof SQLStmt
::: 준비실행 :::
대부분의 SQL문은 크게 2단계로 나뉘어져 실행된다. 첫단계는 반드시 SQLPrepare 프로
시져를 호출해서 SQL문을 준비시켜야 한다.다음단계로, SQLExecute 프로시져를 수행
해서 실제 SQL문을 실행하게 되는 구조이다. 준비실행은 동일한 SQL문을 반복 수행할
경우에 유용하며, 결과적으로 빠른 응답속도를 얻게 될 것이다.
만약에, SQL문에서 파라미터를 사용할 경우 이방법은 아주 빠르게 실행될 것이다.
SQLExecute proto StatementHandle:DWORD
오직 한개의 인자만을 요구한다. 명령핸들만 지정해 주면 된다.
코드샘플
.data
SQLStmt db "select * from Sales",0
.data?
hStmt dd ?
.code
.....
invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
.if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
invoke SQLPrepare, hStmt, addr SQLStmt, sizeof SQLStmt
invoke SQLExecute, hStmt
혹시나, 직접실행보다 준비실행이 무엇이 더 장점이 되는지 궁금해 할 지도 모르겠다.
상기의 예제에서는 크게 느끼질 못할 것이다. SQL명령문에서 파라미터를 사용할 경우
그 위력이 나타나게 된다.
::: 명령 파라미터 :::
파라미터는 SQL문을 통해서 전달할 수 있는 매개변수를 의미한다. 예를 들어,
"employee" 라는 테이블은 3개의 필드를 가지고 있고, 필드명은 각각,
"name", "surname", TelephoneNo" 일 경우, Bob이란 사람의 전화번호를 찾기
위한 SQL문은 다음과 같을 것이다.
select telephoneNo from employee where name='Bob'
만약, 다른사람의 전화번호를 검색하려고 할경우에는 어떻게 해야할까? 파라미터가
없다면 별다른 방법 없이, name='Bob'부분에 원하는 이름을 설정하고 다시 SQL문을
실행해야 할 것이다.
이럴경우 파라미터를 사용하면 아주 간단히 해결된다. 상기의 예를 들면 'Bob'부분을
"?"로 대체하면 된다. 실제 SQL문은 다음과 같이 된다.
select telephoneNo from employee where name=?
어떻게 이것이 가능한지 잠시 알아보도록 하자.
ODBC드라이버는 이값을 어떻게 제대로 인식하는 것일까? 이것은 파라미터 마커라고
불리운다. 파라미터 바인딩이라는 기술을 이용해서 해결된다.( parameter binding)
간단히, 해당프로그램에 해당 파라미터 부분에 표식을 해두는 것이다. 상기의 예를
들면, 이름을 저장할 버퍼를 생성하고 ODBC드라이버에게 실제이름은 해당버퍼에
저장되어 있으니 그것을 사용하라고 말하는 것과 같은 것이다. 이러한 파라미터는
SQLFreeStmt 프로시저를 SQL_RESET_PARAMS 으로 설정하지 않는 이상 유효하게
처리되며, 상기의 프로시져를 호출하면 해제하게 된다.
변수와 파라미터를 바인딩할려면 SQLBindParameter 프로시져를 호출하면 된다.
SQLBindParameter proto StatementHandle:DWORD,
ParameterNumber:DWORD,
InputOutputType:DWORD,
ValueType:DWORD,
ParameterType:DWORD,
ColumnSize:DWORD,
DecimalDigits:DWORD,
ParameterValuePtr:DWORD,
BufferLength:DWORD,
pStrLenOrIndPtr:DWORD- StatementHandle 명령핸들을 지정한다.
- ParameterNumber 1부터 시작하는 파라미터의 번호를 지정한다.
(파라미터는 여러개를 사용할 수있음을 의미한다)ODBC드라이버는 이번호로
각각의 파라미터를 구분하게 된다. 예를 들어 3개의 파라미터를 사용했다면,
첫번째 파라미터의 번호는 1이 되며, 마지막 파라미터는 3이라는
값을가지게 되는 것이다. - InputOutputType 입/출력을 지정하는 용도의 플래그이다. 입력이라는 것은
ODBC드라이버에게 입력하는 값이고, 출력이라는 것은 파라미터의 결과값이
필요하다는 것을 ODBC드라이버에게 지시하는 것을 의미한다. 대부분의 경우
입력용으로 사용하게 된다. 출력용 파라미터는 프로시져에서 주로 사용한다.
지정가능한 값은 다음과 같은 것들이 있다.
SQL_PARAM_INPUT, SQL_PARAM_INPUT_OUTPUT, SQL_PARAM_OUTPUT - ValueType
사용하고 싶은 파라미터의 타입을 지정한다. 이값과 실제 ODBC의 값과 바인딩을
하게된다. 이값은 SQL_C_ 로 항상 시작하게 된다. - ParameterType
SQL파라미터의 타입을 지정한다. 예를 들어서 SQL파라미터가 텍스트필드일 경우
이 항목을 SQL_CHAR 으로 지정해야 한다. 전체 리스트는 MSDN을 참조해야한다 - ColumnSize
컬럼(필드)의 크기를 지정한다. 예제에서는 파라미터 마커를 name 에 지정했다.
해당칼럼은 20바이트 크기를 가지므로, 이값을 20 으로 지정해야 한다. - DecimalDigits 파라미터 마커의 자리수를 지정한다.
- ParameterValuePtr
파라미터 데이터가 저장되어 있는 버퍼의 포인터 - BufferLength
상기에서 사용한 버퍼의 크기를 지정한다. - pStrLenOrIndPtr
더블워드 크기로서 다음과 같은 값을 가진 포인터이다.- 파라미터값이 저장되어있는 버퍼의 포인터. 이값은 ParameterValuePtr 값이다.
이값은 파라미터의 타입이 문자열이거나 이진값일 경우에는 무시된다.
BufferLength화 혼동하지 말기 바란다. - SQL_NTS. NULL문자로 끝나는 문자열.
- SQL_NULL_DATA. 파라미터의 값이 NULL.
- SQL_DEFAULT_PARAM.
프로시져는 기본적으로 파라미터를 사용한다.기본값으로 저장프로시져를
사용할 경우 이값을 설정해서 사용한다. 자세한것은 MSDN을 참고하기
바란다.. - SQL_DATA_AT_EXEC.
The data for the parameter will be sent with SQLPutData.프로시져를 사용해서
데이터를 전송할 경우, 파일크기가 할당 메모리보다 클 경우, ODBC드라이버에게
알려주는 기능을 한다.
pStrLenOrIndPtr,에는 수많은 값이 올 수 있다. 하지만 대다수의 경우 처음과
세번째 옵션만 사용하고 나머지는 기본값으로 사용하므로, 걱정하지 말기 바란다.
- 파라미터값이 저장되어있는 버퍼의 포인터. 이값은 ParameterValuePtr 값이다.
코드샘플
.data
SQLString db "select telephoneNo from employee where name=?",0
Sample1 db "Bob",0
Sample2 db "Mary",0
.data?
buffer db 21 dup(?)
StrLen dd ?
.code
........
invoke SQLPrepare, hStmt, addr SQLString,sizeof SQLString
invoke SQLBindParameter, hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR, 20, 0, addr buffer, sizeof buffer, addr StrLen
;===========================================
; 처음실행
;===========================================
invoke lstrcpy, addr buffer, addr Sample1
mov StrLen, sizeof Sample1
invoke SQLExecute, hStmt
;===========================================
; 두번째 실행
;===========================================
invoke lstrcpy, addr buffer, addr Sample2
mov StrLen, sizeof Sample2
invoke SQLExecute, hStmt
SQLExecute 프로시져를 여러번 수행하고 있다.하지만, SQLPrepare 프로시져를 다시
호출하지는 않았다. 왜냐하면 ODBC드라이버는 파라미터의 값을 어디에서 찾을지를
이미 알고 있기 때문이고 이것은 SQLBindParameter 프로시져를 통해서 알게된다.
현재 예제에서는 결과셋을 무시했었다. 하지만 다음 장에서 이 결과셋(Result Set)을
사용하는 방법을 배우게 될 것이다.
새로운 명령을 위해 새로운 명령핸들을 할당하지는 말기 바란다 SQLFreeStmt
프로시져를SQL_UNBIND / SQL_RESET_PARAMS.옵션으로 언바운드 하기 바란다.
이것은 다음 SQL명령을 위해서 현재 핸들을 다시 사용할 수 있게 해준다.
::: 명령핸들 해제 :::
SQLFreeHandle.프로시져를 호출하므로써, 명령핸들은 해제 된다.