Showing posts with label Test SQL execution using bind variables. Show all posts
Showing posts with label Test SQL execution using bind variables. Show all posts

Saturday, June 14, 2025

How to test SQL execution manually using bind variables

Thank you very much for your interest. 

Problem statement:Suppose some developer said one of the application query performance is not consistent. Out of 10 execution lets say 6 executions are slow.You started analyzing and found that there are multiple plan available in cursor cache.You have gatherd plan stats and decided to create a SQL plan baseline out of that.Now, as a DBA how will you test execution manually from sqlplus client.Application using bind variable and if you use literal then your SQL text will change which results change in SQL_ID.

As a result SQL plan baseline will not be attached because plan baseline is specific to a single SQL_ID.So, how will you solve this problem? If you are interested I will walk you through the steps involved in this case.

Test environment:-Oracle database 19c in OEL 7.9

I have created following SQL plan baseline for the below SQL coming from application.Lets capture all these information from v$sql

SELECT FT_ID, FT_TYPE_FLG, CUR_AMT, TOT_AMT, ARS_DT, SIBLING_ID, VERSION
FROM CI_FT
WHERE SA_ID = :1 AND FREEZE_SW = :2 AND ARS_DT IS NOT NULL AND ARS_DT < :3 AND REDUNDANT_SW = :4 AND NOT_IN_ARS_SW = :5 ORDER BY ARS_DT , SIBLING_ID , FT_TYPE_FLG 

SQL_Plan_BASELINE :SQL_PLAN_dpfk7jn4r397y612e1e29


Please see that all variables are bind here using :1,:2  respectively

You can find SQL text from SQL_TEXT column of v$sql if the query still presents in cursor cache.

If it is aged out you can search in dba_hist_sqltext dictionary.

Now you may be aware that bind variables can be captured during extract of execution plan like below

select * from table(dbmsx_xplan.display(cursor('SQL_ID','CURSOR_CHILD_NO','OUTLINE PEEKED_BINDS'));

but I would recommend you to use below SQL to construct variables which can be used later.

if query is still in cursor cache

select replace(name,':','v'||substr(name,2,0))||' '||datatype_string||' :='''||value_string||''';' from v$sql_bind_capture a where sql_id='7dxj4xw55cra4'

else

select replace(name,':','v'||substr(name,2,0))||' '||datatype_string||' :='''||value_string||''';' from dba_hist_sqlbind a where sql_id='7dxj4xw55cra4' and  snap_id = (select max(snap_id) from dba_hist_sqlbind b where b.sql_id='7dxj4xw55cra4')

REPLACE(NAME,':','V'||SUBSTR(NAME,2,0))||''||DATATYPE_STRING||':='''||VALUE_STRI
--------------------------------------------------------------------------------
v1 CHAR(32) :='7983757218'                                                      
v2 CHAR(32) :='Y'                                                               
v3 DATE :='01/17/2025 00:00:00'                                                 
v4 CHAR(32) :='N'                                                               
v5 CHAR(32) :='N'     

Now construct below anonymous PL/SQL code like below
Use SQL_FULL TEXT of v$sql to get exact SQL text and put it in the variable vSQL.
DO NOT MAKE ANY CHANGES otherwise it will create a new SQL_ID

set serverout on
alter session set current_schema=<Valid Schema for tables>; --This is required 
alter session set nls_date_format='/mm/dd/yyyy HH24:MI:SS';
declare
v1 CHAR(32) :='7983757218' ;                                                     
v2 CHAR(32) :='Y';                                                               
v3 DATE :=to_date('01/17/2025 00:00:00','/mm/dd/yyyy HH24:MI:SS')  ;                                               
v4 CHAR(32) :='N'    ;                                                           
v5 CHAR(32) :='N'   ;
vSQL varchar2(4000);
begin
                                                        
vSQL :='SELECT                    FT_ID,                    FT_TYPE_FLG,                    CUR_AMT,                    TOT_AMT,                    ARS_DT,                    SIBLING_ID,                    VERSION                 FROM                    CI_FT                 WHERE                    SA_ID = :1                      AND FREEZE_SW = :2                      AND ARS_DT IS NOT NULL                     AND ARS_DT < :3                      AND REDUNDANT_SW = :4                      AND NOT_IN_ARS_SW = :5                      ORDER BY ARS_DT                    , SIBLING_ID                    , FT_TYPE_FLG ';
dbms_output.put_line(vSQL);
execute immediate vSQL using v1,v2,v3,v4,v5;
end;
/
Use below query to find out last execution like below
set line 200
col SQL_PLAN_BASELINE format a30
select last_load_time,plan_hash_value,sql_plan_baseline,elapsed_time from v$sql where sql_id='7dxj4xw55cra4'


LAST_LOAD_TIME      PLAN_HASH_VALUE SQL_PLAN_BASELINE              ELAPSED_TIME
------------------- --------------- ------------------------------ ------------
2025-06-14/14:45:41      1243860814 SQL_PLAN_dpfk7jn4r397y612e1e29        11147                                                                                         
1 row selected.

This proves that you have successfully tested the SQL with same SQL_ID which is using your created plan baseline with expected elapsed time

When SQL plan baseline is not used

 Thank you very much for your interest. Problem Statement: Lets assume you are reported about an application query running slow in one envir...