[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";
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();
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을 구분하는 것은 잘못된 것이다!