09 Static VS. Dynamic SQL

(1) Static SQL

1. String 형 변수에 쿼리 문장을 담지 않고 코드 사이에 직접 기술 다른 말로 'Embedded SQL' 이라고도 한다.
2. Pre컴파일러가 PreCompile 과정 에서 Static SQL을 발견 하면 함수를 호출 할 수 있는 코드로 변환함.
3. 런타임 시에 절대 변하지 않아 PreCompile 단계에서 구문 분석, 유효 오브젝트 여부, 오브젝트 액세스 권한 등을 체크하는 것이 가능


<예제>
void main(){ 
   printf("사번을 입력하시오 : ");
   scanf("%d"  ,&empno);                //콘솔로 사번 입력
   EXEC SQL WHENEVER NOT FOUND GOTO notfound;
   EXEC SQL SELECT ENAME INTO :ename    //변수에 담지 않고 바로 기술
              FROM EMP
            WHERE EMPNO = :empno;
   printf("사원명 : %s.\n",ename);
notfound:
   printf("%d는 존재하지 않는 사번입니다.\n",empno);
}

-> SQL문을 String변수에 담지 않고 예약어처럼 코드 사이에 섞어서 기술하고 있다.

(2) Dynamic SQL

1. String 변수에 담아서 처리하는 SQL문 즉 변수를 사용 하므로서 쿼리문을 동적으로 바꿀수 있으며 런타임시에 사용자가 SQL에 대해서 입력(전체 입력 및 변경)이 가능
2. 1번의 이유로 PreCompiler시 문법에 체크(Syntax)/권한체크(Semantics) 가 불가능
3. Semeantic 체크는 DB 접속을 통해 이루어지지만 Syntanx 체크는 PreCompiler에 내장된 SQL파서를 이용한다.
이때, 스칼라 서브쿼리, 분석함수, ANSI 조인문 등을 사용하면 PreCompiler가 그것들을 인식하지 못해 에러가 난다.
=> Dynamic SQL을 이용해 해결가능하다.


$ proc test.pc sqlcheck=syntax : success
$ proc test.pc sqlcheck=full userid=scott/tiger : error

Pro*C 예제


void main(){ 
	char selectId[50] = "SELECT * FROM EMP WHERE EMPNO = :empno";//바인드 변수
	EXEC SQL PREPARE sql_stmt FROM :selectId ; /* SQL 문장을 정의 */

	EXEC SQL DECLARE emp_cursor CURSOR SQL sql_stmt ; /* 커서 선언 */
	
	EXEC OPEN emp_cursor USING :empno ; /* 커서 열기 : 실제 실행 단계가 아님 */
	
	EXEC FETCH emp_cursor INTO :ename ; /* 쿼리문 실행 */
	
	EXEC CLOSE emp_cursor ; /* 커서 닫기 : 닫지 않을 경우 비정상적 오류 발생 */
	Printf("사원명 : %s.\n", ename);
}

Pro*C에서 제공하는 Dynamic Method 요약


Method 1. 입력 Host변수 없는 Non-Query(SELECT 문 제외)
  1) DELETE FROM EMP WHERE EMPNO=20
  2) GRANT SELECT ON EMP TO scott

Method 2. Host 변수가 고정적일 경우 Non-Query(SELECT 문 제외)
  1) INSERT INTO EMP(EMPNO, JOB) VALUES(:empno, :job);
  2) DELETE FROM EMP WHERE EMPNO = :empno

Method 3.select-list의 컬럼 갯수와 Host변수가 고정적일때
  1) SELECT DEPTNO, MAX(SAL) FROM EMP GROUP BY DEPTNO; --컬럼의 갯수가 고정적
  2) SELECT DNAME, LOC FROM DEPT WHERE DEPTNO=20;      --컬럼의 갯수가 고정적
  3) SELECT ENAME, EMPNO FROM EMP WHERE DEPTNO=:deptno --컬럼의 갯수 및 Host 변수가 고정적

Method 4. select-list의 컬럼의 갯수와 Host 변수가 가변적
  1) INSERT INTO EMP(....) VALUES(....);
  1) SELECT .... FROM EMP WHERE DEPTNO=:deptno; 

(3) 일반프로그램 언어에서 SQL작성 방법

JAVA


public void preCursorCaching(int cnt)throws Exception{
	((OracleConnection)conn).setStatementCacheSize(1);
	((OracleConnection)conn).setImplicitCachingEnabled(true);
	
	for( int i = 0; i < cnt; i ++){
                PreparedStatement pstmt = conn.prepareStatement
                (" SELECT a.*,?, ?, ?* FROM EMP a WHERE a.ENAME LIKE 'W%' ");
		pstmt.setInt(1, i);
		pstmt.setInt(2, i);
		pstmt.setString(3, "test");
		ResultSet rs = pstmt.executeQuery();
		rs.close();
		pstmt.close();
	}
	
}

Delphi


begin
  Query1.Close;
  Query1.Sql.Clear;
  Query1.Sql.Add('SELECT ENAME, SAL FROM EMP ');
  Query1.Sql.Add('WHERE EMPNO = :empno');
  Query1.ParamByuName('empno').AsString := txtEmpno.Text;
  Query1.Open;
end;

위의 두 언어만 보더라도 Static SQL 작성 방법은 제공 되지 않는다.(직접적인 쿼리 작성이 아닌 String 변수를 이용)

Static SQL을 제공 : Powerbuilder, PL/SQL, Pro*C, SQLJ, COBOL..
Dynamic SQL만 제공 : JAVA, Delphi, Vaisual Basic ...

(4)문제의 본질은 바인드 변수 사용 여부

Static SQL이든 Dynamic SQL이든 DBMS입장에서는 SQL문 그자체만 인식 할 뿐이므로 애플리케이션 커서 캐싱 기능을 활용하고자 하는 경우 외에는 성능에 전혀 영향이 없다.
애플리케이션 커서 캐싱 기능을 사용 하지 않는다면 Dynamic, Static 구분은 라이브러리 캐시 효율과 무관하다.

그러므로 라이브러리 캐쉬 효율을 논할 때는 바인드 변수 사용 여부에 맞춰져야 한다.
변수를 문자와 결합 하므로서 얼마나 하드 파싱이 일어나 성능이 저하가 되는지, 라이브러리 캐시에 얼마나 심한 경합이 발생에 따라 바인드 변수의 사용 여부에 초점을 맞춰 작성을 해야 한다.

다시 한번 강조하지만, 바인드 변수 사용여부로 Static과 Dynamic을 구분하는 것은 잘못된 것이므로 용어 사용에 주의하자.