Calling or using oracle array package in oracle apex - oracle-apex

I want to calling oracle array package in oracle apex page process.
My package "CREATE OR REPLACE PACKAGE SBPA.DPG_SBPA_ITEM_SUB_CAT AS
/..........................................................................
Program Purpose : SBPA_ITEM_SUB_CAT_ENTRY
Process Execution Time :
Generate By : Morshed
Generate Date : 27-Feb-2020
Modifyed Date :
...................................................................../
TYPE RefCursor is REF CURSOR;
TYPE Array_Item_Sub_Id IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_ID % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Sub_Code IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_CODE % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Sub_Desc IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_DESC % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Cat_Code IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_CAT_CODE % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_RowStatus IS TABLE OF VARCHAR2(5) INDEX BY BINARY_INTEGER;
PROCEDURE DPD_SBPA_ITEM_SUB_CAT (O_Status OUT NUMBER,
P_Item_Sub_Id IN Array_Item_Sub_Id,
P_Item_Sub_Code IN Array_Item_Sub_Code,
P_Item_Sub_Desc IN Array_Item_Sub_Desc,
P_Item_Cat_Code IN Array_Item_Cat_Code,
P_RowStatus IN Array_RowStatus,
P_USER VARCHAR2);
PROCEDURE DPD_SBPA_ITEM_SUB_CAT_GRID (Cur_Data OUT RefCursor);
END DPG_SBPA_ITEM_SUB_CAT;
/"
And Package body "CREATE OR REPLACE PACKAGE BODY SBPA.DPG_SBPA_ITEM_SUB_CAT AS
/..........................................................................
Program Purpose : SBPA_ITEM_SUB_CAT_ENTRY
Process Execution Time :
Generate By : Morshed
Generate Date : 27-Feb-2020
Modifyed Date :
...................................................................../
PROCEDURE DPD_SBPA_ITEM_SUB_CAT (O_Status OUT NUMBER,
P_Item_Sub_Id IN Array_Item_Sub_Id,
P_Item_Sub_Code IN Array_Item_Sub_Code,
P_Item_Sub_Desc IN Array_Item_Sub_Desc,
P_Item_Cat_Code IN Array_Item_Cat_Code,
P_RowStatus IN Array_RowStatus,
P_USER VARCHAR2) IS
V_DataType VARCHAR2(20) :='ITEM_SUBCAT_SAVE';
V_ErrDesc VARCHAR2(500);
BEGIN
/*O_Status :=1;*/
FOR I IN P_Item_Sub_Desc.FIRST..P_Item_Sub_Desc.LAST
LOOP
IF P_RowStatus(I)=1 THEN
INSERT INTO SBPA_ITEM_SUB_CAT
(ITEM_SUB_ID, ITEM_SUB_CODE, ITEM_SUB_DESC, ITEM_CAT_CODE, STATUS, CREATE_DATE, CREATE_BY) VALUES
(SBPA_ITEM_SUB_ID_SEQ.NEXTVAL,'ITMSC-'||LPAD(SBPA_ITEM_SUB_CODE_SEQ.NEXTVAL,4,'0'),P_Item_Sub_Desc(I),P_Item_Cat_Code(I),'A',SYSDATE,P_USER);
ELSIF P_RowStatus(I)=2 THEN
UPDATE SBPA_ITEM_SUB_CAT SET
ITEM_SUB_DESC=P_Item_Sub_Desc(I),
Item_Cat_Code=P_Item_Cat_Code(I),
UPDATE_BY=P_User,
UPDATE_DATE =SYSDATE
WHERE Item_Sub_Code=P_Item_Sub_Code(I);
ELSIF P_RowStatus(I)=3 THEN
DELETE FROM SBPA_ITEM_SUB_CAT
WHERE Item_Sub_Code=P_Item_Sub_Code(I);
END IF;
END LOOP;
COMMIT;
EXCEPTION WHEN OTHERS THEN ROLLBACK;
/* O_Status :=0;*/
V_ErrDesc:=SUBSTR(SQLERRM,1,500);
INSERT INTO SBPA_ERROR_LOG
(RUN_ID, DATA_TYPE, ERROR_DESC, STATUS, RUN_DATE, RUN_BY) VALUES
(SBPA_RUN_ID_SEQ.NEXTVAL,V_DataType,V_ErrDesc,'E',SYSDATE,P_User);
COMMIT;
END DPD_SBPA_ITEM_SUB_CAT;
PROCEDURE DPD_SBPA_ITEM_SUB_CAT_GRID (Cur_Data OUT RefCursor) IS
BEGIN
OPEN CUR_DATA FOR
SELECT ITEM_SUB_ID, ITEM_SUB_CODE, ITEM_SUB_DESC,S.ITEM_CAT_CODE,C.ITEM_CAT_DESC FROM SBPA_ITEM_SUB_CAT S,SBPA_ITEM_CAT C
WHERE S.ITEM_CAT_CODE=C.ITEM_CAT_CODE
ORDER BY ITEM_SUB_ID DESC;
END DPD_SBPA_ITEM_SUB_CAT_GRID;
END DPG_SBPA_ITEM_SUB_CAT;
/"
This database code. So how can i call this package in oracle apex tabular form. Please help me..

(1) Create a button, so the processing(DML) will be performed on click of the button.
(2) Button Action -> Submit Page.
(3) Create a page Process
Point -> Processing.
Tabular Form -> Select your tabular form.
Pl/SQL Code -> Begin
package_name.procedure_name(parameters);
END;
(4) When Button Pressed: Select the created button.
(5) Execution Scope: For Created and Modified rows.

Related

Can you reference an aggregate function on a temporary row in an insert statement within a stored procedure in postgresql?

I am writing a postgres stored procedure that loops through the rows returned from a select statement. For each row it loops through, it inserts values from that select statement into a new table. One of the values I need to insert into the second table is the average of a column. However, when I call the stored procedure, I get an error that the temporary row has no attribute for the actual column that I am averaging. See stored procedure and error below.
Stored Procedure:
create or replace procedure sendToDataset(sentence int)
as $$
declare temprow peoplereviews%rowtype;
BEGIN
FOR temprow IN
select rulereviewid, avg(rulereview)
from peoplereviews
where sentenceid = sentence
group by rulereviewid
loop
insert into TrainingDataSet(sentenceId, sentence, ruleCorrectId, ruleCorrect, dateAdded)
values(sentence, getSentenceFromID(sentence), tempRow.rulereviewid, tempRow.avg(rulereview), current_timestamp);
END LOOP;
END
$$
LANGUAGE plpgsql;
Error:
ERROR: column "rulereview" does not exist
LINE 2: ...omID(sentence), tempRow.rulereviewid, tempRow.avg(rulereview...
^
QUERY: insert into TrainingDataSet(sentenceId, sentence, ruleCorrectId, ruleCorrect, dateAdded)
values(sentence, getSentenceFromID(sentence), tempRow.rulereviewid, tempRow.avg(rulereview), current_timestamp)
CONTEXT: PL/pgSQL function sendtodataset(integer) line 11 at SQL statement
SQL state: 42703
Basically, I am wondering if it's possible to use that aggregate function in the insert statement or not and if not, if there is another way around it.
you don't need to use a slow and inefficient loop for this:
insert into TrainingDataSet(sentenceId, sentence, ruleCorrectId, ruleCorrect, dateAdded)
select getSentenceId(sentence), sentence, rulereviewid, avg(rulereview), current_timestamp
from peoplereviews
where sentenceid = sentence
group by rulereviewid
To answer the original question: you need to provide a proper alias for the aggregate:
FOR temprow IN
select rulereviewid, avg(rulereview) as avg_views
from peoplereviews
where sentenceid = sentence
group by rulereviewid
loop
insert into TrainingDataSet(sentenceId, sentence, ruleCorrectId, ruleCorrect, dateAdded)
values(sentence, getSentenceFromID(sentence), tempRow.rulereviewid,
tempRow.avg_views, current_timestamp);
END LOOP;

Passing an invalid user id, code should display message but the dbms_output is blank

This procedure should list all project IDs mapped to the active user, and on passing an invalid user id it should print "User is not valid". However, no message is getting displayed.
PROCEDURE get_pid_info (p_return_status_o OUT VARCHAR2,
p_error_message_o OUT VARCHAR2,
p_user_id IN VARCHAR2)
IS
CURSOR c_pid
IS
SELECT DISTINCT xpppa.project_id,
papf.person_type_id,
xpppp.end_date_active,
papf.person_id,
COUNT (DISTINCT xpppa.project_id) pid_count
FROM xxcas_prj_pa_projects_all xpppa,
xxcas_prj_pa_project_players xpppp,
per_all_people_f papf
WHERE xpppa.project_id = xpppp.project_id
AND xpppp.person_id = papf.person_id
AND papf.person_id = 61--p_user_id
AND xpppp.project_role_type = 'PROJECT MANAGER'
AND papf.person_type_id = 6
and sysdate between xpppa.start_date and nvl(xpppa.completion_date,sysdate+1)
and sysdate between xpppp.start_date_active and nvl(xpppp.end_date_active,sysdate+1)
AND EXISTS
(SELECT 1
FROM pa_lookups
WHERE lookup_type = 'XXCAS_PRJ_USER_DETAILS'
AND description=papf.email_address
AND enabled_flag = 'Y'
AND SYSDATE BETWEEN start_date_active
AND NVL (end_date_active,
SYSDATE + 1))
GROUP BY xpppa.project_id,
papf.person_type_id,
xpppp.end_date_active,
papf.person_id;
BEGIN
FOR l_rec IN c_pid
LOOP
IF l_rec.project_id IS NOT NULL
THEN
dbms_output.put_line (
'The Projects mapped to the active user ID are : '
|| l_rec.project_id);
ELSIF l_rec.end_date_active < SYSDATE
THEN
dbms_output.put_line (
'The user has been end dated in the system on : ' || l_rec.end_date_active);
ELSIF l_rec.pid_count = 0
THEN
dbms_output.put_line (
'There are no projects mapped to the user having PM role');
ELSE
dbms_output.put_line ('Please check the user ID passed');
END IF;
IF SQL%NOTFOUND
THEN
dbms_output.put_line('Entered user id is not valid');
end if;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
dbms_output.put_line ('Please provide valid user ID');
WHEN OTHERS
THEN
dbms_output.put_line ('Error in get_pid' || SQLERRM);
END get_pid_info;
Anonymous Block:
declare
status varchar2(30);
msg varchar2(30);
begin
--DBMS_OUTPUT.ENABLE('20000000');
XXCAS_PRJ_PROJECT_DTLS.GET_PID_INFO(status,msg,61);
end;
Firstly, if there are no rows to process then the loop is not entered and so any check inside it will not be executed.
Secondly, the SQL% cursor attributes apply to implicit cursors, not named ones like c_pid in your procedure. It would be simplest to declare a Boolean variable, initialise it to FALSE and set it to TRUE in the loop; then you can check its value after the loop. (Or if you want the actual number of rows, use c_pid%rowcount and initialise it to 0.)
I'm assuming this is a simplified version of your actual procedure, as dbms_output is not generally useful in production code unless the caller is set up to capture it.

pl sql How to make my code less

Can anyone help me to make my code less? (If you can notice to both if-elsif statements I make the same Select.. so I wish there was a way to make this select once. and update with 1 or 0 depending on the pilot_action).
Below its my code.
create or replace
PROCEDURE F_16 (TRK_ID NUMBER, pilot_action NUMBER) IS
BEGIN
BEGIN
IF pilot_action=0 THEN
UPDATE "ControlTow"
SET "Intention"=0
WHERE "Id" IN (
SELECT "Id" FROM "ControlTow" WHERE "Id"=TRK_ID );
ELSIF pilot_action=1 THEN
UPDATE "ControlTow"
SET "Intention"=1
WHERE "Id" IN (
SELECT "Id" FROM "ControlTow" WHERE "Id"=TRK_ID );
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN dbms_output.put_line('False Alarm');
COMMIT;
END;
END F_16;
thank you , in advance.
Your code has several issues I have addressed in the comments below. Note that transaction management is not discussed as it's not clear based on the question when commit/rollback should take place.
-- #1 use of explicit parameter mode
create or replace procedure f_16(p_trk_id in number, p_pilot_action in number) is
begin
-- #2 use of in
if p_pilot_action in (0, 1)
then
-- #3 unnecessary subquery removed
update controltow
set intention = p_pilot_action
where id = p_trk_id;
-- #4 use pl/sql implicit cursor attribute to check the number of affected rows
if sql%rowcount = 0
then
dbms_output.put_line('false alarm');
end if;
end if;
end;
Since you seem to be assigning pilot_action to Intention, I would do following:
create or replace
PROCEDURE F_16 (TRK_ID NUMBER, pilot_action NUMBER) IS
BEGIN
BEGIN
IF pilot_action IN (0, 1) THEN
-- if the only condition in subselect is the ID then use it directly
UPDATE "ControlTow"
SET "Intention"= pilot_action
WHERE "Id"=TRK_ID;
-- if there are more conditions than just the ID then subselect may be the way to go
--(hard to say without more information)
-- WHERE "Id" IN (
-- SELECT "Id" FROM "ControlTow" WHERE "Id"=TRK_ID AND ... )
ELSE
Null; -- do whatever you need in this case. Raise exception?
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN dbms_output.put_line('False Alarm');
COMMIT;
END;
END F_16;
EDIT: As #user272735 said, there was room for more improvement on the code. Specifically rewriting the if condition to use in and simplifying the where clause (supposing Id is really the only condition to select rows to be updated).

oracle regular expression and MERGE

As updating my previous question,
I've a some newline separated strings.
I need to insert those each words into a table.
The new logic and its condition is that, it should be inserted if not exists, or update the corresponding count by 1. (as like using MERGE).
But my current query is just using insert, so I've used CONNECT BY LEVEL method without checking the value is existing or not.
it syntax is somewhat like:
if the word already EXISTS THEN
UPDATE my_table set w_count = w_count +1 where word = '...';
else
INSERT INTO my_table (word, w_count)
SELECT REGEXP_SUBSTR(i_words, '[^[:cntrl:]]+', 1 ,level),
1
FROM dual
CONNECT BY REGEXP_SUBSTR(i_words, '[^[:cntrl:]]+', 1 ,level) IS NOT NULL;
end if;
Try this
MERGE INTO my_table m
USING(WITH the_data AS (
SELECT 'a
bb
&
c' AS dat
FROM dual
)
SELECT regexp_substr(dat, '[^[:cntrl:]]+', 1 ,LEVEL) wrd
FROM the_data
CONNECT BY regexp_substr(dat, '[^[:cntrl:]]+', 1 ,LEVEL) IS NOT NULL) word_list
ON (word_list.wrd = m.word)
WHEN matched THEN UPDATE SET m.w_count = m.w_count + 1
WHEN NOT matched THEN insert(m.word,m.w_count) VALUES (word_list.wrd,1);
More details on MERGE here.
Sample fiddle

Calling rss feed with plsql

I'm trying to call a webservice from plsql. I have the following code (which works):
CREATE OR REPLACE PROCEDURE WEBSERVICE AS
v_xml XMLTYPE;
BEGIN
v_xml := XMLTYPE(UTL_HTTP.REQUEST(URL => 'http://dev.markitondemand.com/Api/Quote?symbol=AAPL'));
FOR x IN (SELECT
EXTRACTVALUE(VALUE(p), '/Data/Status/text()') AS title
FROM TABLE(XMLSEQUENCE(EXTRACT(v_xml, '/QuoteApiModel/Data'))) p)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
COMMIT;
END;
But this code does not work:
CREATE OR REPLACE PROCEDURE WEBSERVICE AS
v_xml XMLTYPE;
BEGIN
v_xml := XMLTYPE(UTL_HTTP.REQUEST(URL => 'http://xkcd.com/rss.xml'));
FOR x IN (SELECT
EXTRACTVALUE(VALUE(p), 'item/title/text()') AS title
FROM TABLE(XMLSEQUENCE(EXTRACT(v_xml, '/rss/channel/item'))) p)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
COMMIT;
END;
It gives me the following error:
Connecting to the database oracle.
ORA-31011: XML parsing failed
ORA-19202: Error occurred in XML processing
LPX-00007: unexpected end-of-file encountered
ORA-06512: at "SYS.XMLTYPE", line 310
ORA-06512: at "SYS.WEBSERVICE", line 4
ORA-06512: at line 2
Process exited.
Disconnecting from the database oracle.
The difference I noticed is that the xkcd rss feed has an xml header and the other one doesn't. What should I do to make this work?
You right, error occured because of presence of XML header.
Workaround is to use XMLParse(document ...) to parse XML:
select xmlparse(document UTL_HTTP.REQUEST(URL => 'http://xkcd.com/rss.xml'))
into v_xml
from dual;
Example SQLFiddle with results from xkcd.com.
Notice, that according to documentation HTTP_UTIL.Request function returns only first 2000 bytes of server response, so you can get incomplete XML with such request.
Update
More elegant way to extract values from XML is to use XMLTable() function with XQuery :
FOR x IN (
select title
from
XMLTable(
'for $i in $p/rss/channel/item/title return $i'
passing
XMLParse(document UTL_HTTP.REQUEST(URL => '...')) as "p"
columns
title varchar2(4000) path '//title'
)
)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
SQLFiddle
At least, this method allows you to create more flexible options for different sources of RSS.