[4) 라이브러리 캐시 최적화 원리] 9. Static vs. Dynamic SQL

Yu River·2022년 7월 14일
0

[1] Static SQL(Embedded SQL)

  • String형 변수에 담지 않고 코드 사이에 직접 기술한 SQL문이다.

(1) 특징

  • Static SQL(Embedded SQL)을 이용하는 Pro*C 환경에서는 스칼라 서브쿼리, 분석 함수, ANSI 조인문 등을 사용했을 때 [PreComile] 과정에서 에러가 난다.
  • Syntax 체크만큼은 PreCompiler에 내장된 SQL 파서를 이용한다.
    • PreCompiler가 인식하지 못해 에러를 던진다면 이는 Dynamic SQL을 통해 해결한다.
  • Semantic 체크는 DB 접속을 통해 이루어진다.
  • Static(=Embedded) SQL을 지원하는 개발 언어로는 PowerBuilder, PL/SQL, Pro*C, SQLJ 정도가 있다.

[예시] Pro*C 구문으로 Static SQL을 작성하기

 int main(){
   printf("사번을 입력하십시오 : ");
   scanf("%d", &empno);
   EXEC SQL WHENAVER NOT FOUND GOTO notfound;
   EXEC SQL SELECT ENAME INTO :ename
            FROM   EMP
            WHERE  EMPNO = :empno;
   printf("사원명 : %s.\n", ename);
notfound:
   printf("%d는 존재하지 않는 사번입니다. \n", empno);
}

[2] Dynamic SQL

  • Dynamic SQL은 String형 변수에 담아서 기술하는 SQL문이다.

(1) 특징

  • SQL문을 동적으로 바꿀 수 있고, 또는 런타임 시에 사용자로부터 SQL문의 일부 또는 전부를 입력 받아서 실행할 수도 있다.
  • PreCompile시 Syntax, Semantics 체크가 불가능하다.
  • Dynamic SQL을 만나면 [PreCompiler]는 그 내용을 확인하지 않고 그대로 통과시킨다.
  • Toad, Orange, SQL*Plus과 같은 Ad-hoc 쿼리 툴에서 작성하는 SQL도 모두 Dynamic SQL이다.
    • 컴파일되는 시점에서 SQL이 확정되지 않았으며, 사용자가 던지는 SQL을 런타임 시에 받아서 그대로 DBMS에 던지는 역할만 할뿐이다.

[예시] Pro*C 구문으로 Dynamic SQL을 작성하기

int main(){
  char select_stmt[50] = "SELECT ENAME FROM EMP WHERE EMPNO = :empno";
  // scanf("%c", &select_stmt); --> SQL문을 동적으로 입력 받을 수도 있음

  EXEC SQL PREPARE sql_stmt FROM :select_stmt;
  EXEC SQL DECLARE emp_cursor CURSOR FOR sql_stmt;
  EXEC SQL OPEN emp_cursor USING :empno;
  EXEC SQL PETCH emp_cursor INTO :ename;
  EXEC SQL CLOSE emp_cursor;
  printf("사원명 : %s.\n", ename);
}

(3) Pro*C에서 제공하는 4가지 Dynamic Method

1.입력 Host 변수 없는 Non-Query

DELETE FROM EMP WHERE DEPTNO = 20;
GRANT SELECT ON EMP TO scott;

2. 입력 Host 변수 개수가 고정적인 Non-Query

INSERT INTO EMP (ENAME, JOB) VALUES (:ename, :job)
DELETE FROM EMP WHERE EMPNO = :empno

3. select-list 컬럼 개수와 입력 Host 변수 개수가 고정적인 Query

SELECT DEPTNO, MAX(SAL) FROM EMP GROUP BY DEPTNO
SELECT DNAME, LOC FROM DEPT WHERE DEPTNO = 20
SELECT ENAME, EMPNO FROM EMP WHERE DEPTNO = :deptno

4. select-list 컬럼 개수와 입력 Host 변수 개수가 가변적인 Query

INSERT INTO EMP (<unknown>) values (<unknown>)
SELECT <unknown> FROM EMP WHERE DEPTNO = :deptno

[3] 일반 프로그램 언어에서 SQL 작성

  • 일반 프로그램 언어에서 SQL 작성할 땐 모두 String 변수에 담아서 실행한다.

(1) JAVA

PreparedStatement stmt;
ResultSet rs;
StrongBuffer SQLStmt = new StringBuffer();
SQLStmt.append("SELECT ENAME, SAL FROM EMP ");
SQLStmt.append("WHERE EMPNO = ?");

stmt = conn.prepareStatement(SQLStmt.toString());
stmt.setLong(1, txtEmpno.value);
rs = stmt.executeQuery();

// do anything

rs.close();
stmt.close();

(2) 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;

[4] 문제의 본질은 바인드 변수 사용 여부에 있다.

  • ⭐️ 라이브러리 캐시 효율을 논할 때 초점은 바인드 변수 사용 여부에 맞춰져야 한다.
  • Static, Dynamic SQL은 애플리케이션 개발 측면에서의 구분일 뿐이며, 데이터베이스 입장에서는 차이가 없다.
  • 오라클 입장에서는 던져진 SQL문 그 자체만 인식한다.
  • PL/SQL, Pro*C 등에서 애플리케이션 커서 캐싱 기능을 활용하고자 하는 경우 외에는 성능에도 전혀 영향이 없다.

주의할 점

  • Dynamic SQL을 사용해 문제가 되는 것이 아니라 바인드 변수를 사용하지 않았을때 문제가 되는 것이다.
  • 바인드 변수를 사용하지 않고 Literal 값을 SQL 문자열에 결합하는 방식으로 개발한다면 반복적인 하드 파싱으로 성능이 저하되어 라이브러리 캐시에 심한 경합이 발생한다.
  • 바인드 변수 사용 여부로 Static과 Dynamic을 구분하는 것은 잘못된 것이다!
profile
도광양회(韜光養晦) ‘빛을 감추고 어둠속에서 힘을 기른다’

0개의 댓글