Using bind variables with dynamic SELECT INTO clause in PL/SQL
No you can't use bind variables that way. In your second example :into_bind
in v_query_str
is just a placeholder for value of variable v_num_of_employees
. Your select into statement will turn into something like:
SELECT COUNT(*) INTO FROM emp_...
because the value of v_num_of_employees
is null
at EXECUTE IMMEDIATE
.
Your first example presents the correct way to bind the return value to a variable.
Edit
The original poster has edited the second code block that I'm referring in my answer to use OUT
parameter mode for v_num_of_employees
instead of the default IN
mode. This modification makes the both examples functionally equivalent.
Dynamic SQL to select into a variable
You cannot use placeholders
for table columns in dynamic query. However you can use concatenate to achieve your requirement. See below:
DECLARE
loc_stmt VARCHAR2 (200);
loc_return VARCHAR2 (30) := NULL;
loc_code_tab VARCHAR2 (30);
P_TABLE_NAME VARCHAR2 (100) := 'BILLG_FRQNCY_TYPE';
P_CODE NUMBER := 1;
BEGIN
loc_code_tab := SUBSTR (P_TABLE_NAME, 1, LENGTH (P_TABLE_NAME) - 3);
loc_stmt :=
'SELECT '
|| loc_code_tab
|| '_DESC'
|| ' FROM '
|| loc_code_tab
|| ' WHERE '
|| P_TABLE_NAME
|| ' = '
|| P_CODE;
EXECUTE IMMEDIATE loc_stmt INTO loc_return ;
DBMS_OUTPUT.PUT_LINE (loc_return);
END;
/
sql select statement as bind variable for dynamic plsql block
If I understand well, you need to run an entire dynamic PLSQL block by using as SQL query as a bind variable; if so, you can try this way:
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 --
14 /* whatever you need to do in your block */
15 for i in emp_id.first .. emp_id.last loop
16 dbms_output.put_line(emp_id(i));
17 end loop;
18 END;';
19
20 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
21 end;
22 /
1
PL/SQL procedure successfully completed.
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 /* this does nothing */
14 END;';
15
16 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL>
PL-SQL: why does a dynamic statement using bind-variable input not work?
You can't use a bind variable as a table_reference
. As documented in the link, the table_reference
must be static part of the statement.
select * from :table -- ILLEGAL
Contrary to that the usage in the WHERE
clause is allowed
where :row = :value -- LEGAL, but questionable
but it doesn't do what you expect. The where
clause returns TRUE, if both bind variables passed are same.
I.e. it is equivalent to
where 'name' = 'xxxx'
and not to
where name = 'xxxx'
dynamic sql, bind variables and dynamic USING
A simple but static solution assumes there is a known list of bind variables and their data types and the dynamic queries can use only a subset of those bind variables.
Here an example for five VARCHAR
bind variables. You generates this PL/SQL block:
DECLARE
L_VC1 VARCHAR2(4000) := :VC1;
L_VC2 VARCHAR2(4000) := :VC2;
L_VC3 VARCHAR2(4000) := :VC3;
L_VC4 VARCHAR2(4000) := :VC4;
L_VC5 VARCHAR2(4000) := :VC5;
BEGIN
-- here an statement using L_VC1 up to L_VC5
-- eg
INSERT INTO test (vc1,vc2,vc3) values (L_VC1, L_VC2, L_VC3);
END;
and executes it passing the full list of values (some of them left NULL
).
EXECUTE IMMEDIATE my_generated_block USING vc1, vc2, vc3, vc4, vc5;
A nice feature is, that the dynamic SQL may use one bind variable several times without the need of extending the USING
parameters.
This must of course be maintained, if a new variable appears.
What is an alternative?
In my opinion to be truly dynamic in the bind variable list, you can't solve this with EXECUTE IMMEDIATE
and you must take a step down towards DBMS_SQL
.
Here the idea, without details how to implement it in PL/SQL:
DECLARE
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'INSERT INTO test (vc1,vc2,vc3) values (:vc1, :vc2, :vc3)',
DBMS_SQL.NATIVE);
-- call in a loop for each BV
DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc1', 'x');
DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc2', 'y');
DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc3', 'z');
---
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
/
You'll have to call the DBMS_SQL.BIND_VARIABLE
in a loop for each bind variable name and the value.
Note that I'm completely ignoring the datatype of the bind variables, which should be considered as well and would probably make the solution a bit more involved but possible to solve.
Which solution is more feasible?
If your relational database design is truly key - value (i.e. you may introduce new bind variables without a DDL
) you'll have to follow the second option. Otherwise, i.e. in case that table structure modification is required to get a new bind variable and if the change frequency is low, I'd prefer the first option.
Oracle SQL - How to define a date bind variable based on a select statement
So, to sum up my comments from above, you don't need PL/SQL
to do this. You could do all of your script with straight SQL
:
CREATE TABLE A_TEST_TABLE
(
user varchar2(100),
product varchar2(100),
datum date
);
INSERT INTO a_test_table
SELECT usr.user,
usr.product,
usr.datum
FROM users_table
WHERE usr.datum = (SELECT c_year_ytd FROM master_date_automation)
AND usr.user= '123456789'
Drop the table if you need to first.
Oracle - using bind variable in LIKE clause of dynamic cursor
This is a subtle one. It's often useful to start with a static statement, get that right, then convert it to dynamic SQL.
In non-dynamic SQL we might do it like this:
SELECT column1, column2
FROM my_table
WHERE column1 LIKE '%' || local_var || '%';
The dynamic equivalent is
query := 'SELECT column1, column2
FROM my_table
WHERE column1 LIKE ''%''||:bv1||''%'' ';
Related Topics
Designing a SQL Schema for a Combination of Many-To-Many Relationship (Variations of Products)
Concat All Column Values in SQL
SQL 2005 - the Column Was Specified Multiple Times
How to Do a Case Sensitive Group By
Spark Replacement for Exists and In
Get SQL Xml Attribute Value Using Variable
Convert from Date to Epoch-Oracle
SQL Script to Alter All Foreign Keys to Add on Delete Cascade
Get Multiple Values in SQL Server Cursor
How to Compare 2 Rows from the Same Table (SQL Server)
Division (/) Not Giving My Answer in Postgresql
Query Times Out from Web App But Runs Fine from Management Studio
How to Pass a Temp Table as a Parameter into a Separate Stored Procedure
Hive - Unpivot Functionality in Hive
Insert Dates in the Return from a Query Where There Is None