Issue in passing parameter in oracle Pl-Sql function - oracle

I’m write one function funGetAccount’ inOracle Pl-Sql` passing parameter ProdCd but query has consider its statement how it solved I’m not understand my req is same query pls don’t change the query design.
Function funGetAccount (ProdCd in Number)
Return Varchar2 Is
varResult Varchar2(2000);
Begin
varResult := ' Select AC_NO, NAME From ('
|| ' Select AC_NO, NAME'
|| ' From Tab1 Where PRODUCT_CD = ProdCd)';
varResult := varResult;
Return varResult;
End funGetAccount;

This will return AC_NO and NAME concatenated by space. You cannot return 2 values from it.
Also make sure that your query is returning only 1 row for the given prodcd else it return only first row.
create or replace function funGetAccount (ProdCd in Number)
Return Varchar2 Is
varResult Varchar2(2000);
Begin
Select AC_NO||' '||NAME
into varResult
From Tab1 Where PRODUCT_CD = ProdCd and rownum <2;
Return varResult;
End funGetAccount;

I don't understand what definitely you want to archive with this function: return select statement or retrieve data.
In the first case you should do:
Function funGetAccount (ProdCd in Number)
Return Varchar2
Is
varResult Varchar2(2000);
Begin
varResult := ' Select AC_NO, NAME From ('
|| ' Select AC_NO, NAME'
|| ' From Tab1 '
|| ' Where PRODUCT_CD = ' || ProdCd || ')';
Return varResult;
End funGetAccount;
In the second (retrieve data)
create or replace function funGetAccount (ProdCd in Number)
Return Varchar2
Is
varResult Varchar2(2000);
Begin
Select AC_NO||' '||NAME
into varResult
From Tab1
Where PRODUCT_CD = ProdCd;
Return varResult;
End funGetAccount;

As per the question i can see there are two requirements which can come into picture.
1) OP wants to return the raw SQL statement through FUNCTION.
2) OP wants SQL output in CONCATENATED WAY.
I have focused on 2 point where i have incorporated some of the good coding practices like EXCEPTION Handling. Hope it helps.
FUNCTION funGetAccount(
ProdCd IN NUMBER)
RETURN VARCHAR2
IS
p_result_out VARCHAR2(100);
varResult VARCHAR2(2000);
BEGIN
varResult := 'Select AC_NO||'',''||NAME From
( Select AC_NO, NAME From Tab1 Where PRODUCT_CD = '''||ProdCd||''')';
BEGIN --> Exception handling missed
EXECUTE IMMEDIATE varResult INTO p_result_out;
EXCEPTION
WHEN OTHERS THEN --> Others is broad term you can catch specific exceptions like no data found too many rows etc
NULL; -- Your action logic
END;
RETURN p_result_out;
END funGetAccount;

Function funGetAccount (ProdCd in Number)
Return Varchar2 Is
varResult Varchar2(2000);
Begin
varResult := ' Select AC_NO, NAME From ('
|| ' Select AC_NO, NAME'
|| ' From Tab1 Where PRODUCT_CD = '''|| ProdCd ||''')';
varResult := varResult;
Return varResult;
End funGetAccount;

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}'.

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

How to get name of caller object in oracle

In Oracle, it's complicated to properly get name of caller object when it is wrapped in a package. Here is my attempt to achieve this:
CREATE OR REPLACE function GET_MY_NAME return varchar2 is
v_owner varchar2(50);
v_name varchar2(50);
v_lineno number ;
v_caller_t varchar2(50);
begin
-- let's determine the object
owa_util.who_called_me(
v_owner
, v_name
, v_lineno
, v_caller_t
);
dbms_output.put_line(v_caller_t || ' ' || v_owner || '.' || v_name || ' at ' || v_lineno);
with s as (
select s.*
, max(line - 1) over (partition by name order by line rows between 1 following and 1 following) to_line
, regexp_substr(s.text, '\w+', 1, 2) func_name
from all_source s
where s.type = v_caller_t
and s.owner = v_owner
and s.name = v_name
and regexp_like(s.text, 'procedure|function')
)
select max(nvl(s.func_name, v_name ))
, max(nvl(s.name , v_owner))
into v_name, v_owner
from s
where v_lineno between s.line and nvl(s.to_line, 9999999);
return v_owner || '.' || v_name;
end;
The idea is to determine lines within the source of each inner object is located and take corresponding object. This works fine for packages, but for functions and procedures it cannot determine if it was called from the main block. Here is a little demo:
create or replace function check_my_name return varchar2 is
function sub_check_my_name return varchar2 is
begin
return get_my_name;
end;
begin
dbms_output.put_line(sub_check_my_name);
return get_my_name;
end;
/
You can see that sub_check_my_name works, but direct get_my_name call also returns sub_check_my_name. How can I determine if the call occures inside main function block?
I did something as simple as this,
Declared a variable as below,
call_stack VARCHAR2(3000) := '';
Now use the below to get the levels of the calls,
FOR j IN REVERSE 1..utl_call_stack.dynamic_depth() LOOP
call_stack := CASE
WHEN call_stack IS NOT NULL THEN
call_stack || '->'
END
|| nvl(utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(j)), '');
END LOOP;
dbms_output.put_line(call_stack);
This gives the call hierarchy in the call_stack separated by '->'

Oracle Create Function

I tried to create an Oracle function where table name, column name and where string are dynamic parameters:
CREATE OR REPLACE FUNCTION MYSCHEMA.myFunctionName(
tableName in nvarchar2,
columnName in nvarchar2,
whereStr in nvarchar2)
RETURN nvarchar2
IS nActive nvarchar2(2000);
BEGIN
declare
querystr nvarchar2(2000) ;
result nvarchar2(2000);
begin
querystr :='
select listagg('+columnName+','+','+') within group (order by '+columnName+')
from '+tableName+' where 1 = 1 '+whereStr+';';
EXECUTE IMMEDIATE querystr
INTO result;
nActive := result;
RETURN ( nActive );
end;
END ;
/
But it gives me error "Warning: compiled but with compilation errors".
What am I doing wrong?
For concatenate strings in Oracle use || not +
You don't need ; at the end of execute immediate query string
You need to escape ' using ''.
... as #Aleksej said
Execute immediate need query string in CHAR or VARCHAR2;
listagg return either raw or VARCHAR2
CREATE OR REPLACE FUNCTION MYSCHEMA.myFunctionName(
tableName in varchar2,
columnName in varchar2,
whereStr in varchar2)
RETURN varchar2
BEGIN
declare
querystr varchar2(2000) ;
result varchar2(2000);
begin
querystr :='
select listagg('|| columnName || ', '','') within group (order by ' ||columnName ||')
from ' || tableName || ' where 1 = 1 ' || whereStr;
EXECUTE IMMEDIATE querystr INTO result;
return result;
end;
END ;
/
You need to change the type of your variables, considering that
"The return data type is RAW if the measure column is RAW; otherwise the return value is VARCHAR2" (documentation)
EXECUTE needs a VARCHAR2: "It must be of type CHAR or VARCHAR2, not NCHAR or NVARCHAR2"
So:
declare
querystr varchar2(2000) ;
result VARCHAR2(32767);
begin
...

Dynamically assigning variables oracle sql

I have a table attribute_config with below columns:
table_name column_name key
Let us say is has below 2 rows
account accountphone accountnum
customer customernumber customerid
Key can be only accountnum or customerid.
I have to write code which will accept (i_accountnum,i_customerid) and;
fetch the respective values from columns mentioned in column_name in tables mentioned in table_name using the key in where condition.
For ex: select accountphone from account where accountnum = i_accountnum
select customernumber from customer where customerid = i_customerid
the complete query should be formed dynamically, whether to pass i_accountnum or i_customerid in the query also needs to be decided dynamically. if key - accountnum, i_accountnum will be passed to where condition.
I have been trying on these lines so far, this is not working, i know it is wrong.
declare
v_accountnum varchar2(20);
v_customerid varchar2(20);
v_attribute_value varchar2(20);
v_stmt varchar2(255);
begin
Account_Num := 'TestCustomer'; -- input to the function
v_customer_ref := 'TestAccount'; -- input to the function
for i in (Select * from attribute_config) loop
v_stmt := 'select ' || i.column_name || ' from ' || i.table_name ||' where ' || i.key|| ' = v_' || i.key;
execute immediate v_Stmt into v_attribute_value;
end loop;
end;
This will fix your code, but I do not see any advantage of using dynamic query when your code should accept 2 parameters(i_accountnum,i_customerid) - which is already static situation and fetch the relevant values, perhaps only in learning purposes.
declare
procedure fecth_values(i_accountnum account.accountnum%type,
i_customerid customer.customerid%type) return varchar2 is
v_attribute_value varchar2(20);
begin
for i in (select * from attribute_config) loop
execute immediate 'select ' || i.column_name || ' from ' ||
i.table_name || ' where ' || i.key || ' = ' || case when i.key = 'accountnum' then i_accountnum when i.key = 'customerid' then i_customerid end;
into v_attribute_value;
dbms_output.put_line(v_attribute_value);
end loop;
return null;
end;
begin
fecth_values(1, 1);
end;
Your where clause was wrong the i.key should be compared against the inputed values, not the 'v_' || i.key, which is undeclared when you execute your stmt.

Resources