ORA-01008: not all variables bound - "not all variables bound" - oracle

This is my plsql block, dynamic SQL. I can't find a reason why it comes up with an error 'no ORA-01008: not all variables bound ORA-06512: at line 23'.
I can't find the error on my EXECUTE IMMEDIATE statement.
DECLARE
form_name VARCHAR2(225) := 'MUST AS';
ad_no VARCHAR2(225) := :ad_no;
sql_stmt VARCHAR2(4000);
sql_output VARCHAR2(4000);
db_table VARCHAR(225) := inp_reminder_pkg.form_db_table(form_name);
col_id VARCHAR(225) := inp_reminder_pkg.get_col_id(form_name);
BEGIN
sql_stmt := '
SELECT :1
FROM #tableName
WHERE advno = :2
AND created = ( SELECT MAX(CREATED)
FROM #tableName
WHERE advno = :2 )'
;
sql_stmt := replace(sql_stmt, '#tableName', db_table);
EXECUTE IMMEDIATE sql_stmt
INTO sql_output
USING col_id, ad_no;
dbms_output.put_line(sql_output);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('no-data');
END;
Let me know what I am missing. thanks you

There are three bind variables (even though two have the same name), you need to send 3 arguments for them in the execute immediate statement.
Note that you probably didn’t mean for the column name to be input as a bind variable, this is something that has to be dynamically executed if you want to have a variable column being selected.

There are two problems with your code:
The dynamic query has two instances of the placeholder :advno so you need to pass two values in the USING clause. Yes this is a bit rubbish, but I guess Oracle felt parsing dynamic SQL is hard enough without asking the compiler to match placeholder names.
We can't pass column names or other identifiers as parameters. We have to break up the statement to reference the column name variable. For the same reason you have the replace() call to substitute the table name.
So you need to change your procedure so it looks like this:
DECLARE
form_name VARCHAR2(225) := 'MUST AS';
ad_no VARCHAR2(225) := :ad_no;
sql_stmt VARCHAR2(4000);
sql_output VARCHAR2(4000);
db_table VARCHAR(225) := inp_reminder_pkg.form_db_table(form_name);
col_id VARCHAR(225) := inp_reminder_pkg.get_col_id(form_name);
BEGIN
sql_stmt := '
SELECT ' || col_id || '
FROM #tableName
WHERE advno = :2
AND created = ( SELECT MAX(CREATED)
FROM #tableName
WHERE advno = :2 )'
;
sql_stmt := replace(sql_stmt, '#tableName', db_table);
EXECUTE IMMEDIATE sql_stmt
INTO sql_output
USING ad_no, ad_no;
dbms_output.put_line(sql_output);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('no-data');
END;

Related

Execute the command "UPDATE" which is in the variable CLOB

I keep some commands in CLOB format. I have the command "update WAYBILLS set TARE = 1212 where EXTERNAL_ID = 1212". I want to do it by calling in the process. I want to perform the actions described in this command. But I do not understand how, I will be grateful for the help
declare
l_clob CLOB;
v_sql CLOB;
BEGIN
v_sql := 'update WAYBILLS set TARE = 1212 where EXTERNAL_ID = 1212';
END ;
The easiest way to execute dynamic SQL is to use EXECUTE IMMEDIATE. Use bind variables.
DECLARE
v_sql CLOB;
v_id PLS_INTEGER := 1212;
BEGIN
v_sql := 'UPDATE waybills SET tare = :id WHERE external_id = :id';
EXECUTE IMMEDIATE(v_sql) USING v_id, v_id;
END;
/
If your statements are longer than 32k, then you have to deal with DBMS_SQL.
You can find more information for example here: Database PL/SQL Language Reference

Passing dynamic input parameters to 'execute Immediate'

I have a table where I am storing certain conditions along with input parameters as shown below:
CONDITION | INPUT_PARAMS
---------------------------------------------------------
:p_end_date < :p_start_date | v_end_date, IN v_start_date
:p_joining_day = 'MONDAY' | v_joining_day
I want to use execute immediate to evaluate the conditions.
select condition, input_param
into v_execute_condition, v_input_param
From table;
v_execute_statement :=
'IF '||v_execute_condition ||' '||
'THEN :o_flag := ''Y'';' ||' '||
'ELSE :o_flag := ''N'';' ||' '||
'END IF;';
v_execute_statement := 'BEGIN '||v_execute_statement||' END;';
dbms_output.put_line(v_execute_statement);
EXECUTE IMMEDIATE v_execute_statement USING IN input_param OUT v_flag;
This gives me an error. If I do not pass input parameters dynamically it works.
How can I pass the list of input parameters dynamically?
You can't supply a string list of bind values as a using parameter, so the only way I can see to do this is with nested dynamic SQL calls, which is a bit messy, and means having to declare (and bind) all possible parameters in the inner. nested, dynamic statement.
declare
v_execute_statement varchar2(4000);
v_flag varchar2(1);
v_start_date date := date '2018-01-01';
v_end_date date := date '2018-01-31';
v_joining_day varchar2(9) := 'MONDAY';
begin
-- loop over all rows for demo
for rec in (
select condition, input_params
From your_table
)
loop
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF ]' || rec.condition || q'[ THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING ]' || rec.input_params || q'[, OUT :v_flag;
END;]';
dbms_output.put_line('Statement: ' || v_execute_statement);
EXECUTE IMMEDIATE v_execute_statement
USING v_start_date, v_end_date, v_joining_day, OUT v_flag;
dbms_output.put_line('Result flag: ' || v_flag);
end loop;
end;
/
I've used the alternative quoting mechanism here to reduce confusion from escaped single quotes. There are two nested levels of quoting - the outer one delimited by q'[...]' and the inner one delimited by q'^...^', but you can use other characters if those are a problem because of your actual table contents. Escaping those quotes for two levels would be quite ugly and hard to follow/get right; and you'd also have to worry about further escaping quotes in your condition strings, which would already be a problem with your existing code for the second sample you provided as that contains a text literal within it.
With your two sample table rows and the dummy date/day values I showed above the output from running that is:
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_end_date < :p_start_date THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_end_date, IN v_start_date, OUT :o_flag;
END;
Result flag: N
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_joining_day = 'MONDAY' THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_joining_day, OUT :o_flag;
END;
Result flag: Y
The first thing to note in the generated statement is the declare section, which has to list all the possible variable names you might have in input_params, and set them from new bind variables. You must know these already in the main block/procedure, either as local variables or more likely procedure arguments; but they all have the be duplicated here, since at this point you don't know which will be needed.
Then that statement has its own inner dynamic SQL which is essentially what you were originally doing, but concatenates in the input_params string as well as condition.
The important part here is the quoting. In the first one, for example, both :p_end_date and :p_start_date are inside the second level of quotes, within the q'^...^', so they are bound for the inner dynamic SQL, with values from the local v_end_date and v_start_date from that inner execute immediate.
That entire generated block is executed with bind values for all the possible variable names, which provide values for the local variables (via v_start_date date := :v_start_date; etc.) while preserving data types; plus the output flag.
That block then executes its internal execute immediate statement using only the relevant local variables, which now have bound values; and the output flag which is still a bind variable from the outer execute immediate, so the outer block can still see its result.
You can see that the second generated statement uses a different condition and bind variables and values to the first, and the flag is evaluated based on the relevant condition and parameters in each case.
Incidentally, you could remove the duplicate reference to :o_flag (which isn't a problem but I find slightly confusing) by using a case expression instead:
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
:o_flag := CASE WHEN ]' || rec.condition || q'[ THEN 'Y' ELSE 'N' END;
END;^'
USING OUT :v_flag, ]' || rec.input_params || q'[;
END;]';

Oracle dynamic parameters

I'm struggling to create a dynamic sql parametrized query. It involves using 'IS NULL' or 'IS NOT NULL'
Here's a simple pl/sql query:
CREATE OR REPLACE PROCEDURE GET_ALL_INFORMATION
(
"PARAM_START_DATE" IN DATE,
"PARAM_END_DATE" IN DATE,
"PARAM_IS_SUBMITTED" IN NUMBER,
"EXTRACT_SUBMITTED_CONTACTS" OUT sys_refcursor
) IS
sql_stmt VARCHAR2(3000);
PARAM_CONDITION VARCHAR2(20);
BEGIN
IF PARAM_IS_SUBMITTED = 1 THEN
PARAM_CONDITION := 'NOT NULL';
ELSE
PARAM_CONDITION := 'NULL';
END IF;
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS :A;
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt USING PARAM_CONDITION;
Whereas the parameter (:A) in (USING PARAM_CONDITION) should have 'NULL' or 'NOT NULL'. It does not seem to work the way I envisioned.
Am I missing something?
As explained by GriffeyDog in a comment above, bind parameters could only be used as place holder for values. Not to replace keywords or identifiers.
However, this is not really an issue here, as you are using dynamic SQL. The key idea ifs that you build your query as a string -- and it will be parsed at run-time by the PL/SQL engine when you invoke EXECUTE or OPEN .. FOR.
Simply said, you need a concatenation -- not a bound parameter:
...
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS ' || PARAM_CONDITION;
-- ^^
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt;

using EXECUTE IMMEDIATE with multiple same bind arguments

When I create the following procedure
create or replace procedure check_exec_imm(
tab IN VARCHAR2,
col IN VARCHAR2,
col_name IN VARCHAR2
)
IS
cv SYS_REFCURSOR;
col_value VARCHAR2(32767);
lv_query VARCHAR2(32767);
BEGIN
lv_query := 'SELECT ' ||col||
' FROM ' ||tab||
' WHERE (:1 = ''EUR'' OR :1 = ''USD'') and rownum <=1';
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
DBMS_OUTPUT.PUT_LINE('COLUMN VALUE : ' || col_value);
END;
When the procedure is executed, I'm getting the following error
ORA-01008: not all variables bound
ORA-06512: at "GRM_IV.CHECK_EXEC_IMM", line 18
ORA-06512: at line 2
When I give the bind argument col_name again as below, the procedure is running fine.
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name, col_name ;
Why oracle is behaving differently in this procedure. Since, it is the same bind variable, one bind argument should be sufficient right..!!? Please explain where I'm getting my logic wrong.
There is "special" behaviour in Oracle: Repeated Placeholder Names in Dynamic SQL Statements
In an Anonymous Block or CALL Statement it is not required to repeat the bind values if the names are equal.
For example this Anonymous Block is working:
DECLARE
a NUMBER := 4;
b NUMBER := 7;
plsql_block VARCHAR2(100);
BEGIN
plsql_block := 'BEGIN calc_stats(:x, :x, :y, :x); END;';
EXECUTE IMMEDIATE plsql_block USING a, b; -- calc_stats(a, a, b, a)
END;
/
But this EXECUTE IMMEDIATE plsql_block USING a, b; does not work inside a Procedure.
The way you have referenced the column name through bind variable is not a preferred method as Nichoas pointed out. What you tried is called as native dynamic SQL using 'cleverer' bind variables.
In this method, you need to bind every parameter X times since you use it X times because they are all treated as separate variables.
Read more on binding to dynamic SQL.
#ethan and #ManiSankar I too had a same problem in my scenario as well. I solved this using some kind of brute force techinque. What i have done is
Before this
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
I have added replace condition in my code by replacing parameter with the required value then called "Execute Immediate" without "using" clause
lv_query := replace(lv_query, ':1',col_name);
EXECUTE IMMEDIATE lv_query INTO col_value;
I don't know this is optimal one but served purpose for what i am expecting..
Please advice if this one recommended or not...

oracle sql dynamic query to select column in cursor

I've declared the below cursor and variables:
l_level VARCHAR2(100);
l_level_value VARCHAR2(100);
l_select_clause CLOB;
CURSOR l_data IS
SELECT LEVEL1, LEVEL2, LEVEL3
FROM LEVELS;
Then I loop through the cursor:
FOR c1line IN l_data
LOOP
CASE WHEN c1line.LEVEL1 IS NULL THEN l_level := 'c1line.LEVEL2'
WHEN c1line.LEVEL2 IS NULL THEN l_level := 'c1line.LEVEL3'
WHEN c1line.LEVEL3 IS NULL THEN l_level := 'c1line.LEVEL4'
ELSE l_level := NULL
END CASE;
END LOOP;
l_select_clause := 'SELECT ' || l_level || ' INTO l_level_value FROM dual;';
EXECUTE IMMEDIATE l_select_clause;
And then I have some other statements that execute depending on what is selected into the variable l_level_value
My problem is that when execute my procedure I get the following error:
ORA-00904: "C1LINE"."LEVEL2": invalid identifier
ORA-06512: at "MY_PROCEDURE", line 110
ORA-06512: at line 2
Does anyone know what I have done wrong?
Thanks
About the Actual Error c1line.LEVEL1 , you open the cursor dynamically? it seems valid in thee code you have shown
Then..
EXECUTE IMMEDIATE Accepts bind variable, whereas INTO should be after Execution only. To mention, Semicolon (;) is not needed in the Query String
l_select_clause := 'SELECT ' || l_level || ' FROM dual';
EXECUTE IMMEDIATE l_select_clause INTO l_level_value;
My Question is, do you really need dynamic script? I mean same operation can be performed as
FOR c1line IN l_data
LOOP
CASE WHEN c1line.LEVEL1 IS NULL THEN l_level_value := c1line.LEVEL2
WHEN c1line.LEVEL2 IS NULL THEN l_level_value := c1line.LEVEL3
WHEN c1line.LEVEL3 IS NULL THEN l_level_value := c1line.LEVEL4
ELSE l_level_value := NULL
END CASE;
END LOOP;
Why taking two steps? First assign the value to l_level and then assign same value to l_level_value? That too using dynamic scripts?
Always remember, dynamic script should be the last and last options. You should avoid it.

Resources