행위

바인드변수

DB CAFE

Dbcafe (토론 | 기여)님의 2023년 11월 22일 (수) 13:26 판 (바인드 피킹)
thumb_up 추천메뉴 바로가기


1 리터럴 SQL[편집]

android * 아래의 쿼리는 같은 결과과 나오지만 오라클은 각각 다른 쿼리라고 판단하여 작업을 하게 됩니다.

SELECT EMP_NO FROM EMPLOYEE WHERE EMPID = 10;
SELECT emp_no FROM employee WHERE empid = 10 ;
SELECT EMP_NO
  FROM EMPLOYEE
 WHERE EMPID = 20 ;
  • 즉, 100% 동일해야 SQL파싱이 발생하지 않습니다.
  • 이를 동일한 쿼리로 인식시키기 위해서 바인드 변수를 사용하여야 합니다.


  1. literal SQL은 서로 다른 SQL로 인식하므로 매번 Parsing(Hard Parsing) 작업이 필요하며 , 많은 CPU사용량과 추가적인 메모리(Shared Pool의 Library Cache)가 필요함
  2. Shared Pool Latch 및 Library Cache Latch의 경합을 유발하여 CPU 사용량이 급증함.
  3. 다른 SQL의 Parsing된 정보를 밀어내 버리며 Shared Pool의 Memory Pregmentation을 발생시킨다.
  4. ORA-4031와 같은 상황이 발생되어 DB Reboot 상황이 발생 할수도 있다.
  5. SQL이 Literal로 되어 있으면 Data가 Skewed되어 있는 경우에는 유리하나 다수가 이용하는 사용자 환경에는 적합하지 않다.

2 커서 쉐어링[편집]

  1. Literal SQL을 강제적으로 BIND변수로 바꿔서 SQL을 공유화 하는 기법
  2. 무조건 상수부를 바꿔 버리는 문제
Property Description
Parameter type String
Syntax EXACT | FORCE }
Default value EXACT
Modifiable ALTER SESSION , ALTER SYSTEM
Basic No
  • EXACT : 기본값, 서로 완전히 일치하는 SQL 문장만 커서가 공유됨
  • SIMILAR : SQL을 바인드 변수로 무조건 바꾸고 SQL당 PLAN이 Multi-Version을 가지고 선별적사용(의미분석)
  • FORCE : SQL을 바이드 변수로 무조건 바꼬고 SQL당 PLAN은 1개로 고정
select * from emp where ename='CLACK';
select * from emp where ename='CYKIM';
select * from emp where ename='JHone';

===> 다음과 같이 고정됨

select * from amp where ename=:"SYS_B_0";

3 바인드 피킹 (bind peeking)[편집]

  • 실행계획을 생성하기 전에 바인드 변수의 값을 쿼리 옵티마이져가 가져와서 리터럴 처럼 이용하는것
  • 바인드 변수를 사용하는 SQL은 처음 실행 될때 어떤값이 바인딩되어 수행 되었는지에 따라 , 이후 동일 SQL에 대한 실행계획이 고정되어 버리는 문제점이 있음

4 어뎁티브 커서 쉐어링[편집]

  • Skewed 데이터에 대한 Cursor Sharing의 문제점을 보완하고 바인드 변수에 따른 최적의 Plan을 결정하는 기능
  • _optimizer_adaptive_cursor_sharing = true (default)
  • 바인드 변수에 따라 여러개의 Plan을 가지고 있어 Bind Peeking 문제점을 보완 함

4.1 장점[편집]

  1. Bind 값이 동일한 Selectivity를 가질때 Plan 공유
  2. Bind 값이 다른 Selectivity를 가질때 새로운 Plan 생성

5 바인드 변수 확인[편집]

5.1 where절에 사용되는 바인드 값 확인[편집]

SELECT name, position, dup_position, datatype_string, value_string
  FROM (SELECT a.*
             , ROW_NUMBER () OVER (PARTITION BY a.position ORDER BY a.last_captured DESC) AS rn 
          FROM v$sql_bind_capture a
         WHERE a.sql_id = 'ckrnp9t0x37xh'
           AND a.child_number = 0)
 WHERE rn = 1;
WITH BB AS
 (SELECT DISTINCT NAME BIND_VAL, VALUE_STRING VAL_VAL,DATATYPE_STRING,SQL_ID
    FROM V$SQL_BIND_CAPTURE A
   WHERE (LAST_CAPTURED, SQL_ID) IN
         (SELECT MAX(LAST_CAPTURED), SQL_ID FROM V$SQL_BIND_CAPTURE GROUP BY SQL_ID)),
     CC AS 
 (SELECT 1 RNUM FROM DUAL UNION ALL SELECT 2 FROM DUAL)
SELECT SQL_ID,DECODE(MOD(ROWNUM, 2), 1, A, B)
  FROM (SELECT SQL_ID,REPLACE('VARIABLE BIND_VAL '||DATATYPE_STRING,'BIND_VAL',BIND_VAL) A,
               REPLACE(REPLACE('EXEC :BIND_VAL :=''VAL_VAL'';','VAL_VAL',VAL_VAL),'BIND_VAL',BIND_VAL) B
          FROM BB,
               CC)
WHERE sql_id = 'gsqxm6zz3w2by';
  • AWR 이용 (DBA_HIST_SQLBIND + DBA_HIST_SNAPSHOT)

- SQL문 텍스트를 보려면 DBA_HIST_SQLTEXT

SELECT DISTINCT 
       A.SNAP_ID , A.INSTANCE_NUMBER , B.END_INTERVAL_TIME
     , A.LAST_CAPTURED, A.NAME , A.VALUE_STRING 
  FROM DBA_HIST_SQLBIND A
     , DBA_HIST_SNAPSHOT B
 WHERE A.DBID = B.DBID
   AND A.SNAP_ID = B.SNAP_ID
   AND A.INSTANCE_NUMBER = B.INSTANCE_NUMBER 
   AND B.END_INTERVAL_TIME >= TRUNC(SYSDATE) + 9/24 - 1 / 8
   AND A.SQL_ID = :sqlid
   AND A.LAST_CAPTURED IS NOT NULL
 ORDER BY B.END_INTERVAL_TIME DESC;