Get value from Dynamic input - plsql

I am trying to get the value from dynamically created field in my app using a branch in Oracle Apex with PL/SQL. I have tried to fetch it like other fields with :NAME and i had no luck. My code from branch looks like this,
DECLARE
tabs INTEGER := :P2_CREATE_TABS;
lines INTEGER := :P2_LINES;
counter NUMBER := 1;
content VARCHAR2(100);
BEGIN
INSERT INTO EmailContent (CUSTOMERNAME, ORGANISATIONNUMBER, CONTACTNUMBER, EMAIL)
VALUES (:P2_CUST_NAME, :P2_ORG_NUM, :P2_CONTACT_NUM, :P2_EMAIL);
FOR tab_counter IN 1 .. tabs
LOOP
FOR line_count IN 1 .. lines
LOOP
FOR element_count IN 1 .. 3
LOOP
IF(counter < 10) THEN
content := :F||0||counter;
INSERT INTO DynamicContent (ID_EMAIL, ID_TAB, ID_LINE, CONTENTVALUE)
VALUES
(
(SELECT COUNT(*) FROM EmailContent),
tab_counter,
line_count,
content
);
counter := counter + 1;
ELSE
content := :F||counter;
INSERT INTO DynamicContent (ID_EMAIL, ID_TAB, ID_LINE, CONTENTVALUE)
VALUES
(
(SELECT COUNT(*) FROM EmailContent),
tab_counter,
line_count,
content
);
counter := counter + 1;
END IF;
END LOOP;
END LOOP;
counter := 0;
END LOOP;
END;
Fields are created with APEX_ITEM.TEXT(... which has p_idx which generates F01, F02, ..., F50 and that's why i have tried to use content := :F||0||counter; and content := :F||counter;.
With this outcome, my database it's populated with values 01, 02, 03, ..., 00, 01...etc. And i want to fetch the value from input with name FXX.
Thanks in advance!

Thank you #romeuBraga
Since fields were created dynamically with APEX_ITEM.TEXT(... the p_idx generates F with range 01-50 and this can be used as array. Can be accessed like this APEX_APPLICATION.G_F01(i);.

Related

PL/SQL: I get expression 'I' cannot be used as an assignment target

My code:
create table info(str varchar2(30));
declare
cursor c(job emp_ast.job_id%type, dep emp_ast.department_id%type) is select employee_id
from emp_ast
where job_id=job and department_id=dep;
type t_job is table of emp_ast.job_id%type;
t t_job:=t_job();
emp emp_ast.employee_id%type;
i number(3);
begin
select job_id
bulk collect into t
from emp_ast;
for i in 10..270 loop
for j in 1..t.count loop
open c(i, t(j));
loop
fetch c into emp;
insert into info
values (i||' '||t(j)||' '||emp);
exit when c%notfound;
end loop;
i:=i+10;
end loop;
end loop;
end;
/
I get "expression 'I' cannot be used as an assignment target", reffering to the line where I increment i by 10. I am trying to save the department_id, employee_id and job_id as a string in a table for each department and each job.
At the point where you get that message, i refers to the loop control variable i defined in the line for i in 10..270 loop, not the int(3) variable defined earlier. In PL/SQL a loop definition defines a variable which is only accessible inside the loop, and which you cannot alter. I suggest you change the name of one or the other to make them unique.
EDIT
PL/SQL doesn't provide a way to step by more than 1 in a computed FOR loop. Instead, you will need to compute the desired department number value within the loop:
DECLARE
CURSOR c(job EMP_AST.JOB_ID%TYPE,
dep EMP_AST.DEPARTMENT_ID%TYPE)
IS SELECT EMPLOYEE_ID
FROM EMP_AST
WHERE JOB_ID = job AND
DEPARTMENT_ID = dep;
TYPE t_job IS TABLE OF EMP_AST.JOB_ID%TYPE;
t t_job := t_job();
emp EMP_AST.EMPLOYEE_ID%TYPE;
nDepartment NUMBER;
BEGIN
SELECT job_id
BULK COLLECT INTO t
FROM EMP_AST;
FOR i IN 1..27 LOOP
nDepartment := i * 10;
FOR j IN 1..t.COUNT LOOP
OPEN c(t(j), nDepartment);
LOOP
FETCH c INTO emp;
INSERT INTO info
VALUES (nDepartment || ' ' || t(j) || ' ' || emp);
EXIT WHEN c%notfound;
END LOOP; -- cursor c
CLOSE c;
END LOOP; -- j
END LOOP; -- i
END;
/
Note that in the code above the nDepartment value is computed within the i loop, which now increments from 1 to 27 instead of going from 10 to 270.

Bulk collect and forall with dynamic query

Declare
Vquery varchar2(32000);
Vitem varchar2(50);
Vskuloc varchar2(50);
vstartdate Date;
Vdur Number;
vtype Number;
vqty Float(126);
GP_ohpost Date:= fnc_ohpost;
sdate1 Date:= to_date('01/01/1970','dd/mm/yyyy');
Cursor C_DRIVER is
(Select h.*,b.item,b.skuloc,h.rowid
FROM SCPOMGR.histwide h, SCPOMGR.dfutosku b
WHERE h.dmdunit=b.dmdunit
AND h.loc=b.dfuloc
AND (b.eff = Sdate1 OR b.eff <= h.startdate
AND b.disc = Sdate1 OR b.disc > h.startdate)
And NOT EXISTS (SELECT 1 FROM SCPOMGR.SKUHIST d
WHERE b.dmdunit = d.item
AND b.skuloc = d.loc
AND h.startdate = d.startdate
)) order by h.StartDate;
TYPE GP_cursor_Type IS TABLE OF C_DRIVER%ROWTYPE;
GP_cursor_tab GP_cursor_Type := GP_cursor_type();
c_limit constant PLS_INTEGER DEFAULT 10;
TYPE GP_Insert_type IS TABLE OF scpomgr.skuhist%ROWTYPE;
GP_Insert_tab GP_Insert_type := GP_Insert_type();
GP_tot_accept_fetched NUMBER := 0;
begin
OPEN C_DRIVER;
LOOP
FETCH c_driver BULK COLLECT INTO GP_cursor_tab limit c_limit;
Exit when c_driver%NOTFOUND;
FOR i IN GP_cursor_tab.FIRST .. GP_cursor_tab.LAST LOOP
vquery:= 'Select ...<skipped to make post shortest>';
Execute immediate vquery BULK COLLECT INTO GP_Insert_tab;
FORALL i IN INDICES OF GP_Insert_tab
Insert into scpomgr.skuhist
values( GP_Insert_tab(i).startdate
,1
,10080
,GP_Insert_tab(i).qty
,GP_Insert_tab(i).item
,GP_Insert_tab(i).loc
);
End Loop;
End Loop;
Close C_DRIVER;
END;
/
What i want to do here is i want to use the forall query outside of loop but if i am using the forall with the 2 nd array outside the loop then.all of the records are not getting inserted to the final table..please suggest me some solution....
Ankita.
I didn't know the structure of your tables, so i made a simple tables to understand what should work here:
create table histwide (f1 number);
insert into histwide values (1);
insert into histwide values (2);
create table skuhist (f1 number);
select * from histwide
select * from skuhist
Now i changed your code keeping changed code in comments. Seems it works. I think the trouble was because of you are used Exit when c_driver%NOTFOUND; in begining of loop. It is wrong for FORALL because after first fetching cursor is empty (if limit is reached).
So, my solution of your case:
Declare
Vquery varchar2(32000);
Vitem varchar2(50);
Vskuloc varchar2(50);
vstartdate Date;
Vdur Number;
vtype Number;
vqty Float(126);
GP_ohpost Date:= trunc(sysdate); --fnc_ohpost;
sdate1 Date:= to_date('01/01/1970','dd/mm/yyyy');
Cursor C_DRIVER is
(Select h.*--,b.item,b.skuloc,h.rowid
--FROM SCPOMGR.histwide h, SCPOMGR.dfutosku b
FROM histwide h
/*WHERE h.dmdunit=b.dmdunit
AND h.loc=b.dfuloc
AND (b.eff = Sdate1 OR b.eff <= h.startdate
AND b.disc = Sdate1 OR b.disc > h.startdate)
And NOT EXISTS (SELECT 1 FROM SCPOMGR.SKUHIST d
WHERE b.dmdunit = d.item
AND b.skuloc = d.loc
AND h.startdate = d.startdate
)) order by h.StartDate*/
);
TYPE GP_cursor_Type IS TABLE OF C_DRIVER%ROWTYPE;
GP_cursor_tab GP_cursor_Type := GP_cursor_type();
c_limit constant PLS_INTEGER DEFAULT 10;
TYPE GP_Insert_type IS TABLE OF skuhist%ROWTYPE; --scpomgr.skuhist%ROWTYPE;
GP_Insert_tab GP_Insert_type := GP_Insert_type();
GP_tot_accept_fetched NUMBER := 0;
begin
OPEN C_DRIVER;
LOOP
FETCH c_driver BULK COLLECT INTO GP_cursor_tab limit c_limit;
Exit when GP_cursor_tab.count = 0;
dbms_output.put_line('arr cur size: '||GP_cursor_tab.count);
FOR i IN GP_cursor_tab.FIRST .. GP_cursor_tab.LAST LOOP
vquery:= 'Select * from histwide';
Execute immediate vquery BULK COLLECT INTO GP_Insert_tab;
dbms_output.put_line('arr size: '||GP_Insert_tab.count);
-- FORALL j IN INDICES OF GP_Insert_tab
FORALL j in GP_Insert_tab.first..GP_Insert_tab.last --works too
Insert into skuhist --scpomgr.skuhist
values(GP_Insert_tab(j).f1
/*GP_Insert_tab(i).startdate
,1
,10080
,GP_Insert_tab(i).qty
,GP_Insert_tab(i).item
,GP_Insert_tab(i).loc*/
);
End Loop;
Exit when c_driver%NOTFOUND; --it should be at end of the loop.
End Loop;
Close C_DRIVER;
END;
/
Hope it will help you

Separating multiple rows of a select query using delimiter (Oracle)

I have a query
SELECT originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
The output in a linux script is as below:
09-OCT-17 04.59.33.758 AM -05:00 09-OCT-17 05.03.22.645 AM -05:00
there are two rows above each starting by date.
I would like to have the output like
09-OCT-17 04.59.33.758 AM -05:00;09-OCT-17 05.03.22.645 AM -05:00
This is just two rows, there can be many more, I would like it so that every row is separated via delimiter.
I have tried few options like
1) using listagg:
select listagg(originating_timestamp,', ') within group(order by originating_timestamp) csv
from sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
But this gives error:
ERROR at line 1:
ORA-01489: result of string concatenation is too long
2) using XMLAGG:
SELECT RTRIM(XMLAGG(XMLELEMENT(E,originating_timestamp,';').EXTRACT('//text()') ORDER BY originating_timestamp).GetClobVal(),',') AS LIST
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY')= to_char(systimestamp,'DD-MON-YY');
But the output is like :
2017-10-09T04:59:33.758-05:00;2017-10-09T05:03:22.645-05:00;
Which is also not correct.
e.g. Select username from dba_users. Suppose there are 10 users, i want those 10 usernames to be separated via delimiter.
Below two anonymous block example which may be helpful to solve your problem :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select 'a' ||',' into tmp from dual;
ELSE
select 'a' into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
Second :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(1000);
tmp VARCHAR2(1000);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') || ',' into tmp from dual;
ELSE
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
You should replace my query select with yours and of course the format of date you wanna get and replace comma with your desire character .
To make it working properly i wanna suggest you to create a cursor :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
cont NUMBER;
CURSOR C_1 IS
SELECT to_char(originating_timestamp,'your date format')
originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
BEGIN
cont := 0;
FOR C_row IN C_1 LOOP
IF cont != C_1%rowcount THEN
tmp := C_row.originating_timestamp ||',';
res := concat(res,tmp);
ELSE
tmp := C_row.originating_timestamp;
res := concat(res,tmp);
END IF;
cont := cont +1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/

ORA-21700 on SELECT FROM package-user-defined TABLE

I'm creating an Oracle package (MyPackage) where I have a public custom type table (ObjDataCollection) of custom type records (ObjData), which will be used as IN parameter for one of the Package functions (Calculate):
CREATE OR REPLACE PACKAGE MyPackage AS
TYPE ObjData IS RECORD (
t date NOT NULL := DATE '0001-01-01',
v number(9, 4)
);
TYPE ObjDataCollection IS TABLE
OF ObjData;
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number;
END MyPackage;
CREATE OR REPLACE PACKAGE BODY MyPackage AS
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number IS
res number(9, 4) := 0;
dateStart date;
dsv ObjData;
CURSOR q1 (dt date) IS
SELECT * FROM TABLE(DataSource) --Throws ORA-21700: Object does not exist or is marked for delete oracle.
WHERE t >= dt
ORDER BY t;
BEGIN
-- some irrelevant code
dateStart := DATE '2015-01-01';
OPEN q1(dateStart);
LOOP FETCH q1 INTO dsv;
EXIT WHEN q1%NOTFOUND;
res := res + dsv.v;
-- some irrelevant code
END LOOP;
CLOSE q1;
-- some irrelevant code
return res;
END Calculate;
END MyPackage;
I debbuged my code and I get the error on the second line of the cursor (marked in code):
ORA-21700: Object does not exist or is marked for delete oracle.
I'm using this data to execute my package:
CREATE TABLE TestTable (d date DEFAULT DATE '0001-01-01', v number(9, 4));
INSERT INTO TestTable VALUES (DATE '2015-01-01', 2.1);
INSERT INTO TestTable VALUES (DATE '2015-01-08', 3.1);
INSERT INTO TestTable VALUES (DATE '2015-01-15', 4.1);
INSERT INTO TestTable VALUES (DATE '2015-01-22', 5.1);
INSERT INTO TestTable VALUES (DATE '2015-01-29', 6.1);
INSERT INTO TestTable VALUES (DATE '2015-02-05', 7.1);
And this code to run a test:
CREATE OR REPLACE PROCEDURE TestMyPackage AS
res MyPackage.ObjDataCollection;
counter number(9, 4);
BEGIN
SELECT d, v
BULK COLLECT INTO res
FROM TestTable
ORDER BY v;
counter := MyPackage.Calculate(res);
END TestMyPackage;
Why I recieve this ORA-21700 exception?
PACKAGE BODY MyPackage AS
FUNCTION Calculate(
DataSource IN ObjDataCollection
) RETURN number IS
res BINARY_FLoAT:= 0;
dateStart date;
dsv ObjData;
copy_of_DataSource ObjDataCollection;
procedure sortCollection(toSort in out ObjDataCollection)
is
type idx_coll is table of ObjData;
type sort_help is table of idx_coll index by varchar2(16 char);
v_sort sort_help;
v_temp varchar2(16);
v_result ObjDataCollection := new ObjDataCollection();
v_cnt PLS_INTEGER := 0;
begin
for i in nvl(toSort.first,1) .. nvl(toSort.last,-1) loop
v_temp := to_char(toSort(i).t,'yyyymmddhh24miss');
if v_sort.exists(v_temp) then
v_sort(v_temp).extend(1);
v_sort(v_temp)(v_sort(v_temp).count) := toSort(i);
else
v_sort(v_temp) := idx_coll(toSort(i));
end if;
end loop;
v_result.extend(toSort.count);
v_temp := v_sort.first;
while v_temp is not null loop
for i in nvl(v_sort(v_temp).first,1) .. nvl(v_sort(v_temp).last,-1) loop
v_cnt := v_cnt +1;
v_result(v_cnt) := v_sort(v_temp)(i);
end loop;
v_temp := v_sort.next(v_temp);
end loop;
toSort := v_result;
end;
BEGIN
-- some irrelevant code
copy_of_DataSource := DataSource;
dateStart := DATE '2015-01-01';
sortCollection(copy_of_DataSource);
for i in nvl(copy_of_DataSource.first,1) .. nvl(copy_of_DataSource.last,-1) loop
if copy_of_DataSource(i).t > dateStart then
res := res + copy_of_DataSource(i).v;
dbms_output.put_line(copy_of_DataSource(i).t);
end if;
end loop;
-- some irrelevant code
return res;
END Calculate;
END MyPackage;
As it was a task due to a date, I ended up fixing it by creating user-defined types and tables at schema level as suggested Arkadiusz Ɓukasiewicz in one of his comments. Thank you for your effort.
As is not what I wanted, I won't mark any answer as Correct.
Got the same problem and solved it by doing the following steps:
Drop package where type is defined
Drop package using that type
Compile package with type
Compile target package
I got this ORA-21700 after adding new constants in a package with types/constants. I guess there's some internal issue with dependencies but I haven't dug a lot.

Array Binding for Select Statement in dbms_sql (Oracle 11g)

My Problem is that I want to execute a dynamic SQL-Query within PL/SQL where I have a List of IDs as my Array Bind.
In the Oracle-Documentation I found some Examples how to join Lists of Numbers to an DML-Statement. (http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996963)
Now I am trying to make the same thing for Select-Statements.
I know that I can use Array-Binds for the execute immediate-Statement. But this has the disadvantage that I must know the exact number of Bind-Variables before executing the Statement. That is the reason why I have to use dbms_sql.
The following Example Returns only one Row, but it should return 3 rows. Does anyone know what the Problem with my Example is?
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
p_ids dbms_sql.number_table;
c number;
dummy NUMBER;
p_name varchar2(100);
begin
p_ids(1) := 2;
p_ids(2) := 3;
p_ids(3) := 4;
--
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, 'select name from PERSON where id in(:num_array)', DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_ARRAY(c, ':num_array', p_ids);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;
Here is my current solution for binding multiple values to a Select Statement, maybe someone can need it:
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
c number;
dummy NUMBER;
p_name varchar2(100);
xml$ varchar2(1000);
begin
--Generate a XML-List instead of dbms_sql.number_table:
xml$ := '<ids><id>2</id><id>3</id><id>4</id></ids>';
--
c := dbms_sql.open_cursor;
--Using XML-Functions for extracting the Values from the XML-String
DBMS_SQL.PARSE(c, 'select name
from PERSON
where id in(select extractvalue(value(x), ''id'')
from table(xmlsequence(xmltype(:ids).extract(''ids/*'')))x)'
, DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_variable(c, ':ids', xml$);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;

Resources