Skip to Main Content
  • Questions
  • EXECUTE IMMEDIATE Dynamically handling USING clause

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Girish.

Asked: October 15, 2024 - 5:07 am UTC

Last updated: October 21, 2024 - 12:57 pm UTC

Version: Oracle Database 19c EE - 19.0.0.0.0

Viewed 1000+ times

You Asked

Hi,

We have case which is depicted in below code. Requirement is SQL has three input values in WHERE clause and which are passed using an EXECUTE IMMEDIATE USING clause. There may be cases where user may not pass one or more values. Now it is giving error when executed. Is there a way to handle this requirement.

declare
l_empno number := &l_empno;
l_deptno number:= &l_deptno;
l_mgr number:=&l_mgr;
l_stm varchar2(1000);
begin
l_stm := 'select * from emp where empno = :1 and deptno = :2 and mgr = :3';
execute immediate l_stm using l_empno,l_deptno,to_number(l_mgr);
end;


1) When all values are passed as input then PL/SQL block completes successfully
2) When any one of the value is not passed then it errors. Is there a way to do this?

Thanks,
Girish

and Chris said...

This is using substitution variables. The client replaces them with the literal values you supply. If you provide no value, they effectively vanish and your code becomes:

declare
l_empno number := ;
l_deptno number:= ;
l_mgr number:= ;
...


Which is invalid code.

It's much better to use bind variables instead. You'll have to define these in advance, e.g.:

var empno number;
var deptno number;
var mgr number;

exec :empno := 100;

declare
l_empno number := :empno;
l_deptno number:= :deptno;
l_mgr number:= :mgr;
l_stm varchar2(1000);
begin
l_stm := 'select * from emp where empno = :1 and deptno = :2 and mgr = :3';
execute immediate l_stm using l_empno,l_deptno,to_number(l_mgr);
end;
/


If you don't assign a value to any of these the code will still run.

Of course, there are much bigger questions here around this whole code block - particularly why you're using execute immediate for a static SQL statement. I'm assuming this is a simplified example.

Rating

  (3 ratings)

Comments

A reader, October 17, 2024 - 1:33 pm UTC

It is a simplified example of what is actually being done. In real code it is a search screen with multiple/few inputs and based on input search values query is constructed dynamically and executed using EXECUTE IMMEDIATE. When they use execute immediate there may be few variables in USING clause for which there may not be any values.
Chris Saxon
October 21, 2024 - 12:52 pm UTC

If this application code, you definitely should not be using substitution variables.

See the posts from Rajesh and Stew below if you need to support dynamic queries.

From Tom kyte - Oracle magazine column.

Rajeshwaran, Jeyabal, October 18, 2024 - 4:04 pm UTC

Read this Oracle magazine from Tom kyte - that should help your scenario for conditional binding.

https://asktom.oracle.com/Misc/oramag/on-popularity-and-natural-selection.html

Also take some time to read about Chapter# 16 - from Expert one on one oracle, more details about Dynamic sql there including Native Dynamic SQL Vs DBMS_SQL and when should one and why.

Backing up a little bit

Stew Ashton, October 19, 2024 - 8:15 am UTC

This issue has been brought up again and again over the years: how to handle queries that are the same except for the WHERE clause, whose conditions can vary in number and type.

Why is this a PL/SQL question? PL/SQL cannot fetch the data and return it to the client as is! At best, it can open a REF CURSOR and let the client do the fetching. If the client can do the fetching, why not let it build the dynamic query, which it can do much more easily than PL/SQL?

Besides, if the number and type of the bind variables can vary, then the query must be built using DBMS_SQL, then the resulting query must be converted to a REF CURSOR. What fun...

Finally, the PL/SQL function that does this has to handle the varying inputs, so the API it provides will probably be clunky anyway.

Personally, I would just build, execute and fetch from the query using whatever language accesses the database.

A compromise position would be to use a SQL macro. The client would still execute and fetch from the query, and it would have to provide logic to handle varying inputs, but there would be some guardrails to promote the use of bind variables.

Here is an example:
- the API would include parameters for all possible values, but they would be optional (and they can be bind variables);
- the client would have to explicitly say which conditions are to be tested;
- in the end, the client is executing and fetching from a query, so the PL/SQL is no longer a clunky middleman.
SQL> create or replace function emp_filtered(
  2    p_filter_cols in dbms_tf.columns_t default null,
  3    p_empno in number default null,
  4    p_deptno in number default null,
  5    p_mgr in number default null
  6  ) return varchar2 sql_macro is
  7  
  8    type tt_filters is table of varchar2(255) index by varchar2(130);
  9    lt_filters tt_filters := new tt_filters(
 10      '"EMPNO"' => ' and empno = p_empno',
 11      '"DEPTNO"' => ' and deptno = p_deptno',
 12      '"MGR"' => ' and mgr = p_mgr'
 13    );
 14    l_sql varchar2(4000) := 'select * from emp where 1=1';
 15  
 16  begin
 17    if p_filter_cols is not null then
 18      for i in 1..p_filter_cols.count loop
 19        l_sql := l_sql || lt_filters(p_filter_cols(i));
 20      end loop;
 21    end if;
 22    dbms_output.put_line(l_sql);
 23    return l_sql;
 24  
 25  end emp_filtered;
 26  /

Function EMP_FILTERED compiled

SQL> var v_deptno number;
SQL> var v_mgr number;
SQL> begin
  2    :v_deptno := 30;
  3    :v_mgr := 7839;
  4  end;
  5  /

PL/SQL procedure successfully completed.

SQL> select * from emp_filtered(
  2    p_filter_cols => columns(deptno, mgr),
  3    p_deptno => :v_deptno,
  4    p_mgr => :v_mgr
  5  );

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7698 BLAKE      MANAGER         7839 1981-05-01 00:00:00       2850                    30

--generated query: select * from emp where 1=1 and deptno = p_deptno and mgr = p_mgr

Chris Saxon
October 21, 2024 - 12:57 pm UTC

Nice example of using a macro for a dynamic query Stew.

I think there can be value in using PL/SQL to construct dynamic SQL though. While it can't return the data as-is, you could convert it to JSON, XML, object types or other structured formats in PL/SQL.