Showing captured bind values from Execution Plans and Captured binds #JoelKallmanDay

Hello,

In my previous post, I showed how to see USED and PEEKED bind variables from SQL Monitor views, either in memory and/or from AWR repository.

In this new one, I show how to see these same values from Execution Plans (either GV$SQL_PLAN and/or DBA_HIST_SQL_PLAN) as well as from Captured SQL binds (either from GV$SQL_BIND_CAPTURE and/or DBA_HIST_SQLBIND).

So, here is the same test case I’ve created in the previous post:

create table t (
col_n number, col_ch char, col_vc varchar2(10),
col_dt date, col_ts timestamp);
 
Table created.
 
define mydt="2023/10/01 12:01:12"
define myts="2023/10/01 12:01:12.123"
 
insert into t values (
1, 'A', 'Edu$!123', to_date('&mydt','yyyy-mm-dd hh24:mi:ss'), to_timestamp('&myts','yyyy-mm-dd hh24:mi:ss.ff'));
 
1 row created.

Now let’s run a query with binds:

alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
alter session set nls_timestamp_format='yyyy-mm-dd hh24:mi:ss.ff';

set serveroutput on
declare
    n number;
    c char;
    vc varchar2(10);
    dt date;
    ts timestamp;
    function run_query
    return number is
        rn number;
    begin
        select COL_N
        into rn
            from t
            where COL_VC!='MYQUERY3'
            and COL_N = n
            and COL_CH = c
            and COL_VC like vc
            and COL_DT = dt
            and COL_TS = ts
            ;
        return rn;
    exception when no_data_found then return (-1);
    end;
begin
    n := 1;
    c := 'A';
    vc := 'Edu%';
    dt := to_date('&mydt','yyyy-mm-dd hh24:mi:ss');
    ts := to_timestamp('&myts','yyyy-mm-dd hh24:mi:ss.ff');
    dbms_output.put_line(run_query);
    n := 2;
    c := 'B';
    vc := 'ABC%';
    dt := sysdate;
    ts := systimestamp; 
    dbms_output.put_line(run_query);
end;
/

1
-1

Let me find the SQL_ID of my query:

select sql_id, executions, sql_text
from v$sql
where sql_text like q'[SELECT %COL_VC!='MYQUERY3'%]';

SQL_ID        EXECUTIONS SQL_TEXT
------------- ---------- ------------------------------------------------------------
fb3gqgd87ac8h          2 SELECT COL_N FROM T WHERE COL_VC!='MYQUERY3' AND COL_N = :B5

1 row selected.

Now, here is the script to get the bind values from the SQL Plans:

col bind_name format a10 wrap
col peeked_value format a40
col datatype head "DATA_TYPE" for a20
col src for a9

def sqlid="fb3gqgd87ac8h"
def datini=sysdate-1
def datfin=sysdate

select src, INST_ID, sql_id, plan_hash_value, child_number, c.bind_name, c.bind_pos, c.bind_type,
	decode(bind_type,1,'VARCHAR2',2,'NUMBER',12,'DATE',96,'CHAR',180,'TIMESTAMP',181,'TIMESTAMP WITH TZ',231,'TIMESTAMP WITH LTZ',to_char(bind_type)) datatype,
	case
		 when c.bind_type = 1 /*varchar2*/ then utl_raw.cast_to_varchar2(c.bind_data)
		 when c.bind_type = 2 /*number*/ then to_char(utl_raw.cast_to_number(c.bind_data))
		 when c.bind_type = 96 /*char*/ then to_char(utl_raw.cast_to_varchar2(c.bind_data))
		 when c.bind_type = 12 /*date*/ then TO_CHAR(dbms_stats.convert_raw_to_date(c.bind_data),'yyyy-mm-dd hh24:mi:ss')
		 when bind_type = 180 /*timestamp*/ then 
				to_char(
					to_timestamp(
						to_char(
							dbms_stats.convert_raw_to_date(hextoraw(substr(bind_data,1,14)))
							,'yyyy-mm-dd hh24:mi:ss')
						,'yyyy-mm-dd hh24:mi:ss') 
						+ numtodsinterval(nvl(to_number(hextoraw(substr(bind_data,15,8)),'XXXXXXXX')/1e9,0), 'SECOND')
					,'yyyy-mm-dd hh24:mi:ss.ff9')
		 else 'Not printable'
	end as peeked_value
from (
	select 'SQLPLAN-M' src, sql_id, plan_hash_value, OTHER_XML, child_number, INST_ID
	from gv$sql_plan
	UNION ALL
	select 'SQLPLAN-H' src, sql_id, plan_hash_value, OTHER_XML, to_number(NULL) as child_number, to_number(NULL) as INST_ID
	from dba_hist_sql_plan
	join dba_hist_snapshot using (DBID)
	where END_INTERVAL_TIME between &datini and &datfin
) p, 
xmltable
	(
	'/*/peeked_binds/bind' passing xmltype(p.other_xml)
	columns 
		bind_name varchar2(30) path './@nam',
		bind_pos number path './@pos',
		bind_type number path './@dty',
		bind_data  raw(2000) path '.'
	) c
where sql_id = '&sqlid'
and p.other_xml is not null
order by src, inst_id, sql_id, plan_hash_value, child_number, bind_pos, peeked_value
/

SRC        In SQL_ID             PLAN_HV CHILD_NUMBER BIND_NAME    BIND_POS  BIND_TYPE DATA_TYPE            PEEKED_VALUE
--------- --- ------------- ------------ ------------ ---------- ---------- ---------- -------------------- ----------------------------------------
SQLPLAN-M   1 fb3gqgd87ac8h   1601196873            0 :B5                 1          2 NUMBER               1
SQLPLAN-M   1 fb3gqgd87ac8h   1601196873            0 :B4                 2         96 CHAR                 A
SQLPLAN-M   1 fb3gqgd87ac8h   1601196873            0 :B3                 3          1 VARCHAR2             Edu%
SQLPLAN-M   1 fb3gqgd87ac8h   1601196873            0 :B2                 4         12 DATE                 2023-10-01 12:01:12
SQLPLAN-M   1 fb3gqgd87ac8h   1601196873            0 :B1                 5        180 TIMESTAMP            2023-10-01 12:01:12.123000000

5 rows selected.

NOTE: you can get peeked values from SQL Plans direct in your execution plan. Maria Colgan explains how you can do that here.

And here is the script to get the bind values from the Captured binds:

col bind_name format a10 wrap
col captured_value format a40
col DATATYPE_STRING for a20
col src for a9

def sqlid="fb3gqgd87ac8h"
def datini=sysdate-1
def datfin=sysdate

select DISTINCT src, INST_ID, sql_id, plan_hash_value, child_number, 
	name as bind_name, position as bindpos, DATATYPE_STRING,
	case
		 when DATATYPE in (1,2,96) /*varchar2,number,char*/ then VALUE_STRING
         when DATATYPE = 12 /*date*/ then to_char(anydata.accessDate(value_anydata),'yyyy-mm-dd hh24:mi:ss')
         when DATATYPE = 180 /*timestamp*/ then to_char(anydata.accesstimestamp(value_anydata),'yyyy-mm-dd hh24:mi:ss.ff9')
		else 'Not printable'
	END captured_value
from (
	select 'CAPTURE-M' src, 
		sql_id, child_number, PLAN_HASH_VALUE, INST_ID, 
		name, position, datatype, DATATYPE_STRING, value_string, value_anydata
	from gv$sql_bind_capture
	join gv$sql s using (INST_ID, sql_id, child_number)
	UNION ALL
	select 'CAPTURE-H' src, 
		sql_id, to_number(NULL) as child_number, to_number(NULL) PLAN_HASH_VALUE, INSTANCE_NUMBER as INST_ID, 
		name, position, datatype, DATATYPE_STRING, value_string, value_anydata
	from dba_hist_sqlbind
	join dba_hist_snapshot using (DBID, INSTANCE_NUMBER, SNAP_ID)
	where END_INTERVAL_TIME between &datini and &datfin
	) c
where sql_id = '&sqlid'
order by src, INST_ID, sql_id, plan_hash_value, child_number, position, captured_value
/

SRC        In SQL_ID             PLAN_HV CHILD_NUMBER BIND_NAME     BINDPOS DATATYPE_STRING      CAPTURED_VALUE
--------- --- ------------- ------------ ------------ ---------- ---------- -------------------- ----------------------------------------
CAPTURE-M   1 fb3gqgd87ac8h   1601196873            0 :B5                 1 NUMBER               1
CAPTURE-M   1 fb3gqgd87ac8h   1601196873            0 :B4                 2 CHAR(32)             A
CAPTURE-M   1 fb3gqgd87ac8h   1601196873            0 :B3                 3 VARCHAR2(32)         Edu%
CAPTURE-M   1 fb3gqgd87ac8h   1601196873            0 :B2                 4 DATE                 2023-10-01 12:01:12
CAPTURE-M   1 fb3gqgd87ac8h   1601196873            0 :B1                 5 TIMESTAMP            2023-10-01 12:01:12.123000000

5 rows selected.

And that’s it for today.

See you next time!

Leave a comment