I have a function like this:
create or replace function params
(
p_pr varchar2,
p_qu_id varchar2,
p_date date := sysdate,
p_param varchar2 := null
)
return varchar2
as
...
sql_stmt varchar2(4000);
rc sys_refcursor;
...
BEGIN
sql_stmt := 'select parameter_name, parameter_value from ' || p_pr | '.v_view where query_id = ''' || p_qu_id || '''';
IF p_param IS NOT NULL
THEN
sql_stmt := sql_stmt || ' and parameter_value=''' || p_param || '''';
END IF;
OPEN rc FOR sql_stmt;
LOOP
FETCH rc
INTO v_param_name, v_param_value;
EXIT WHEN rc%NOTFOUND;
EXIT WHEN v_param_value is NULL;
....
DBA said this function using hard parse, I must use bind variable in this function. How can I do that?
Thanks.
I must use bind variable in this function.
The solution is to use a placeholder in the template SQL ...
sql_stmt := sql_stmt || ' and parameter_value= :p1';
... then pass the actual value with the USING clause when you open the ref cursor.
Things are slightly tricky because you are executing different statements depending on whether the parameter is populated. So you need to do something like this instead:
sql_stmt := 'select parameter_name, parameter_value from ' || p_pr
|| '.v_view where query_id =:p1';
IF p_param IS NOT NULL
THEN
sql_stmt := sql_stmt || ' and parameter_value= :p2';
OPEN rc FOR sql_stmt using p_qu_id, p_param;
else
OPEN rc FOR sql_stmt using p_qu_id;
END IF;
LOOP
Note that p_pr - a schema name - cannot be replaced with a bind variable.
Related
I am building a function on PL/SQL using Oracle 11g.
I am trying to use a table variable within an EXECUTE IMMEDIATE statement, but it is not working, as you can see:
ERROR at line 1:
ORA-00904: "CENTER_OBJECTS": invalid identifier
ORA-06512: at "HIGIIA.KNN_JOIN", line 18
The code I am using is...
First, the type definitions
CREATE TYPE join_t IS OBJECT (
inn char(40),
out char(40)
);
/
CREATE TYPE join_jt IS TABLE OF join_t;
/
CREATE TYPE blob_t IS OBJECT (
id CHAR(40),
fv BLOB
);
/
CREATE TYPE blob_tt IS TABLE OF blob_t;
/
The function is:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2, blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER ) RETURN join_jt
IS
var_fv BLOB;
var_id CHAR(40);
center_objects blob_tt := blob_tt();
retval join_jt := join_jt ();
join_table join_jt := join_jt();
sql_stmt1 varchar2(400);
sql_stmt2 varchar2(400);
BEGIN
sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' || tab_inn || ' r WHERE ' || dist_alg || '_knn(r.' || blob_col2 || ', center_objects(idx).' || blob_col1 || ')<=' || kv;
dbms_output.put_line(sql_stmt2);
EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
for idx in center_objects.first()..center_objects.last()
loop
--SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) BULK COLLECT INTO join_table FROM londonfv r WHERE manhattan_knn(r.fv, center_objects(idx).fv) <=5;
EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table;
for idx2 in join_table.first()..join_table.last()
loop
retval.extend();
retval(retval.count()) := join_table(idx2);
end loop;
end loop;
RETURN retval;
END;
/
To run the function:
select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5));
I am trying to use run the statement 'SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) BULK COLLECT INTO join_table FROM london r WHERE manhattan_knn(r.fv, center_objects(idx).fv) <=5' using the EXECUTE IMMEDIATE, but it does not work because I am using a variable in it.
Can someone give me a hand on it?
Thanks in advance!
You can't refer to a local PL/SQL variable inside a dynamic SQL statement, because it is out of scope within the SQL context used by the dynamic call. You could replace your first call:
SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' ...
with a bind variable:
SELECT join_t(ROWIDTOCHAR(r.rowid), :id FROM ' ...
EXECUTE IMMEDIATE ... USING center_objects(idx).id ...
but you can't do what when the object attribute is variable too:
... ', center_objects(idx).' || blob_col1 || ')<='...
although - at least in the example you've shown - the only object attribute name available is fv, regardless of the table column names passed in to the function - so that could be hard-coded; and thus a bind variable could be used:
... ', :fv)<='...
EXECUTE IMMEDIATE ... USING center_objects(idx).id, center_objects(idx).fv ...
and the kv value should also be a bind variable, so you'd end up with:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
center_objects blob_tt := blob_tt();
retval join_jt := join_jt ();
join_table join_jt := join_jt();
sql_stmt1 varchar2(400);
sql_stmt2 varchar2(400);
BEGIN
sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), :id) FROM ' || tab_inn || ' r WHERE '
|| dist_alg || '_knn(r.' || blob_col2 || ', :fv)<= :kv';
dbms_output.put_line(sql_stmt1);
dbms_output.put_line(sql_stmt2);
EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
for idx in center_objects.first()..center_objects.last()
loop
EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table
USING center_objects(idx).id, center_objects(idx).fv, kv;
for idx2 in join_table.first()..join_table.last()
loop
retval.extend();
retval(retval.count()) := join_table(idx2);
end loop;
end loop;
RETURN retval;
END;
/
As far as I can tell you could still do the join within the dynamic SQL statement, and eliminate the loops and the need for the intermediate center_objects and join_table collections:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
retval join_jt;
sql_stmt varchar2(400);
BEGIN
sql_stmt := 'SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))'
|| ' FROM ' || tab_inn || ' tinn JOIN ' || tab_out || ' tout'
|| ' ON ' || dist_alg || '_knn(tinn.fv, tout.fv) <= :kv';
dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt BULK COLLECT INTO retval USING kv;
RETURN retval;
END;
/
When you call it as you've shown:
select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5));
that's the equivalent of the hard-coded:
SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))
FROM london tinn
JOIN cophirfv tout
ON manhattan_knn(tinn.fv, tout.fv) <= 5
... so I guess you can verify whether that hard-coded version gives you the results you expect first. (Adding sample data and expected results to the question would have helped, of course).
That join condition may be expensive, depending on what the function is doing, how may rows are in each table (as every row in each table has to be compared with every row in the other), whether you actually have other filters, etc. The loop version would be even worse though. Without more information there isn't much to be done about that anyway.
As an aside, using varchar2 instead of char for the object attributes would be more normal; that's also the data type returned by the rowidtochar() function.
Here is my procedure code :
create or replace Procedure SP_CUSTOMER_PRODUCT_QRY
( PARENT_PARTYID IN varchar2,PRODUCT_ID IN varchar2,PRODUCT_STATUS IN varchar2,PAGING IN varchar2,OFFSET IN varchar2,LIST_OF_PARTIES OUT VARCHAR2 )
AS
V_SQL_WHERE varchar2(10000):= null;
V_SQL varchar2(10000):='SELECT PARTYID FROM TABLE1';
V_check varchar2(10000) := '' ;
BEGIN
IF(PARENT_PARTYID IS NOT NULL) THEN
V_SQL_WHERE := V_SQL_WHERE || ' PARENT_PARTYID=:1';
END IF;
IF(PRODUCT_ID IS NOT NULL) THEN
IF (V_SQL_WHERE is not null) THEN
V_SQL_WHERE := V_SQL_WHERE || ' AND ';
END IF;
V_SQL_WHERE := V_SQL_WHERE || ' PRODUCT_ID=:2';
END IF;
IF(PRODUCT_STATUS IS NOT NULL) THEN
IF (V_SQL_WHERE is not null) THEN
V_SQL_WHERE := V_SQL_WHERE || ' AND ';
END IF;
V_SQL_WHERE := V_SQL_WHERE || ' PRODUCT_STATUS=:3';
END IF;
IF (V_SQL_WHERE is not null) then
V_SQL := V_SQL || ' WHERE ' || V_SQL_WHERE;
end if;
dbms_output.put_line(V_SQL);
EXECUTE IMMEDIATE V_SQL INTO LIST_OF_PARTIES;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_check:= '' ;
END;
I have used bind variables ,unable to understand the issue.Is it mandatory to bind all parameters even if they are not used in procedure
When I am passing any input I am getting error as
java.sql.SQLException: ORA-01008: not all variables bound
In this case there is no need for using dynamic plsql. Try to do this this way:
CREATE OR REPLACE PROCEDURE SP_CUSTOMER_PRODUCT_QRY
( P_PARENT_PARTYID IN VARCHAR2,
P_PRODUCT_ID IN VARCHAR2,
P_PRODUCT_STATUS IN VARCHAR2,
P_PAGING IN VARCHAR2,
P_OFFSET IN VARCHAR2,
P_LIST_OF_PARTIES OUT VARCHAR2 )
AS
CURSOR CUR_PARTYID
IS
SELECT PARTYID
FROM TABLE1
WHERE (P_PARENT_PARTYID IS NULL
OR PARENT_PARTYID = P_PARENT_PARTYID)
AND (P_PRODUCT_ID IS NULL
OR PRODUCT_ID = P_PRODUCT_ID)
AND (P_PRODUCT_STATUS IS NULL
OR PRODUCT_STATUS = P_PRODUCT_STATUS);
BEGIN
OPEN CUR_PARTYID;
FETCH CUR_PARTYID INTO P_LIST_OF_PARTIES;
CLOSE CUR_PARTYID;
END;
/
You need the "using" clause with your execute immediate for the 3 variables in the where clause.
for example:
EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = :num' USING dept_id;
I have the following code :
procedure Replace(sUser in Varchar2,sNomTable in varchar2,sColonne in varchar2,sID in Varchar2,nbCharAlterer IN NUMBER) is
l_cursor NUMBER;
l_return NUMBER;
l_ref_cursor SYS_REFCURSOR;
TYPE t_tab IS TABLE OF VARCHAR2(4000);
l_tab t_tab;
l_tab_Id t_tab;
sChaine VARCHAR2(4000 CHAR);
sqlReq CONSTANT VARCHAR2(1000):= 'select ' || sId || ',' || sColonne || ' from ' || sUser || '.' || sNomTable ;
begin
--
l_cursor := DBMS_SQL.open_cursor;
DBMS_SQL.parse(l_cursor, sqlReq, DBMS_SQL.NATIVE);
l_return := DBMS_SQL.EXECUTE(l_cursor);
-- Connvert from DBMS_SQL to a REF CURSOR.
l_ref_cursor := DBMS_SQL.to_refcursor(l_cursor);
Here I am getting the following error :
pls 00302 component 'TO_REFCURSOR' must be declared
since my oracle version is 10g.
Any idea of how to do the equivalent in Oracle 10g?
Here's how you could use native dynamic sql:
PROCEDURE p_replace(suser IN VARCHAR2,
snomtable IN VARCHAR2,
scolonne IN VARCHAR2,
sid IN VARCHAR2,
nbcharalterer IN NUMBER) IS
v_validate_sid_col_name VARCHAR2(32);
v_validate_scolonne_col_name VARCHAR2(32);
v_validate_suser VARCHAR2(32);
v_validate_snomtable VARCHAR2(32);
sqlreq VARCHAR2(2000);
refcur sys_refcur;
BEGIN
-- Check the input values are valid identifiers (to avoid sql injection)
-- N.B. this does not check they are valid object names!
v_validate_sid_col_name := dbms_assert.qualified_sql_name(sid);
v_validate_scolonne_col_name := dbms_assert.qualified_sql_name(scolonne);
v_validate_suser := dbms_assert.qualified_sql_name(suser);
v_validate_snomtable := dbms_assert.qualified_sql_name(scolonne);
sqlReq := 'select ' || v_validate_sid_col_name || ',' ||
v_validate_scolonne_col_name ||
' from ' || v_validate_suser || '.' || v_validate_snomtable;
-- or maybe you want to use execute immediate to bulk collect into arrays?
OPEN refcur FOR sqlreq;
...
END p_replace;
Note that I've changed the name of the procedure since "replace" is the name of a pre-existing built-in function, and therefore not a very good name to use.
You don't mention what it is you're going to do with the results of your query, so I wasn't sure if opening a ref cursor is what you actually need, or whether bulk collecting via execute immediate would work better for you.
I have problem Dynamic Call Store Procedure
v_sql := 'begin '|| p_procname || '(''test1'','' test2 '',:v_output2); end;';
execute immediate v_sql
using out v_output2 ;
dbms_output.put_line(v_output2 || ' ' );
In here ı can call procedure with execute immediate .
But my problem is dynamic bind variable . This values comes from log table then i parse for execute_immediate procedure
v_sql := 'begin '|| p_procname || '(''test1'','' test2'',:v_output2); end;';
v_sql1:= ||using|| 'out v_output2 ' ;
execute immediate v_sql
v_sql1;
It doesnt work like that . How can i make dynamic variables bind , because i call a lot of procedure and thats procedure has different in and out parameters.
I hope you can understand what problem i have .How can i pass this problems thx
here is simple procedure
create procedure test_proc(p_user varchar2, p_code varchar2, p_error varchar2) is
begin
p_error := p_user || p_code;
end;
calling code for same ..
Declare
v_test_proc varchar2(50) := 'test_proc';
p_user varchar2(50) := 'test_name';
p_code varchar2(50) := 'test_code';
p_error varchar2(100);
v_sql varchar2(2000);
begin
v_sql := 'begin ' || v_test_proc || '( :1 ,:2, :3 ); end;';
execute immediate v_sql
using p_user, p_code, out p_error;
dbms_output.put_line(p_error);
end;
I am confused as to the meaning of something I just read in the book Oracle PL/SQL Recipes.
In recipe 8-12, it states:
It is not possible to simply pass a NULL value using native dynamic
SQL. At least, you cannot pass a NULL as a literal.
What that suggests to me is that something like the following (A):
DECLARE
v_string varchar2(255);
v_count natural;
BEGIN
v_string := 'SELECT count(*) INTO :count FROM item WHERE color IS NOT NULL';
EXECUTE IMMEDIATE v_string INTO v_count;
dbms_output.put_line('Count: ' || v_count);
END;
Would not work properly. It suggests (B):
Passing an uninitialized variable via the EXECUTE IMMEDIATE statement
will have the same effect as substituting a NULL value for a bind
variable.
The book's example code:
DECLARE
TYPE cur_type IS REF CURSOR;
cur cur_type;
null_value CHAR(1);
sql_string VARCHAR2(150);
emp_rec employees%ROWTYPE;
BEGIN
sql_string := 'SELECT * ' ||
'FROM EMPLOYEES ' ||
'WHERE MANAGER_ID IS :null_val';
OPEN cur FOR sql_string USING null_value;
LOOP
FETCH cur INTO emp_rec;
DBMS_OUTPUT.PUT_LINE(emp_rec.first_name || ' ' || emp_rec.last_name ||
' - ' || emp_rec.email);
EXIT WHEN cur%NOTFOUND;
END LOOP;
CLOSE cur;
END;
/
So my confusion is this: method (A) seems to behave just fine for me, whether I run the query dynamically or not, it is properly selecting records without NULL (or with them, if I invert the conditional).
So what's the deal with this? I'm running Oracle 11g release 2.
What you're not allowed to write is :
OPEN cur FOR sql_string USING NULL;
when you could write :
OPEN cur FOR sql_string USING 'A string';
There's nothing more to it.