Calling an internal function inside a procedure PL/SQL - oracle

A bit green with PL/SQL functions but trying to add a function where if the current person does not have an alt_id to get the relative alt_id via the application. Many thanks in advance.
CREATE OR REPLACE PROCEDURE CLIENT.p_Verification(pin_Member_ID IN dbo.member.member_id%Type,
pin_Person_ID IN dbo.person.person_id%type,
pin_user_id IN NUMBER default null,
pioc_ref_cursor IN OUT dbo.pkg_benefit_q.ref_cursor) IS
lv_member_id dbo.person.alt_identifier%Type;
-- other variables
...
Procedure p_get_other_deductions(...
Begin
...
end;
new function here
FUNCTION f_get_alt_id
RETURN Varchar2
IS-- dbo.person.alt_identifier%Type IS
alt_id Varchar2(10); --dbo.person.alt_identifier%Type;
BEGIN
SELECT p.ALT_IDENTIFIER
into alt_id
FROM DBO.PERSON p , DBO.MEMBER m, DBO.APPLICATION a
where p.PERSON_ID = m.PERSON_ID
AND m.MEMBER_ID = a.MEMBER_ID
AND a.APPLICATION_ID= cn_EntAppId
;
RETURN alt_id;
END;
calling it here...
Select dbo.pkg_benefit_s.f_get_value_description('PREFIX',
p.prefix,
'Y',
'N') || case
when p.prefix is not null then
' '
else
Null
end || nvl2(p.first_name, initcap(p.first_name) || ' ', '') ||
nvl2(p.middle_name, initcap(p.middle_name) || ' ', '') ||
nvl2(p.last_name, initcap(p.last_name), '') || ' ' ||
dbo.pkg_benefit_s.f_get_value_description('SUFFIX',
p.suffix,
'Y',
'N'),
nvl2(p.alt_identifier,
f_get_alt_id, -- errors here
''),
nvl2(p.first_name, initcap(p.first_name), ''),
nvl2(p.last_name, initcap(p.last_name), '')
into lv_member_name,
lv_member_id,
lv_member_first_name,
lv_member_last_name
From dbo.person p
where p.person_id = ln_person_id;
... more code
END p_Verification;
/

It would be better practice in a number of ways to create a package that has your procedure and your function listed in the header and the body.
CREATE OR REPLACE PACKAGE CLIENT IS
PROCEDURE p_Verification(pin_Member_ID IN dbo.member.member_id%Type,
pin_Person_ID IN dbo.person.person_id%type,
pin_user_id IN NUMBER default null,
pioc_ref_cursor IN OUT dbo.pkg_benefit_q.ref_cursor);
Procedure p_get_other_deductions(...
FUNCTION f_get_alt_id(alt_id_in IN NUMBER:= NULL) RETURN Varchar2;
END CLIENT;
CREATE OR REPLACE PACKAGE BODY CLIENT IS
END CLIENT;
Notice how I have added a null parameter so if you need to redesign the function you can do so without invalidating any dependent objects.

I added the
alt_id varchar(10) in the declaration section
restored the select query above to simply return
dbo.pkg_benefit_s.f_get_value_description('SUFFIX',
p.suffix,
'Y',
'N'),
p.alt_identifier,
nvl2(p.first_name, initcap(p.first_name), ''),
nvl2(p.last_name, initcap(p.last_name), '')
into lv_member_name,
lv_member_id,
lv_member_first_name,
lv_member_last_name
... everything else is the same...until
I added another section to set the lv_member_id
BEGIN
Select p.alt_identifier
into alt_id
from dbo.person p
where P.PERSON_ID = pin_Person_id;
if alt_id is not null
then
lv_member_id := alt_id;
else
lv_member_id := f_get_alt_id;
end if;
END;
... rest of procedure and end; -- it now compiles

Related

Can i use REGEXP_LIKE as a condition with IF in a PL/SQL block and my string i will pass as parameter

if (NOT REGEXP_LIKE(l_hdr_high_amt_fare_tab(j).fare_basis,
L_HIGH_FARE_FLAG)) then
if l_total_fare > 13000 then
Begin
update tsapp_dwh_staging_tbl
set error_code = 'LA_QC_S10'
where sys_seq_no = p_sys_seq_no;
commit;
and my L_HIGH_FARE_FLAG is parameterized.
which is
+CHARTER(*)|+GRUPO(*)|+GROUP(*)
this is working if i hard-code this string in my code but when i pass string as parameter it wont work.
It worked with
SELECT '' || replace(a.value_text, ',', '|') || ''
FROM tmaac_app_parameters a
WHERE a.key_name = 'EMD_FLAG'
and a.module = 'IO'
and a.sub_module = 'EXTRACT'
and a.function = 'PARAMETER_PRCS';
If you just want to know whetherregexp_like will work in an if condition, then yes it will:
declare
function testit
( stringval varchar2 )
return varchar2
is
begin
if regexp_like(stringval,'^A') then
return 'Y';
else
return 'N';
end if;
end testit;
begin
dbms_output.put_line(testit('Abracadabra!'));
end;
/
Y
PL/SQL procedure successfully completed.
I'm not sure how l_high_fare_flag and tsapp_dwh_staging_tbl etc are related to the question.

Update: Function Error: PLS-00306: wrong number or types of arguments in call to 'get_bus_result_attribute'

I just think the problem of issue and change the function-to-function logic design. Then, the error as mentioned the function (get_result_attribute) - PLS-00306: wrong number or types of arguments in call to 'get_result_attribute'. May I know the function "get_result_attribute" problem in which part?
Coding for the bus station system.
FUNCTION get_bus_result_attribute (iv_result_name varchar2,
iv_result_key varchar2,
iv_result_attribute varchar2)
--define the parameter to get the "sql_result" script result
RETURN varchar2 IS
sql_result varchar2(500);
sql_return varchar2(500);
BEGIN
sql_result := 'SELECT ' || iv_result_attribute || '
FROM MyTable a,
MyTable b
WHERE a.bus_value_set_id = b.bus_value_set_id
AND b.bus_value_set_name = iv_result_name
AND a.enabled_flag = ''Y''
AND a.bus_value = iv_result_key
AND iv_result_name = get_bus_code (v_bus)
AND iv_result_key = get_bus_name(v_group)
AND iv_result_key = iv_result_attribute';
EXECUTE IMMEDIATE sql_result
INTO sql_return; --get the "sql_result" script result
return sql_return;
exception
when others then
return '';
end get_bus_result_attribute;
FUNCTION get_bus_code (v_bus varchar2)
RETURN VARCHAR2 IS
v_get_bus_code_result VARCHAR2(20) ;
BEGIN
SELECT busa.bus_code
INTO v_get_bus_code_result
FROM tbl_bus_code busa, tbl_bus_line busb
WHERE busa.bus_code_set_id = busb.bus_code_set_id
AND busb.bus_line_set_name = 'HK_BUS_CODE'
AND busa.enabled_flag = 'Y'
AND (busa.attribute4 = 'Y' OR busa.attribute5 = 'Y')
AND busa.BUS_VALUE = v_bus;
RETURN v_get_bus_code_result;
RETURN get_result_attribute('BUS_LINES', v_bus, 'attribute1'); /*BUS_GP*/
EXCEPTION
WHEN OTHERS THEN
RETURN '';
END get_bus_code;
FUNCTION get_bus_name(v_group VARCHAR2) --define the parameter and enter the value in the function 'get_bus_result_attribute'
RETURN VARCHAR2 IS
v_get_bus_div_result VARCHAR2(20) ;
BEGIN
SELECT DISTINCT CASE busa.bus_code --Bus code
WHEN '52' THEN '52X'
WHEN '58P' THEN '58'
WHEN 'K1' THEN 'K1C'
WHEN '40' THEN '40X'
WHEN '6' THEN '6X'
WHEN '7' THEN '7'
WHEN '58M' THEN '58'
ELSE ''
END bus_code --Bus code
INTO v_get_bus_div_result
FROM tbl_bus_code busa, tbl_bus_line busb
WHERE busa.bus_code_set_id = busb.bus_code_set_id
AND busb.bus_line_set_name = 'HK_BUS_LINES'
AND busa.enabled_flag = 'Y'
AND (busa.attribute4 = 'Y' OR busa.attribute5 = 'Y')
AND busa.bus_code NOT IN ('INACTIVE', 'XXX')
AND get_bus_code(busa.BUS_VALUE) = v_group
RETURN get_result_attribute('BUS_GROUP', v_group, 'attribute2');
--bus_group_dir
EXCEPTION
WHEN OTHERS THEN
RETURN '';
END get_bus_name;
FUNCTION BUS_DOC_TEXT (N_ID NUMBER, N_HEAD_ID NUMBER)
RETURN VARCHAR2 IS
v_bus_doc_text VARCHAR2(150);
BEGIN
SELECT 'BUS\'
|| get_bus_result_attribute(abc.attribute14)
|| '\'
|| abc.attribute14
|| '\'
|| abc.segment1
INTO v_bus_doc_text
FROM my_table_c abc
WHERE abc.ORG_ID = N_ID -- parameter
AND abc.bus_id = N_HEADER_ID; -- parameter
END;
RETURN v_bus_doc_text ;
END;
END;

Oracle function, how to pass parameters to cursor and use it

Here is one of oracle functions. There is a cursor called c_adv_course_credit which receives 2 parameters. These 2 parameters are using the where statement:
WHERE
-- year
cc.year = p_year AND
-- rela_pk
cc.sequence_number = p_sequence_number AND
cc.closed_ind = 'N';
When I run it in oracle sql developer:
SET SERVEROUTPUT ON
variable res varchar2(200);
EXECUTE :res := advp_test_cursor(2018, 92919);
select :res from dual;
The result text is always "not working"
Here is the full function (not working):
CREATE OR REPLACE Function SISD_OWNER.advp_test_cursor (
p_sequence_number IN NUMBER, -- rela_pk
p_year IN NUMBER -- year
)
RETURN VARCHAR2
IS
v_return_var VARCHAR2(300) := 'not working';
CURSOR c_adv_course_credit (
p_sequence_number IN NUMBER,
p_year IN NUMBER
)
IS
SELECT
cc.EXTERNAL_COURSE_CD
FROM
adv_course_credit cc
WHERE
cc.year = p_year AND
-- rela_pk
cc.sequence_number = p_sequence_number AND
cc.closed_ind = 'N';
BEGIN
FOR v_at_rec IN c_adv_course_credit(p_sequence_number, p_year) LOOP
v_return_var := v_at_rec.EXTERNAL_COURSE_CD;
DBMS_OUTPUT.PUT_LINE('?output = ' || v_return_var);
EXIT;
END LOOP;
RETURN v_return_var;
END;
If I change the cursor to use hard-coded numbers the function works and returns actual result.
WHERE
-- year
cc.year = 2018 AND
-- rela_pk
cc.sequence_number = 92919 AND
cc.closed_ind = 'N';
Your function is defined as (ignoring the data types):
advp_test_cursor(p_sequence_number, p_year)
but you're calling it as
advp_test_cursor(2018, 92919);
which has the arguments the wrong way round. You either need to flip them:
advp_test_cursor(92919, 2018);
or use named parameter notation:
advp_test_cursor(p_year=>2018, p_sequence_number=>92919)
or indeed combine both:
advp_test_cursor(p_sequence_number=>92919, p_year=>2018)
You do not need to use cursors:
CREATE OR REPLACE Function SISD_OWNER.advp_test_cursor (
p_sequence_number IN adv_course_credit.sequence_number%TYPE,
p_year IN adv_course_credit.year%TYPE
) RETURN adv_course_credit.EXTERNAL_COURSE_CD%TYPE
IS
v_return_var adv_course_credit.EXTERNAL_COURSE_CD%TYPE;
BEGIN
SELECT EXTERNAL_COURSE_CD
INTO v_return_var
FROM adv_course_credit
WHERE year = p_year
AND sequence_number = p_sequence_number
AND closed_ind = 'N';
DBMS_OUTPUT.PUT_LINE('?output = ' || v_return_var);
RETURN v_return_var;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'Not working';
END;

Reusing PROCEDURE/ FUNCTION whichever to do the same thing using different values

I have many procedures that do the same thing:
they refresh materialized view and check if the count is not 0, then push that data into production tables. this is the skeleton of what each one does, the only thing that changes is the name of the materialized view. I thought about creating one function that will take in the name of the MV and process it, but it is not working :(
create or replace
function REFRESH_MV (mv_to_refresh IN VARCHAR2)
RETURN VARCHAR2
AUTHID CURRENT_USER
AS
COUNTS INT;
begin
DBMS_MVIEW.REFRESH(mv_to_refresh,'C');
COMMIT;
SELECT COUNT(*) INTO COUNTS FROM 'SEMANTIC.' || mv_to_refresh;
IF COUNTS = 0 THEN
RETURN 'SEMANTIC.' || mv_to_refresh || ' is empty';
ELSE
'SEMANTIC_READ_ONLY.' || RELOAD_TABLE(mv_to_refresh);
RETURN 'SEMANTIC_READ_ONLY.' || mv_to_refresh || ' has been refreshed today';
END IF;
EXCEPTION WHEN OTHERS THEN NULL;
end;
You have to use EXECUTE IMMEDIATE or DBMS_SQL to do that; the first one should be easier to use in your case.
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM 'SEMANTIC.' || mv_to_refresh INTO COUNTS;
should do the trick.
You should use dynamic SQL for this purpose:
CREATE OR REPLACE FUNCTION REFRESH_MV (mv_to_refresh IN VARCHAR2)
RETURN VARCHAR2
AUTHID CURRENT_USER
AS
COUNTS INT;
VSQL VARCHAR2(100);
begin
DBMS_MVIEW.REFRESH('SEMANTIC.' || mv_to_refresh, 'C');
COMMIT;
VSQL := 'SELECT COUNT(1) FROM SEMANTIC.' || mv_to_refresh;
EXECUTE IMMEDIATE VSQL INTO COUNTS;
IF COUNTS = 0 THEN
RETURN 'SEMANTIC.' || mv_to_refresh || ' is empty';
ELSE
SEMANTIC_READ_ONLY.RELOAD_TABLE(mv_to_refresh);
RETURN 'SEMANTIC_READ_ONLY.' || mv_to_refresh
|| ' has been refreshed today';
END IF;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Error has occured: ' || SQLERRM;
END;
Please make sure you pass view name without schema prefix as input parameter.
You should also note that scince it function it should return value or raise exception. But in you example function will return nothing in case of exception.
I didn't quite get the semantics of RELOAD_TABLE() procedure. In example given it is supposed to be a some procedure in SEMANTIC_READ_ONLY schema. In case you really need the appropriate function to be evaluated dynamically, you again can use dynamic SQL to construct the valid string contaning the code and call it:
vsql := 'begin SCHEMA_NAME.' || GET_PROCEDURE_FOR(mv_to_refresh) || '; end;';
execute immediate vsql;

If no data then don't create empty file

I would like to check if there's no data coming from SQL query then file should not be created.
Here is my code:
CREATE OR REPLACE PROCEDURE VR_AD_INTEGRATION_EXPORT AS
l_v_file UTL_FILE.file_type
BEGIN
l_v_file := UTL_FILE.fopen('integration', 'HRMtoAD1_'||to_char(sysdate,'YYYYMMDD')||'_'||to_char(sysdate,'HH24MISS'), 'w', 32767);
FOR x IN (SELECT * FROM (SELECT
decode(pid, NULL, RPAD(' ',7,' '), RPAD(user_id, 7, ' '))|| '' ||
decode(name_last, NULL, RPAD(' ',50,' '), RPAD(name_last, 50, ' '))
str FROM vr_ad_integration WHERE integrated = 'N') str WHERE rownum <= 1000 ORDER BY rownum)
LOOP
BEGIN
UTL_FILE.put_line(l_v_file, x.str);
END;
END LOOP;
UTL_FILE.fflush(l_v_file);
UTL_FILE.fclose(l_v_file);
END VR_AD_INTEGRATION_EXPORT;
Now I can create file successfully in a remote location. However, what if there's no data in select query? no file should be created. If I am correct, I need to include exception code but I have no idea how to do it in this case. Any suggestion?
Cheers! :-)
There are several ways to achieve this. One is to adopt a more procedural approach with a explicit cursor and only open the file once a record is fetched. A second is to modify your code to include a count inside the loop and delete the file if the count is zero.
Here is a third choice, which is a variant on the previous option. It tests the size of the file and if it is zero deleted it using the UTL_FILE.FREMOVE() command. Note the need to store the generated file name in a variable for reference later.
CREATE OR REPLACE PROCEDURE VR_AD_INTEGRATION_EXPORT AS
l_v_file UTL_FILE.file_type;
l_filename varchar2(128);
f_exists boolean;
f_size pls_integer;
f_blk_size pls_integer;
BEGIN
l_filename := 'HRMtoAD1_'||to_char(sysdate,'YYYYMMDD')||'_'||to_char(sysdate,'HH24MISS');
l_v_file := UTL_FILE.fopen('integration', l_filename , 'w', 32767);
FOR x IN (SELECT * FROM (SELECT
decode(pid, NULL, RPAD(' ',7,' '), RPAD(user_id, 7, ' '))|| '' || decode(name_last, NULL, RPAD(' ',50,' '), RPAD(name_last, 50, ' '))
str
FROM vr_ad_integration
WHERE integrated = 'N') str
WHERE rownum <= 1000 ORDER BY rownum) LOOP
BEGIN
UTL_FILE.put_line(l_v_file, x.str);
END;
END LOOP;
utl_file.fgetattr('integration', l_filename , f_exists, f_size, f_blk_size);
if f_size > 0 then
UTL_FILE.fflush(l_v_file);
UTL_FILE.fclose(l_v_file);
else
UTL_FILE.fclose(l_v_file);
utl_file.fremove('integration', l_filename);
end if;
END VR_AD_INTEGRATION_EXPORT;
There is some useful functionality in the UTL_FILE package beyond reading and writing lines. I suggest you read the documentation to find out more.
Use a flag to check if the file is created, and only create it once on the first run through your loop. Pseudocode:
bool fileCreatedFlag = false;
for x in (SELECT ...):
if(!fileCreatedFlag):
l_v_file = fopen(...);
fileCreatedFlag = true;
put_line(...);
if(fileCreatedFlag):
fflush;
fclose;

Resources