1. SQL을 실행하면 제일먼저 SQL 파서(parser)가 SQL 문장에 문법적 오류가 없는지 검사(Syntax 검사)
2. 문법적 오류가 없다면 의미상 오류가 없는지 검사(Semantic 검사, 오브젝트 존재유무등)
3. 사용자가 발생한 SQL과 그 실행계획이 라이브러리캐시(프로시저캐시)에 캐싱되어 있는지 확인
4. SQL과 실행계획이 캐싱되어 있다면 소프트파싱, 캐싱되어있지 않다면 하드파싱
SQL 수행절차
1. 문법적 오류와 의미상 오류가 없는지 검사
2. 해시 함수로부터 반환받은 해시 값으로 라이브러리 캐시 내 해시버킷 탐색
3. 찾아간 해시버킷에 체인으로 연결된 엔트리를 차례로 스캔하면서 같은 SQL 문장 탐색
4. SQL문장을 찾으면 함께 저장된 실행계획을 가지고 바로 실행
5. 찾아간 해시 버킷에서 SQL 문장을 찾지 못하면 최적화를 수행
6. 최적화를 거친 SQL과 실행계획을 방금 탐색한 해시 버킷 체인에 연결
7. 방금 최적화한 실행계획을 가지고 실행
칼럼의 분포가 균일할때는 바인드 변수 처리가 나쁘지 않음
칼럼의 분포가 균일하지 않을때에는 실행 시점에 바인딩되는 값에 따라 쿼리 성능이 다르게 나타날 수 있으므로 이럴때는 상수값을 사용하는것이 나을수 있음
바인드변수 Peeking 기능 도입 : 첫번째 바인드 변수값을 살짝 훔쳐보고 그 값에 대한 분포를 이용하여 실행계획 결정하는 기능
Oracle | 바인드 변수 Peeking |
SQL Server | Parameter Sniffing |
>바인드변수 Peeking 기능 도입 : 첫번째 바인드 변수값을 살짝 훔쳐보고 그 값에 대한 분포를 이용하여 실행계획 결정하는 기능
>바인드변수 Peeking 기능 도입 : 첫번째 바인드 변수값을 살짝 훔쳐보고 그 값에 대한 분포를 이용하여 실행계획 결정하는 기능
>오라클은 11g부터는 적응적 커서공유(Adaptive Cursor Sharing)를 도입하여 칼럼 분포에 따라 다른 실행계획이 사용되도록 처리하였지만 이또한 완전한 기능이 아니므로 주의해서 사용 필요
String형 변수에 담지 않고 코드 사이에 직접 기술한 SQL문(Embedded SQL)
개발언어 : PowerBuilder, PL/SQL, Pro*C, SQLJ
Proc*C 구분으로 Static SQL 작성한 예시
int 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 변수에 담지 않고 마치 예약된 키워드처럼 C/C+\+ 코드 사이에 섞어 기술
\-구문분석, 유효 오브젝트 여부, 오브젝트 엑세스 권한등의 체크 가능
String 형 변수에 담아서 기술하는 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 FETCH emp_cursor INTO :ename;
EXEC SQL CLOSE emp_cursor;
printf("사원명 : %s.\n", ename);
}
조건에 따란 SQL이 동적으로 바뀔수 있으므로 syntax, semantics 체크 불가능
for(;;) {
EXEC ORACLE OPTION (HOLD_CURSOR=YES);
EXEC ORACLE OPTION (RELEASE_CURSOR=NO);
EXEC SQL INSERT ...... ; // SQL 수행
EXEC ORACLE OPTION (RELEASE_CURSOR=YES);
}
call count cpu elapsed disk query current rows
----- ------ ----- ------- ----- ------- ------ -----
Parse 1 0.00 0.00 0 0 0 0
Execute 5000 0.18 0.14 0 0 0 0
Fetch 5000 0.17 0.23 0 10000 0 5000
----- ------ ----- ------- ----- ------- ------ -----
total 10001 0.35 0.37 0 10000 0 5000
Misses in library cache during parse: 1
public static void CursorCaching(Connection conn, int count) throws Exception{
// 캐시 사이즈를 1로 지정
((OracleConnection)conn).setStatementCacheSize(1);
// 묵시적 캐싱 기능을 활성화
((OracleConnection)conn).setImplicitCachingEnabled(true);
for (int i = 1; i <= count; i++) {
// PreparedStatement를 루프문 안쪽에 선언
PreparedStatement stmt = conn.prepareStatement(
"SELECT ?,?,?,a.* FROM emp a WHERE a.ename LIKE 'W%'"); stmt.setInt(1,i);
stmt.setInt(2,i); stmt.setString(3,"test");
ResultSet rs=stmt.executeQuery();
rs.close();
// 커서를 닫더라도 묵시적 캐싱 기능을 활성화 했으므로 닫지 않고 캐시에 보관하게 됨
stmt.close();
}
}
public static void CursorHolding(Connection conn, int count) throws Exception{
// PreparedStatement를 루프문 바깥에 선언
PreparedStatement stmt = conn.prepareStatement(
"SELECT ?,?,?,a.* FROM emp a WHERE a.ename LIKE 'W%'");
ResultSet rs;
for (int i = 1; i <= count; i++) {
stmt.setInt(1,i);
stmt.setInt(2,i);
stmt.setString(3,"test");
rs=stmt.executeQuery();
rs.close();
} // 루프를 빠져 나왔을 때 커서를 닫는다.
stmt.close();
}