문서유형ㅣ기술정보
분야ㅣ모니터링/점검
적용제품버전ㅣ7FS04PS
문서번호ㅣTMOTI043
개요
쿼리 내에 바인드 변수가 '?'로 선언되어 있는 경우, tbsql에서 실행할 수 있는 바인드 변수로 치환하는 방법에 대해 예시와 함께 안내합니다.
테스트 환경
| 서버명 | OS version | DB Version | DB Name | Port | Username | IP |
|---|---|---|---|---|---|---|
| Tibero | CentOS 8.4.2105 | Tibero 7.2.4 | tibero | 8629 | sys | 172.16.59.110 |
방법
프로그램에 따라 바인드 변수를 선언하는 방식이 다릅니다.
예)
- 자바 프로그램, orange for tibero : '?' 형태로 선언
- cli 프로그램, tbsql : ':문자' 형태로 선언
따라서, 자바프로그램에서 실행된 쿼리의 plan stat 정보를 확인하기 위해 tbsql에서 쿼리를 실행해야 할 경우, 쿼리 내에 '?' 형태로 바인드 처리한 부분을 ':문자' 형태로 치환하는 작업이 필요합니다.
이러한 경우 아래와 같은 psm을 사용하면 tbsql에서 실행 가능한 쿼리로 간단히 변경할 수 있습니다.
자바프로그램에서 바인드변수를 사용한 예시
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Date;
public class bind_capture {
public static void main(String[] args) {
// DB 접속 정보
String url = "jdbc:tibero:thin:@localhost:8629:tibero0";
String user = "sys";
String password = "tibero";
// 조회할 변수
int col1 = 1;
String col2 = "A";
Date col3 = new Date(System.currentTimeMillis());
// SQL 구문
String sql = "SELECT col1, col2, col3 "
+ "FROM tibero.test "
+ "WHERE col1 = ? "
+ "AND col2 = ? "
+ "AND TO_CHAR(col3, 'yyyy/mm/dd') = ?";
// 드라이버 클래스명: Tibero JDBC 드라이버 필요 (tibero6-jdbc.jar or tibero7-jdbc.jar)
try {
Class.forName("com.tmax.tibero.jdbc.TbDriver");
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, col1);
pstmt.setString(2, col2);
pstmt.setString(3, col3.toString().replace("-", "/")); // yyyy/mm/dd 형식 맞추기
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
int r1 = rs.getInt("col1");
String r2 = rs.getString("col2");
Date r3 = rs.getDate("col3");
System.out.println("col1=" + r1 + ", col2=" + r2 + ", col3=" + r3);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
실행구문
자바 프로그램이 수행한 SQL_ID, PLAN_HASH_VALUE 확인
[tibero@kimmi java_test]$ tbsql sys/tibero tbSQL 7 TmaxTibero Corporation Copyright (c) 2020-. All rights reserved. Connected to Tibero. SQL> select * from v$sqltext where sql_text like '%SELECT col1, col2, col3%'; HASH_VALUE PLAN_HASH_VALUE SQL_ID CHILD_NUMBER COMMAND_TYPE PIECE SQL_TEXT ---------- --------------- ------------- ------------ ------------ ---------- ---------------------------------------------------------------- 207937103 3704774042 5qkffdw669rkg 1484 1 0 SELECT col1, col2, col3 FROM tibero.test WHERE col1 = ? AND col2 1 row selected.
실행구문 수행
실행구문 내 v_sqlid, v_plan_val 값은 이전에 조회한 쿼리의 결과값 내용으로 입력이 필요합니다.
SQL> set serverout on
SQL> DECLARE
v_cnt number;
v_str varchar(4000);
v_tmp1 varchar(4000);
v_tmp2 varchar(4000);
v_tmp3 varchar(4000);
v_sqlid varchar(13);
v_plan_val number;
v_type varchar(128);
v_len number;
v_val varchar(4000);
BEGIN
v_cnt := 0;
v_str := '';
v_tmp1 := '';
v_tmp2 := '';
v_tmp3 := '';
v_sqlid := '';
v_plan_val := 0;
v_type := '';
v_len := 0;
v_val := '';
--sql_id, plan_hash_value 값 입력
v_sqlid := '5qkffdw669rkg';
v_plan_val := '3704774042';
--동일한 sql_id와 plan_hash_value를 가진 sql_text 내용을 v_str에 병합
FOR l_cnt IN (select sql_text from V$SQLTEXT_WITH_NEWLINES2 where sql_id = v_sqlid and plan_hash_value = v_plan_val order by piece) LOOP
v_str := v_str || chr(13)||chr(10) || l_cnt.sql_text;
END LOOP;
--바인드 갯수 확인
select regexp_count(v_str,'\?') into v_cnt from dual;
v_tmp3 := v_str;
--바인드변수 선언
--V$SQL_BIND_CAPTURE_ALL에서 마지막 시점의 바인드 값 확인
FOR l_cnt IN 1..v_cnt LOOP
select type_name, length, value into v_type, v_len, v_val from (select type_name, length, value, time_captured as time_captured from V$SQL_BIND_CAPTURE_ALL where sql_id = v_sqlid and plan_hash_value = v_plan_val and position = l_cnt-1 group by sql_hash_value, position, type_name, length, value, time_captured order by time_captured desc) where rownum = 1;
IF v_type = 'VARCHAR2' THEN
v_tmp1 := 'var a'||l_cnt||' ' || v_type || '(' || v_len || ')' || ';';
v_tmp2 := 'exec :a'||l_cnt||' := ' || '''' || v_val || ''';';
ELSIF v_type = 'DATE' THEN
v_tmp1 := 'var a'||l_cnt||' ' || v_type || ';';
v_tmp2 := 'exec :a'||l_cnt||' := ' || '''' || v_val || ''';';
ELSE
v_tmp1 := 'var a'||l_cnt||' ' || v_type || ';';
v_tmp2 := 'exec :a'||l_cnt||' := ' || v_val || ';';
END IF;
dbms_output.put_line (v_tmp1);
dbms_output.put_line (v_tmp2);
END LOOP;
--쿼리에서 ?를 바인드변수로 치환
FOR l_cnt IN 1..v_cnt LOOP
select regexp_replace(v_tmp3,'\?',':a'||l_cnt,1,1) into v_tmp3 from dual;
END LOOP;
dbms_output.put_line ('');
dbms_output.put_line (v_tmp3);
EXCEPTION
WHEN NO_DATA_FOUND THEN
null;
END;
/
결과값
var a1 NUMBER; exec :a1 := 1; var a2 VARCHAR2(32); exec :a2 := 'A'; var a3 VARCHAR2(32); exec :a3 := '2025/11/06'; SELECT col1, col2, col3 FROM tibero.test WHERE col1 = :a1 AND col2 = :a2 AND TO_CHAR(col3, 'yyyy/mm/dd') = :a3 PSM completed.
참고
- BIND_VARIABLE_CAPTURE=Y인 경우 사용 가능
- 바인드 변수 타입이 number, varchar, date인 경우에만 사용 가능