How to pass 2 arguments to a procedure? - oracle

I need to transform this code to accept
EXECUTE concatenate_strings('big','dog');
instead of how it currently has the values written within the code.
create or replace
procedure concatenate_strings as
str1 varchar2(10) := 'begin';
str2 varchar2(10) := 'end';
result varchar2(20);
begin
result := str1 || ' ' || str2;
dbms_output.put_line('The result is: ' || result);
end;

Hi you need to pass these in as parameters see sample below
create or replace
procedure concatenate_strings(p_str1 in varcahr2, p_str2 in varchar2) as
result varchar2(20);
begin
result := p_str1 || ' ' || p_str2;
dbms_output.put_line('The result is: ' || result);
end;

Related

how to pass formatted string to column and same fetch it in package in PL/SQL

I have error master table which contain description like 'Error in table abc in xyz column.' I need to format string for column name which is xyz here. Where ever I need to call this table I will pass column name and then I will get expected description.
Ex - Insert into errorTabl values(01,There is error in {0})
Whenever inside package I need to retrieve value of 01 then I will pass column name col1 so then expected value will be as below :
01 There is error in col1
Request you to please help me for insert and select both statements.
Though this doesn't make sense, maybe the code below could help you to start with something or to clarify your problem.
NOTE: the code below is here just to show you the basics - there is no sense in it for any kind of production. You are the one to adjust it to your context.
So, the package to put and get things into or from errors table:
CREATE OR REPLACE PACKAGE ERRS AS
Procedure putError(p_table IN VarChar2, p_column IN VarChar2);
Function getError(p_table VarChar2, p_column VarChar2) RETURN VarChar2;
END ERRS;
-- ---------------------------------------------------------------------------------
CREATE OR REPLACE PACKAGE BODY ERRS AS
Procedure putError(p_table IN VarChar2, p_column IN VarChar2) AS
BEGIN
Declare
mSql VarChar2(512) := '';
sq VarChar2(1) := '''';
Begin
mSql := 'Insert Into ERRORTABLE values( ' || sq || '01' || sq || ', ' || sq ||
'There is error in table ' || p_table || ' in ' || p_column || ' column' || sq || ')';
Execute Immediate mSql;
Commit;
End;
END putError;
-- -------------------------------------------------------------------------------
Function getError(p_table VarChar2, p_column VarChar2) RETURN VarChar2 IS
BEGIN
Declare
Cursor c IS
Select ERR_DESC From ERRORTABLE Where ERR_DESC Like('%table ' || p_table || ' in ' || p_column || '%');
mRet VarChar2(512) := '';
mDesc VarChar2(512) := '';
Begin
Open c;
LOOP
FETCH c into mDesc;
EXIT WHEN c%NOTFOUND;
mRet := '01 ' || mDesc || Chr(10);
END LOOP;
Close c;
RETURN RTRIM(mRet, Chr(10));
End;
END getError;
END ERRS;
Now the calling code to insert 5 records (once more - this is senseless) and to get you one of them...
set serveroutput on
DECLARE
errMsg VarChar2(512);
BEGIN
ERRS.putError('T_ABC', 'C_XYZ');
ERRS.putError('T_ABC', 'C_MNO');
ERRS.putError('T_ABC', 'C_PQR');
ERRS.putError('T_DEF', 'C_MNO');
ERRS.putError('T_DEF', 'C_XYZ');
--
errMsg := ERRS.getError('T_ABC', 'C_XYZ');
dbms_output.put_line(errMsg);
END;
/* R e s u l t :
anonymous block completed
01There is error in table T_ABC in C_XYZ column
*/
Just needed to pass double colon in insert query so then it will take single colon in table.
Ex - Insert into errorTabl values(01,There is error in ''{0}'')
In table it will be look like
**Id** **Description**
01 There is error in'{0}'.

How to know if a procedure has successfully executed using dynamic sql in PL/SQL?

I am trying to get an OUT parameter calling a procedure dynamically using bind parameters. The procedure has some IN parameters and an OUT parameter. The IN parameters are fetched from a single column containing all the parameters separated with comma. I am adding the bind parameter after that. But its giving an error. How to resolve this issue? I am returning 'S' for successful execution of the procedure being called and 'R' for unsuccessful execution. I need this returning data to process some further coding.
CREATE OR REPLACE PROCEDURE PROC_TEST (P_SL_NO VARCHAR2,
P_DATA VARCHAR2, /*P_DATA = A,B,C,D (COMES FROM A COLUMN IN A TABLE)*/
P_MSG OUT NUMBER)
IS
V_PROC VARCHAR2(250) := 'PKG_PROC.PROC_INTER#DBLINK';--PROCEDURE TO BE CALLED DYNAMICALLY
V_VAR2 VARCHAR2(4000);
V_BEGIN VARCHAR2(10) := 'BEGIN ';
V_END VARCHAR2(10) := ' END';
PARAM_MSG CHAR(1);
BEGIN
V_VAR2 := V_BEGIN||V_PROC||'('||P_DATA||','||':X'||')'||';'||V_END||';'; --:X IS AN OUT PARAMETER HERE RETURNS 'S' FOR SUCCESS OR 'R' IF UNSUCCESSFUL
EXECUTE IMMEDIATE V_VAR2 USING OUT PARAM_MSG;
DBMS_OUTPUT.PUT_LINE(PARAM_MSG);
P_MSG := 1;
EXCEPTION WHEN OTHERS THEN
P_MSG := 0;
END;
/
You are constructing pl/sql block without single quotes where they are needed. Your V_VAR2 variable with given values returns something like:
BEGIN PKG_PROC.PROC_INTER#DBLINK(A,B,C,D,:X);END;;
So, you need to insert single quotes with parameter values to get the executable pl/sql block.
I suggest you to declare a variable for single quote with default value of 4 single quotes (which in the code wil be represented as one) and change your code for V_VAR2 to look like this:
Declare
-- ... ... ...
sq VARCHAR2(1) := '''';
BEGIN
-- ... ... ...
-- ... ... ...
V_VAR2 := V_BEGIN || Chr(10) || Chr(9) || V_PROC || '(' || sq || SubStr(P_DATA, 1, 2) || sq || SubStr(P_DATA,3, 2) || sq || SubStr(P_DATA, 5, 2) || sq || SubStr(P_DATA, 7, 1) || sq || ', :X);' || Chr(10) || V_END;
- ... ... ...
-- ... ... ...
END;
-- Result for V_VAR2 should be ok. now
-- BEGIN
-- PKG_PROC.PROC_INTER#DBLINK('A,'B,'C,'D', :X);
-- END;
I don't knowwhat that procedure does afterwards but the error is probabli caused by EXEC IMMEDIATE with bad pl/sql code. Regards...

Bulk into table objects

I want populate my table of objects and I try of different ways but I canĀ“t. With this code I have an extensive error: "Encountered the symbol "L_T_O_TYPE" when expecting one of the following: .(*#_-+/", it is a big message
CREATE OR REPLACE TYPE O_Type AS OBJECT (
depar_id NUMBER,
depar_name VARCHAR2(20),
man_id NUMBER,
loca_id NUMBER,
CONSTRUCTOR FUNCTION O_Type(
depar_id NUMBER,
depar_name VARCHAR2,
man_id NUMBER,
loca_id NUMBER)
RETURN SELF AS RESULT
);
CREATE OR REPLACE TYPE BODY O_Type IS
CONSTRUCTOR FUNCTION O_Type(
depar_id NUMBER,
depar_name VARCHAR2,
man_id NUMBER,
loca_id NUMBER,
)RETURN AS SELF AS RESULT IS
BEGIN
self.depar_id := depar_id;
self.depar_name := depar_name;
self.man_id := man_id;
self.loca_id := loca_id;
RETURN;
END;
END;
/
CREATE OR REPLACE TYPE T_o_type AS TABLE OF O_Type;
/
DECLARE
CURSOR C_depar IS SELECT *
FROM departments;
TYPE T_C_DEPAR IS TABLE OF C_depar%ROWTYPE;
L_TABLE T_C_DEPAR;
l_T_o_type T_o_type;
BEGIN
l_T_o_type := T_o_type();
OPEN C_depar;
LOOP
FETCH C_depar BULK COLLECT INTO L_TABLE;
EXIT WHEN C_depar%NOTFOUND;
FORALL i IN 1..L_TABLE.count
l_T_o_type(i) : = T_o_type(L_TABLE.DEPARTMENT_ID, L_TABLE.DEPARTMENT_NAME, L_TABLE.MANAGER_ID , L_TABLE.LOCATION_ID)
END LOOP;
CLOSE C_depar;
END;
/
Can someone tell me the best way to populate my object table?
"Best" is subjective.
If you want all the rows in a single collection, use SELECT ... BULK COLLECT INTO:
DECLARE
l_depts T_o_type;
BEGIN
SELECT O_Type(id, name, man_id, loc_id)
BULK COLLECT INTO l_depts
FROM departments;
FOR i IN 1 .. l_depts.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
l_depts(i).depar_id
|| ' ' || l_depts(i).depar_name
|| ' ' || l_depts(i).man_id
|| ' ' || l_depts(i).loca_id
);
END LOOP;
END;
/
If you want to process the rows in batches then you can use:
DECLARE
CURSOR C_depar IS
SELECT O_Type(id, name, man_id, loc_id)
FROM departments;
l_depts T_o_type;
BEGIN
OPEN C_depar;
LOOP
FETCH C_depar BULK COLLECT INTO L_depts LIMIT 10;
FOR i IN 1 .. l_depts.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
l_depts(i).depar_id
|| ' ' || l_depts(i).depar_name
|| ' ' || l_depts(i).man_id
|| ' ' || l_depts(i).loca_id
);
END LOOP;
EXIT WHEN C_depar%NOTFOUND;
END LOOP;
CLOSE C_depar;
END;
/
db<>fiddle here

Can we use collection variable as a table in execute immediate select statement? [duplicate]

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.

Oracle function with table as input parameter

How would you create a function in Oracle that has a table as an input parameter and return a string? Here is my attempt but is returning an error:
create or replace type temp_table as object (col_name varchar(100));
/
create or replace type col_table as TABLE of temp_table;
/
create or replace FUNCTION COLUMN_HEADERS
(col_name in col_table)
RETURN VARCHAR2
is
return_string VARCHAR2(4096);
BEGIN
return_string := '';
for i in 1 .. col_name.count loop
return_string := return_string || ' ''' || col_name(i) || ''' as ' || regexp_replace(col_name(i), '[ \+]', '_');
if i < col_name.count then
return_string := return_string || ',';
end if;
end loop;
RETURN return_string;
END;
I get the following:
Error(9,9): PL/SQL: Statement ignored
Error(9,26): PLS-00306: wrong number or types of arguments in call to '||'
Which points to this line:
return_string := return_string || ' ''' || col_name(i) || ''' as ' || regexp_replace(col_name(i), '[ \+]', '_');
My guess is that col_name(i) doesn't return a string but using VALUE() or TO_CHAR() gives me other errors. Does anyone know how to debug this?
When you reference an index in the table using col_name(i), you then also need to reference the object property of that table index as in col_name(i).col_name. In your case you used col_name both as the object property and as your function argument. For clarity you might change that. This compiled for me:
create or replace type temp_table is object (col_name varchar(100));
/
create or replace type col_table is TABLE of temp_table;
/
create or replace FUNCTION COLUMN_HEADERS
(col_name in col_table)
RETURN VARCHAR2
is
return_string varchar2(4096);
BEGIN
return_string := '';
for i in 1 .. col_name.count loop
return_string := return_string || ' ''' || col_name(i).col_name || ''' as ' || regexp_replace(col_name(i).col_name, '[ \+]', '_');
if i < col_name.count then
return_string := return_string || ',';
end if;
end loop;
return return_string;
END;

Resources