Loop variable with concatenation in pl/sql - oracle

I hava a loop in pl/sql.I want to use loop variable with string as below actually array value
FETCH REFCUR BULK COLLECT INTO MY_ARRAY;
FOR indx IN 1 .. MY_ARRAY.COUNT LOOP
FOR cntr IN 1..3
LOOP
student_rec := student_type_wr(null, null, null, null);
student_rec.invoice_date := MY_ARRAY(indx).invoice_date;
student_rec.service_type := MY_ARRAY(indx).type || cntr; // it gives compile error
student_rec.amount := MY_ARRAY(indx).amount || cntr; // it gives compile error
student_rec.gross := MY_ARRAY(indx).gross || cntr; // it gives compile error
student_recs .extend();
student_recs(student_recs.count()) := student_rec;
END LOOP;
END LOOP;
My array is like this :
TYPE a_type IS RECORD (invoice_date DATE,
type1 VARCHAR2(50),
amount1 NUMBER,
gross1 NUMBER,
type2 VARCHAR2(50),
amount2 NUMBER,
gross2 NUMBER,
type3 VARCHAR2(50),
amount3 NUMBER,
gross3 NUMBER,);
TYPE TABLETYPE IS TABLE OF a_type;
MY_ARRAY TABLETYPE;
How can i concat loop variable with array fields? I want to read MY_ARRAY(indx).type1 with MY_ARRAY(indx).type || cntr
My error is :
Error(70,66): PLS-00302: component 'TYPE' must be declared
Error(71,60): PLS-00302: component 'AMOUNT' must be declared
Error(72,66): PLS-00302: component 'GROSS' must be declared
I know my mistake MY_ARRAY(indx).type is not defined but I have to use it.Do you have any idea?

As far as I understand your code is that you want to call your columns dynamically while as far as I know about Oracle is that you cannot do so. So you have to go with the below code -
FETCH REFCUR BULK COLLECT INTO MY_ARRAY;
FOR indx IN 1 .. MY_ARRAY.COUNT LOOP
student_rec := student_type_wr(null, null, null, null);
student_rec.invoice_date := MY_ARRAY(indx).invoice_date;
student_rec.service_type := MY_ARRAY(indx).type1;
student_rec.amount := MY_ARRAY(indx).amount1;
student_rec.gross := MY_ARRAY(indx).gross1;
student_recs .extend();
student_recs(student_recs.count()) := student_rec;
student_rec := student_type_wr(null, null, null, null);
student_rec.invoice_date := MY_ARRAY(indx).invoice_date;
student_rec.service_type := MY_ARRAY(indx).type2;
student_rec.amount := MY_ARRAY(indx).amount2;
student_rec.gross := MY_ARRAY(indx).gross2;
student_recs .extend();
student_recs(student_recs.count()) := student_rec;
student_rec := student_type_wr(null, null, null, null);
student_rec.invoice_date := MY_ARRAY(indx).invoice_date;
student_rec.service_type := MY_ARRAY(indx).type3;
student_rec.amount := MY_ARRAY(indx).amount3;
student_rec.gross := MY_ARRAY(indx).gross3;
student_recs .extend();
student_recs(student_recs.count()) := student_rec;
END LOOP;
1 thing here worth trying is to use EXECUTE IMMEDIATE statement like below -
FETCH REFCUR BULK COLLECT INTO MY_ARRAY;
FOR indx IN 1 .. MY_ARRAY.COUNT LOOP
FOR cntr IN 1..3
LOOP
student_rec := student_type_wr(null, null, null, null);
student_rec.invoice_date := MY_ARRAY(indx).invoice_date;
EXECUTE IMMEDIATE 'student_rec.service_type := MY_ARRAY(indx).type' || cntr;
EXECUTE IMMEDIATE 'student_rec.amount := MY_ARRAY(indx).amount' || cntr;
EXECUTE IMMEDIATE 'student_rec.gross := MY_ARRAY(indx).gross' || cntr;
student_recs .extend();
student_recs(student_recs.count()) := student_rec;
END LOOP;
END LOOP;
Though I have never tried this before yet my knowledge about says this wouldn't work.

Related

Q: Detecting Insert/Update/Delete on a Table using OCN and pass the "real_id" to UTL_HTTP.REQUEST on the callback?

I'm trying to detect Insert, Update or Delete on a Table. I know I can't use QRCN for this so I'm trying to use OCN. What I'm trying to achieve is... when an Insert/Update is detected call "callback_iu" when a Delete is detected call 'callback_d'.
DECLARE
qosflags NUMBER;
reginfo_iu cq_notification$_reg_info;
reginfo_d cq_notification$_reg_info;
regid_iu NUMBER;
regid_d NUMBER;
v_cursor SYS_REFCURSOR;
opfilter_iu NUMBER;
opfilter_d NUMBER;
BEGIN
qosflags := DBMS_CQ_NOTIFICATION.QOS_ROWIDS;
opfilter_iu := DBMS_CQ_NOTIFICATION.INSERTOP + DBMS_CQ_NOTIFICATION.UPDATEOP;
opfilter_d := DBMS_CQ_NOTIFICATION.DELETEOP;
reginfo_iu := cq_notification$_reg_info('callback_iu', qosflags,0, opfilter_iu, 0);
regid_iu := DBMS_CQ_NOTIFICATION.NEW_REG_START(reginfo_iu);
OPEN v_cursor FOR
SELECT DBMS_CQ_NOTIFICATION.QOS_ROWIDS, department_id, department_name, manager_id, location_id
FROM HR.departments
CLOSE v_cursor;
DBMS_CQ_NOTIFICATION.REG_END;
reginfo_d := cq_notification$_reg_info('callback_d', qosflags,0, opfilter_d, 0);
regid_d := DBMS_CQ_NOTIFICATION.NEW_REG_START(reginfo_d);
OPEN v_cursor FOR
SELECT DBMS_CQ_NOTIFICATION.QOS_ROWIDS, department_id, department_name, manager_id, location_id
FROM HR.departments
CLOSE v_cursor;
DBMS_CQ_NOTIFICATION.REG_END;
END;
I know that I can get ROWID and the "real_id" using QRCN like this:
CREATE OR REPLACE PROCEDURE chnf_callback
(ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)
IS
event_type NUMBER;
tbname VARCHAR2(60);
numtables NUMBER;
operation_type NUMBER;
numrows NUMBER;
row_id VARCHAR2(2000);
numqueries NUMBER;
qid NUMBER;
real_id NUMBER;
BEGIN
event_type := ntfnds.event_type;
numqueries :=0;
IF (event_type = DBMS_CQ_NOTIFICATION.EVENT_QUERYCHANGE) THEN
numqueries := ntfnds.query_desc_array.count;
FOR i in 1..numqueries LOOP
qid := ntfnds.QUERY_DESC_ARRAY(i).queryid;
numtables := 0;
numtables := ntfnds.QUERY_DESC_ARRAY(i).table_desc_array.count;
FOR j IN 1..numtables LOOP
tbname := ntfnds.QUERY_DESC_ARRAY(i).table_desc_array(j).table_name;
operation_type := ntfnds.QUERY_DESC_ARRAY(i).table_desc_array(j).Opflags;
IF (bitand(operation_type, DBMS_CQ_NOTIFICATION.ALL_ROWS) = 0)
THEN
numrows := ntfnds.query_desc_array(i).table_desc_array(j).numrows;
ELSE
numrows :=0; /* ROWID INFO NOT AVAILABLE */
END IF;
/* The body of the loop is not executed when numrows is ZERO */
FOR k IN 1..numrows LOOP
Row_id := ntfnds.query_desc_array(i).table_desc_array(j).row_desc_array(k).row_id;
select department_id into real_id from hr.departments where rowid = Row_id;
-->INSERT IN NFROWCHANGES<--
INSERT INTO nfrowchanges VALUES(qid, tbname, Row_id, real_id);
END LOOP; /* loop over rows */
END LOOP; /* loop over tables */
END LOOP; /* loop over queries */
END IF;
COMMIT;
END;
I also know that I can check operation_type to know if it's an Insert (2) or Update (4), but I can't make it work with Delete.
So how can I get ROWID and the "real_id" using OCN and pass it to UTL_HTTP.REQ on the callback?
DECLARE req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
BEGIN
req := utl_http.begin_request(
url => 'localhost:3000/departments/'||real_id,
method => 'GET'
);
resp := utl_http.get_response(r => req);
utl_http.end_response(r => resp);
END;
I still don't know how to work with OCN, in this solution I'm using QRCN. Since I can't get row_id when a row is deleted, instead of deleting the row I created a new column called "Active", so when I want to "delete" a row I change "Active" from "Yes" to "No".
So here is the solution to send the "real_id" from the Inserted/Updated/"Deleted" row using UTL_HTTP.
anonymous block:
DECLARE
l_reginfo CQ_NOTIFICATION$_REG_INFO;
l_cursor SYS_REFCURSOR;
l_regid NUMBER;
qosflags NUMBER;
BEGIN
qosflags := DBMS_CQ_NOTIFICATION.QOS_QUERY + DBMS_CQ_NOTIFICATION.QOS_ROWIDS;
l_reginfo := cq_notification$_reg_info ('query_callback', qosflags, 0, 0, 0);
l_regid := dbms_cq_notification.new_reg_start(l_reginfo);
OPEN l_cursor FOR
SELECT
id,
city_name,
country_name,
votes,
active
FROM hr.jsao_super_cities
WHERE active = 'YES';
CLOSE l_cursor;
dbms_cq_notification.reg_end;
END;
/
callback:
CREATE OR REPLACE PROCEDURE query_callback
(ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)
IS
event_type NUMBER;
numtables NUMBER;
operation_type NUMBER;
numrows NUMBER;
row_id VARCHAR2(2000);
is_active VARCHAR2(20);
numqueries NUMBER;
real_id NUMBER;
l_req UTL_HTTP.REQ;
l_resp UTL_HTTP.RESP;
BEGIN
event_type := ntfnds.event_type;
numqueries :=0;
IF (event_type = DBMS_CQ_NOTIFICATION.EVENT_QUERYCHANGE) THEN
numqueries := ntfnds.query_desc_array.count;
FOR i in 1..numqueries LOOP
numtables := 0;
numtables := ntfnds.QUERY_DESC_ARRAY(i).table_desc_array.count;
FOR j IN 1..numtables LOOP
operation_type := ntfnds.QUERY_DESC_ARRAY(i).table_desc_array(j).Opflags;
IF (bitand(operation_type, DBMS_CQ_NOTIFICATION.ALL_ROWS) = 0)
THEN
numrows := ntfnds.query_desc_array(i).table_desc_array(j).numrows;
ELSE
numrows :=0;
END IF;
FOR k IN 1..numrows LOOP
Row_id := ntfnds.query_desc_array(i).table_desc_array(j).row_desc_array(k).row_id;
--getting "real_id"
select id into real_id from hr.jsao_super_cities where rowid = Row_id;
-- 2 = insert
IF(operation_type = 2) THEN
l_req := utl_http.begin_request(
url => 'localhost:3000/city/'||real_id,
method => 'GET'
);
l_resp := utl_http.get_response(r => l_req);
utl_http.end_response(r => l_resp);
-- 4 = update
ELSIF (operation_type = 4) THEN
select active into is_active from hr.jsao_super_cities where id = real_id;
IF (is_active = 'YES') THEN
l_req := utl_http.begin_request(
url => 'localhost:3000/city/'||real_id,
method => 'GET'
);
l_resp := utl_http.get_response(r => l_req);
utl_http.end_response(r => l_resp);
ELSIF (is_active = 'NO') THEN
l_req := utl_http.begin_request(
url => 'localhost:3000/delete/'||real_id,
method => 'GET'
);
l_resp := utl_http.get_response(r => l_req);
utl_http.end_response(r => l_resp);
END IF;
END IF;
END LOOP; /* loop over rows */
END LOOP; /* loop over tables */
END LOOP; /* loop over queries */
END IF;
END query_callback;
/
I hope this can help someone else.

How can I use MOD function within WHILE LOOP with PLS_INTEGER data type?

I'm trying to print on screen odd values of an associative array using a simple "WHILE LOOP" with MOD condition. Is it possible? I know that PLS_INTEGER only accept not decimal values (like int datatype on Java). So... I tried with a NUMBER counter but I receive the same results. How Can I resolve it? . Thanks
SET SERVEROUTPUT ON
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45)
INDEX BY PLS_INTEGER;
t_test_5 type_test;
v_counter_1 PLS_INTEGER;
v_counter_2 NUMBER;
BEGIN
t_test_5(1) := 'Test1';
t_test_5(2) := 'Test2';
t_test_5(3) := 'Test3';
t_test_5(4) := 'Test4';
t_test_5(5) := 'Test5';
t_test_5(6) := 'Test6';
t_test_5(7) := 'Test7';
t_test_5(8) := 'Test8';
t_test_5(9) := 'Test9';
t_test_5(10) := 'Test10';
DBMS_OUTPUT.PUT_LINE('PLS_INTEGER COUNTER TEST');
v_counter_1 := t_test_5.FIRST;
WHILE MOD(v_counter_1, 2) <> 0
LOOP
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('NUMBER COUNTER TEST');
v_counter_2 := t_test_5.FIRST;
WHILE MOD(v_counter_2, 2) <> 0
LOOP
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_2));
v_counter_2 := t_test_5.NEXT(v_counter_2);
END LOOP;
END;
I want to retrieve on screen the values 1, 3, 5, 7, 9 but in both situations I only retrieve value 1:
Procedimiento PL/SQL terminado correctamente.
PLS_INTEGER COUNTER TEST
Test1
NUMBER COUNTER TEST
Test1
The issue is not in the type of your variables, but in the fact that your loops end at the first row that does not match MOD(v_counter_1, 2) <> 0, thus not scanning all the rows.
What you need is not a loop ending when MOD(v_counter_1, 2) = 0, but a loop that scans all the rows, simply printing the values for the only rows that match your criteria:
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45)
INDEX BY PLS_INTEGER;
t_test_5 type_test;
v_counter_1 PLS_INTEGER;
v_counter_2 NUMBER;
BEGIN
t_test_5(1) := 'Test1';
t_test_5(2) := 'Test2';
t_test_5(3) := 'Test3';
t_test_5(4) := 'Test4';
t_test_5(5) := 'Test5';
t_test_5(6) := 'Test6';
t_test_5(7) := 'Test7';
t_test_5(8) := 'Test8';
t_test_5(9) := 'Test9';
t_test_5(10) := 'Test10';
DBMS_OUTPUT.PUT_LINE('PLS_INTEGER COUNTER TEST');
v_counter_1 := t_test_5.FIRST;
WHILE v_counter_1 is not null
LOOP
if mod(v_counter_1, 2) != 0 then
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
end if;
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
END;
the result:
PLS_INTEGER COUNTER TEST
Test1
Test3
Test5
Test7
Test9
Your while loop ends as soon as the counter's value is an even number - so as soon as it reaches 2 then loop will end. What you want is to loop through all the values but skip the even values:
WHILE ( v_counter_1 IS NOT NULL )
LOOP
IF MOD( v_counter_1, 2 ) = 0 THEN
v_counter_1 := t_test_5.NEXT(v_counter_1);
CONTINUE;
END IF;
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
If you are not going to have a sparse array then you do not need to use an associative array:
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45);
t type_test := type_test( 'Test1', 'Test2', 'Test3', 'Test4', 'Test5', 'Test6' );
BEGIN
FOR i = 1 .. t.COUNT / 2 LOOP
DBMS_OUTPUT.PUT_LINE(t(2*i-1));
END LOOP;
END;
/

Can I iterate over columns of a composite type?

Let's say I have the following table named bar:
key | columnA | columnB | columnC
A | B | C | D
E | F | G | H
I want to write a function taking a key and a string and doing the following (best described by examples):
Input: ('A', '${columnB} - ${columnA}') / Output : 'C - B'
Input: ('B', 'Hello ${columnC}') / Output: 'Hello H'
For the moment, I have this implementation:
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
my_row bar%ROWTYPE;
retval VARCHAR2(4000);
BEGIN
BEGIN SELECT * INTO my_row FROM bar WHERE "key" = param_key;
EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL;
END;
retval := format_string;
retval := REPLACE(retval, '${columnA}', my_row.columnA);
retval := REPLACE(retval, '${columnB}', my_row.columnB);
retval := REPLACE(retval, '${columnC}', my_row.columnC);
RETURN retval;
END;
/
I would like to avoid enumerating all columns one by one in the last part, because the structure of my table can change (new columns for instance). Is there a way to iterate on all columns of my_row, and to replace ${the column name} with the value stored in that column, in a generic way?
Thank you
Another way to achieve this.
Create xmltype from table row.
Create xsl-transform from format_string.
Transform xml using xsl
declare
v_string_format varchar2(200) := '{columnA} + {columnB} + {columnA}{columnB}';
v_key varchar2(10) := 'A';
v_cursor sys_refcursor;
l_xml xmltype;
v_xslt VARCHAR2(500):='<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/ROWSET/ROW">{patern}</xsl:template></xsl:stylesheet>';
begin
-- create xsl transform
v_string_format := upper(v_string_format);
v_string_format := REPLACE(v_string_format,'{','<xsl:value-of select="');
v_string_format := REPLACE(v_string_format,'}','"/>');
v_xslt := replace(v_xslt,'{patern}',v_string_format);
dbms_output.put_line(v_string_format);
-- open cursor for table
open v_cursor for select * from bar where key = v_key;
-- get v_cursor as xmltype.
l_xml := xmltype(v_cursor);
-- print xml
dbms_output.put_line(l_xml.getClobVal());
-- tranform xml and print result
dbms_output.put_line(l_xml.transform(xmltype(v_xslt)).getClobVal());
close v_cursor;
end;
A more efficient solution is this one. For sure you have to write more code and it uses the full scope of dynamic SQL.
CREATE OR REPLACE FUNCTION foo (param_key IN VARCHAR2, format_string IN VARCHAR2)
RETURN VARCHAR2 IS
retval VARCHAR2(4000) := format_string;
cur SYS_REFCURSOR;
curId INTEGER;
descTab DBMS_SQL.DESC_TAB;
colCnt NUMBER;
numvar NUMBER;
datevar DATE;
namevar VARCHAR2(4000);
tsvar TIMESTAMP;
BEGIN
OPEN cur FOR SELECT * FROM bar WHERE "key" = param_key;
curId := DBMS_SQL.TO_CURSOR_NUMBER(cur);
DBMS_SQL.DESCRIBE_COLUMNS(curId, colCnt, descTab);
-- Define columns
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, tsvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, namevar, 4000);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.DEFINE_COLUMN(curid, i, ...);
END IF;
END LOOP;
-- Fetch Rows
IF DBMS_SQL.FETCH_ROWS(curid) > 0 THEN
-- Fetch only the first row and do not consider if further rows exist,
-- otherwise use WHILE DBMS_SQL.FETCH_ROWS(curid) > 0 LOOP
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.COLUMN_VALUE(curid, i, namevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', namevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.COLUMN_VALUE(curid, i, numvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.COLUMN_VALUE(curid, i, datevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.COLUMN_VALUE(curid, i, tsvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', tsvar);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.COLUMN_VALUE(curid, i, ...);
--retval := REPLACE(retval, '${'||desctab(i).col_name||'}', ...);
END IF;
END LOOP;
ELSE
retval := NULL;
END IF;
DBMS_SQL.CLOSE_CURSOR(curId);
RETURN retval;
END;
You can get the result you are after using dynamic queries...
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
retval VARCHAR2(4000) := format_string;
cols SYS.ODCIVARCHAR2LIST;
BEGIN
SELECT COLUMN_NAME
BULK COLLECT INTO cols
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'bar'
ORDER BY COLUMN_ID;
FOR i IN 1 .. cols.COUNT LOOP
EXECUTE IMMEDIATE 'SELECT REPLACE( :1, ''${' || cols(i) || '}'', ' || cols(i) || ' ) FROM bar WHERE key = :2'
INTO retval
USING retval, param_key;
END LOOP;
RETURN retval;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
/
... but:
This uses dynamic SQL to query the table directly and does not use a %ROWTYPE record.
You may not have access to USER_TAB_COLUMNS (or may need ALL_TAB_COLUMNS) and the DBA might not want you to have access to the data dictionary tables.
It is probably (almost certainly) very inefficient.
I've seen this done before and never let it pass a code review (writing out the explicit column names has always seemed preferable).
So, while it is possible, I would say don't do this.

merging two indexed nested tables per unique value in oracle plsql

I have this record type, a list of VARCHAR2 :
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
In my PLSQL, I have two of these arrays, and I would like to merge them into a third array, per unique value. Something like :
DECLARE
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
foo varchar2_array;
bar varchar2_array;
foobar varchar2_array;
BEGIN
foo(1) := '1';
foo(2) := '2';
foo(3) := '3';
bar(1) := '2';
bar(2) := '3';
bar(3) := '4';
foobar := unique_merge (foo, bar);
FOR i IN 1 .. foobar.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('foobar(' || i || ') : ' || foobar(i));
END LOOP;
-- output : 1, 2, 3, 4
END;
I am looking for a simple way to do this. Thanks.
Try MULTISET UNION DISTINCT.
The solution could be something like
customer_list_3 := customer_list_2 MULTISET UNION DISTINCT customer_list_1
EDIT:
Ok, I did a mistrake, I didn't see this is not a nested table.
Here a possible impelmentation:
DECLARE
TYPE VAR_TABLE_TYPE IS TABLE OF VARCHAR2(10);
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
foo varchar2_array;
bar varchar2_array;
foobar varchar2_array;
foobar_tmp VAR_TABLE_TYPE:= VAR_TABLE_TYPE() ;
j number;
BEGIN
foo(1) := '1';
foo(2) := '2';
foo(3) := '3';
bar(1) := '2';
bar(2) := '3';
bar(3) := '4';
j:=foo.count;
for i in foo.first .. foo.last
loop
foobar_tmp.EXtend;
foobar_tmp(i) := foo(i) ;
end loop;
for i in bar.first .. bar.last
loop
foobar_tmp.EXtend;
foobar_tmp(j+i) := bar(i) ;
end loop;
foobar_tmp := SET (foobar_tmp) ;
for i in foobar_tmp.first .. foobar_tmp.last
loop
foobar(i) := foobar_tmp(i) ;
end loop;
for i in foobar.first .. foobar.last
loop
DBMS_OUTPUT.PUT_LINE('foobar(' || i || ') '||foobar(i));
end loop;
END;

Creating an Oracle DB stored procedure for creating user accounts and/or checking if account already exists

I am new to oracle db and especially stored procedures.
My main goal is to have a procedure that checks and makes sure that an account (username or email) does not already exist before creating a user in my table.
The problem i have is: When executing the procedure, nothing is inserted into the database.
If someone could check this code over and make sure it is correct, would help alot.
CREATE OR REPLACE PROCEDURE A2PROXYCREATEUSER (
in_name IN VARCHAR2
, in_password IN VARCHAR2
, in_email IN VARCHAR2
, in_subscript IN NUMBER DEFAULT 1
, customaction IN VARCHAR2
, userdata IN VARCHAR2
, userdatalen IN NUMBER
, returncode OUT NUMBER )
AS
CodeSuccess constant number := 0;
CodeAlreadyExists constant number := 1;
CodeInvalidUserName constant number := 2;
CodeAccountCreationDisabled constant number := 3;
CodeInvalidPassword constant number := 4;
CodeKeyInUser constant number := 10;
CodeInvalidKey constant number := 11;
current_name VARCHAR2(32);
current_email varchar2(12);
wonidseq number;
BEGIN
wonidseq := 0;
returncode := CodeSuccess;
SELECT NAME
INTO current_name
FROM WONUSER
WHERE NAME = in_name;
returncode := CodeAlreadyExists;
-- If the first SELECT statement above fails to return any
-- records at all, then the NO_DATA_FOUND exception will be
-- signalled. The following code reacts to this exception
EXCEPTION
WHEN NO_DATA_FOUND THEN
BEGIN
SELECT EMAIL
INTO current_email
FROM WONUSER
WHERE EMAIL = in_email;
returncode:=CodeAlreadyExists;
EXCEPTION
WHEN NO_DATA_FOUND THEN
BEGIN
SELECT UNIQUEID_SEQ.nextval into wonidseq from WONUSER;
INSERT INTO WONUSER(WONUSERSEQ, NAME, PASSWORD, NEWPASSWORD, EMAIL, TRUSTLEVEL, COMMUNITYSEQ, ISBANNED, ISACTIVE)
VALUES(wonidseq, in_name, in_password, NULL, in_email, 120, 0, 0, 1);
returncode := CodeSuccess;
end;
END;
if returncode = CodeSuccess then
commit;
else
rollback;
end if;
END;
EDIT:
I managed to fix the code (probably looks like a hack to someone who knows the syntax well)
create or replace
PROCEDURE A2PROXYCREATEUSER (
in_name IN VARCHAR2
, in_password IN VARCHAR2
, in_email IN VARCHAR2
, in_subscript IN NUMBER DEFAULT 1
, customaction IN VARCHAR2
, userdata IN VARCHAR2
, userdatalen IN NUMBER
, returncode OUT NUMBER )
AS
CodeSuccess constant number := 0;
CodeAlreadyExists constant number := 1;
CodeInvalidUserName constant number := 2;
CodeInvalidEmail constant number := 7;
CodeAccountCreationDisabled constant number := 3;
current_name VARCHAR2(32);
current_email varchar2(12);
wonidseq number;
BEGIN
SELECT LoginName into current_name from WONUSER WHERE loginname = in_name;
returncode := CodeAlreadyExists;
exception
when NO_DATA_FOUND then
returncode := CodeSuccess;
if returncode = CodeSuccess then
BEGIN
SELECT EMAIL into current_email from WONUSER where EMAIL = in_email;
returncode := CodeAlreadyExists;
exception
when NO_DATA_FOUND then
returncode := CodeSuccess;
INSERT INTO WONUSER (WONUSERSEQ, LOGINNAME, PASSWORD, NEWPASSWORD, EMAIL, TRUSTLEVEL, COMMUNITYSEQ, ISBANNED, ISACTIVE,birthdate)
VALUES(wonidseq, in_name, in_password, ' ', in_email, 120, 0, 0, 1, sysdate);
commit;
end;
else
rollback;
end if;
end;
My code would be this one:
PROCEDURE A2PROXYCREATEUSER (...) AS
CodeSuccess constant number := 0;
CodeAlreadyExists constant number := 1;
CodeInvalidUserName constant number := 2;
CodeInvalidEmail constant number := 7;
CodeAccountCreationDisabled constant number := 3;
CURSOR curWonUser IS
SELECT *
FROM WONUSER
WHERE EMAIL = in_email OR NAME = in_name;
WonUser curWonUser%ROWTYPE;
BEGIN
OPEN curWonUser;
FETCH curWonUser INTO WonUser;
IF curWonUser%NOTFOUND THEN
INSERT INTO WONUSER(WONUSERSEQ, NAME, PASSWORD, NEWPASSWORD, EMAIL, TRUSTLEVEL, COMMUNITYSEQ, ISBANNED, ISACTIVE)
VALUES (UNIQUEID_SEQ.NEXTVAL, in_name, in_password, NULL, in_email, 120, 0, 0, 1);
returncode = CodeSuccess;
COMMIT;
ELSE
returncode = CodeAlreadyExists;
END IF;
CLOSE curWonUser;
end;
Note, you are not using any of the other return codes.

Resources