Assign value to array from plsql block - oracle

I have the following code in Oracle Forms detail block
BEGIN
v_product_no := :detail_block.product_no;
Go_block('detail_block');
first_record;
--some if condition
WHILE :SYSTEM.last_record != 'TRUE' LOOP
next_record;
if(:detail_block.product_no = v_product_no) then
-- other condtions
end if;
END LOOP;
I would like to store v_product_no into a some kind of collection object so that I could compare with value of :detail_block.product_no.
How can I do this?
Edit 1
product_no will have values such as K1BATTERY, K2BATTERY, ZCATBATEERY etc.
So if K1BATTERY is same as :detail_block.product_no then proceed with next condition
Edit 2
Go_block('detail_block');
v_product_no := :detail_block.product_no;
v_products(v_product_no) := 1;
first_record;
WHILE :SYSTEM.last_record != 'TRUE' LOOP
if(v_products.exists(v_product_no)) then
alert('duplicate');
end if;
END LOOP;
END if;
Edit 3
Go_block('detail_block');
v_product_no := :detail_block.product_no;
v_products(v_product_no) := 1;
first_record;
-- condition
WHILE :SYSTEM.last_record = 'FALSE' LOOP
next_record;
v_product_no := :detail_block.product_no;
if(v_products.exists(v_product_no)) then
alert('duplicate');
else
v_products(v_product_no) := 1;
end if;
END LOOP;
END if;

Use pl/sql associative array to store already processed values.
declare
type t_processed is table of number(1) index by varchar2(100);
v_product_no varchar2(100); --hold the current value
v_products t_processed; --hold all processed values as keys
begin
v_product_no := :detail_block.product_no;
v_products(v_product_no) := 1; --create entry (v_product_no, 1)
...
--later in while
v_product_no := :detail_block.product_no;
if(v_products.exists(v_product_no)) then --entry exists
-- other conditions
else
v_products(v_product_no) := 1; --create entry (v_product_no, 1)
end if;
...

I think you are having Varying IN list of values in v_product_no variable.
You could do it in following way,
Test# 1
SQL> var product_no VARCHAR2(1000)
SQL> exec :product_no := 'K1BATTERY'
PL/SQL procedure successfully completed.
SQL>
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 v_product_no VARCHAR2(1000);
3 BEGIN
4 v_product_no := 'K1BATTERY, K2BATTERY, ZCATBATEERY';
5 IF :product_no IN (trim(regexp_substr(v_product_no, '[^,]+'))) THEN
6 DBMS_OUTPUT.PUT_LINE('FOUND A MATCH');
7 ELSE
8 DBMS_OUTPUT.PUT_LINE('NO MATCH FOUND');
9 END IF;
10 END;
11 /
FOUND A MATCH
PL/SQL procedure successfully completed.
Test# 2
SQL> var product_no VARCHAR2(1000)
SQL> exec :product_no := 'ABCD'
PL/SQL procedure successfully completed.
SQL>
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 v_product_no VARCHAR2(1000);
3 BEGIN
4 v_product_no := 'K1BATTERY, K2BATTERY, ZCATBATEERY';
5 IF :product_no IN (trim(regexp_substr(v_product_no, '[^,]+'))) THEN
6 DBMS_OUTPUT.PUT_LINE('FOUND A MATCH');
7 ELSE
8 DBMS_OUTPUT.PUT_LINE('NO MATCH FOUND');
9 END IF;
10 END;
11 /
NO MATCH FOUND
PL/SQL procedure successfully completed.
SQL>

Related

Oracle PLSQL refcursor%ROWTYPE the declaration of the type of this expression is incomplete or malformed

I want to assign cursor variable in PL/SQL code and then fetch a row from that. When I declared cursor explicitly, it works.
DECLARE
cursor c1 is SELECT * from acme;
row1 c1%ROWTYPE;
but when I use code to assign cursor per e.g. Oracle PLSQL setting a cursor from a variable like that:
c1 sys_refcursor;
r1 c1%ROWTYPE;
I get error
the declaration of the type of this expression is incomplete or
malformed
writting just r1 %ROWTYPE produces error too.
As per general use of variables explained e.g. here How to declare variable and use it in the same Oracle SQL script? I've tried:
SET SERVEROUTPUT ON
DECLARE
c1 sys_refcursor;
BEGIN
open c1 for 'SELECT * FROM foo';
declare r1 c1%ROWTYPE;
begin
fetch c1 into r1;
end;
DBMS_OUTPUT.PUT_LINE('FOO');
close c1;
END;
and again got the declaration of the type of this expression is incomplete or malformed
How can I declare variable of row type it my case? Oracle 11g.
P.S. workaround would be to fetch c1 into v_empno, v_ename, ect declaring several variables, still, is there a way to use rowtype one?
Something like this?
SQL> set serveroutput on
SQL>
SQL> declare
2 c1 sys_refcursor;
3 r1 dept%rowtype; --> this
4 begin
5 open c1 for 'select * from dept';
6 fetch c1 into r1;
7 dbms_output.put_line(r1.dname);
8 end;
9 /
ACCOUNTING
PL/SQL procedure successfully completed.
SQL>
If your intention is to display result from any given query, you may use this standard procedure similar to the one described in this AskTOM article
create or replace procedure return_result( l_query varchar2 )
is
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_colCnt number := 0;
l_separator varchar2(1);
l_descTbl dbms_sql.desc_tab;
begin
dbms_sql.parse( l_theCursor, l_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
l_separator := '';
for i in 1 .. l_colCnt loop
dbms_output.put( l_separator || l_descTbl(i).col_name );
l_separator := ',';
end loop;
dbms_output.put_line('');
for i in 1 .. l_colCnt loop
dbms_sql.define_column( l_theCursor, i, l_columnValue, 4000 );
end loop;
l_status := dbms_sql.execute(l_theCursor);
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
l_separator := '';
for i in 1 .. l_colCnt loop
dbms_sql.column_value( l_theCursor, i, l_columnValue );
dbms_output.put( l_separator || l_columnValue );
l_separator := ',';
end loop;
dbms_output.new_line;
end loop;
dbms_sql.close_cursor(l_theCursor);
end;
/
Pass any query to this procedure to get result.
set serveroutput on
set feedback off
set sqlformat ansiconsole
set pages 0
BEGIN
return_result('select e.employee_id,e.first_name,e.salary,
d.department_name from
employees e join
departments d on e.department_id= d.department_id');
END;
/
Result
EMPLOYEE_ID,FIRST_NAME,SALARY,DEPARTMENT_NAME
200,Jennifer,4400,Administration
201,Michael,13000,Marketing
202,Pat,6000,Marketing
114,Den,11000,Purchasing
Note: Oracle 12c provides DBMS_SQL.RETURN_RESULT using which this procedure can be easily implemented.

regex to remove commas between quotes in Oracle 11.2g

My code is:
set serveroutput on size unlimited;
DECLARE
v_line_unclean VARCHAR2(32767); -- 32767 BYTES
v_line_clean VARCHAR2(32767);
v_clean_val VARCHAR2(32767);
SQLSMT VARCHAR2(32767);
v_line_in_record INTEGER;
pattern varchar2(15) := '("[^"]*"|[^,]+)';
v_name VARCHAR2(50);
v_first_column VARCHAR2(200);
EMP_FILE UTL_FILE.FILE_TYPE;
BEGIN
DBMS_OUTPUT.ENABLE(9000000);
EMP_FILE := UTL_FILE.FOPEN('EGIS_FILE_DIR','TEST.csv','R', 32767); -- open the file from oracle directory
v_line_in_record := 0; --we skip the first line
IF UTL_FILE.IS_OPEN(EMP_FILE) THEN
LOOP
v_line_in_record := v_line_in_record + 1;
--DBMS_OUTPUT.PUT_LINE(v_line_in_record);
BEGIN
UTL_FILE.GET_LINE(EMP_FILE,v_line_unclean);
IF v_line_in_record = 1 THEN-- first record here
DBMS_OUTPUT.PUT_LINE('');
ELSIF v_line_in_record = 2 THEN-- second record here (header)
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE(v_line_unclean);
v_first_column := SUBSTR(v_line_unclean,1,instr(v_line_unclean,',',10,1)-1);
dbms_output.put_line('1st '||REGEXP_SUBSTR(v_line_unclean, '[^,]+', 1, 1));
ELSE -- body records here);
SELECT REPLACE(v_line_unclean,'((\)|^).*?(\(|$))|,', '\1')INTO v_line_clean FROM DUAL;
SQLSMT := 'INSERT INTO SITE_CONFIG_2G VALUES('''||v_line_clean||''')';
EXECUTE IMMEDIATE SQLSMT;
END IF;
COMMIT;
DBMS_OUTPUT.PUT_lINE(SQLSMT);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_lINE('NO DATA FOUND EXCEPTION');
EXIT;
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_lINE('TO MANY ROW EXCEPTION');
EXIT;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlcode||sqlerrm);
ROLLBACK;
EXIT;
END;--EXCEPTION
END LOOP;
END IF;
UTL_FILE.FCLOSE(EMP_FILE);
END;
Result :
INSERT INTO SITE_CONFIG_2G VALUES('1,gold,benz,2018,1,blue,"34,000,000",6.4,new,CSV')
INSERT INTO SITE_CONFIG_2G VALUES('2,silver,bmw,2016,1,silver,"51,000,000",6.5,New,CSV')
INSERT INTO SITE_CONFIG_2G VALUES('3,bronze,range,2017,1,light-blue,"24,000,000",7.8,New,RVS')
I would like to remove commas between quotes in "24,000,000" to give me "24000000"
Current result is:
3,bronze,range,2017,1,light-blue,"24,000,000",7.8,New,RVS
Expected result is:
3,bronze,range,2017,1,light-blue,"24000000",7.8,New,RVS
can you try this.
select regexp_replace('1,gold,benz,2018,1,blue,"34,000,000",6.4,new,CSV',
'(")([^"|,]+)(,)([^"|,]+)(,)([^"|,]+)(")',
'\1\2\4\6\7') from dual;

sequence generate 10 records each time

I have the following procedure. I need to display 10 unique records each time when I execute the procedure.
CREATE OR REPLACE PROCEDURE USERAUTHENTICATIONCODEWRITE
AS
v_counter NUMBER;
BEGIN
SELECT SEQ_USER_AUTHENTICATION_CODES2.NEXTVAL INTO v_counter FROM DUAL;
LOOP
v_counter := v_counter + 1;
-- if EXIT condition yields TRUE exit the loop
IF v_counter = 11 THEN
EXIT;
END IF;
DBMS_OUTPUT.PUT_LINE ('AUTHENTICATION_CODES = '||v_counter);
END LOOP;
v_counter := v_counter;
-- control resumes here
DBMS_OUTPUT.PUT_LINE ('Done...');
END;
But I won't return the unique numbers in next run. What I miss in the code. Please help me regarding this issue.
Thanks in Advance.
Here is the code you want:
CREATE OR REPLACE PROCEDURE USERAUTHENTICATIONCODEWRITE
AS
v_counter NUMBER;
BEGIN
SELECT SEQ_USER_AUTHENTICATION_CODES2.NEXTVAL INTO v_counter FROM DUAL;
FOR i in 1..10
LOOP
v_counter := v_counter + 1
DBMS_OUTPUT.PUT_LINE ('AUTHENTICATION_CODES = '||v_counter);
END LOOP;
END;
Also you must be sure that your sequence increment by 10 by using the INCREMENT BY clause when you create it:
https://docs.oracle.com/database/121/SQLRF/statements_6017.htm#SQLRF01314

PL/SQL file writing with generic input

I recently created a PL/SQL program that creates five different pipe delimited files from related data in a database.
I could not find a way to dynamically pull different tabular data in this case cursors, into a generic procedure that would create the files.
Instead I had to create five separate procedures, one for each file, that took in five different cursors, one for each file requirement record selection.
I can't help but think that there has to be a better way. I was looking into reference cursors but I don't think they are exactly what I am looking for.
How can I achieve this in PL/SQL?
I think what I am looking for is some generic type that can take any data from a cursor given any amount of records and record columns and have the ability to query itself to find what data is in it.
Pass the cursor into your procedure as a SYS_REFCURSOR. Then, use DBMS_SQL.TO_CURSOR_NUMBER(); to convert the ref cursor to a DBMS_SQL cursor.
Then, use DBMS_SQL.DESCRIBE_COLUMNS to figure out the columns in the cursor and DBMS_SQL.DEFINE_COLUMN, DBMS_SQL.FETCH_ROWS and DBMS_SQL.VALUE to get the data from the cursor into PL/SQL variables. Then, write your PL/SQL variables to your output file.
Here's some code that puts all that together for you.
DECLARE
l_rc SYS_REFCURSOR;
PROCEDURE dump_cursor (p_rc IN OUT SYS_REFCURSOR) IS
-- Dump the results of p_rc to log
l_cursor INTEGER;
l_column_count INTEGER;
l_column_descriptions SYS.DBMS_SQL.desc_tab;
l_status INTEGER;
l_column_value VARCHAR2 (4000);
l_column_width NUMBER;
l_rec_count NUMBER := 0;
l_line VARCHAR2 (4000);
FUNCTION get_length (l_column_def IN SYS.DBMS_SQL.desc_rec)
RETURN NUMBER IS
l_width NUMBER;
BEGIN
l_width := l_column_def.col_max_len;
l_width := CASE l_column_def.col_type WHEN 12 THEN /* DATE */
20 WHEN 2 THEN /* NUMBER */
10 ELSE l_width END;
-- Don't display more than 256 characters of any one column (this was my requirement -- your file writer probably doesn't need to do this
l_width := LEAST (256, GREATEST (l_width, l_column_def.col_name_len));
RETURN l_width;
END get_length;
BEGIN
-- This is the date format that I want to use for dates in my output
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY HH24:MI:SS''';
l_cursor := sys.DBMS_SQL.to_cursor_number (p_rc);
-- Describe columns
sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);
l_line := '';
FOR i IN 1 .. l_column_count LOOP
l_column_width := get_length (l_column_descriptions (i));
l_line := l_line || RPAD (l_column_descriptions (i).col_name, l_column_width);
l_line := l_line || ' ';
DBMS_SQL.define_column (l_cursor,
i,
l_column_value,
4000);
END LOOP;
DBMS_OUTPUT.put_line (l_line);
l_line := '';
FOR i IN 1 .. l_column_count LOOP
l_column_width := get_length (l_column_descriptions (i));
l_line := l_line || RPAD ('-', l_column_width, '-');
l_line := l_line || ' ';
DBMS_SQL.define_column (l_cursor,
i,
l_column_value,
4000);
END LOOP;
DBMS_OUTPUT.put_line (l_line);
-- l_status := sys.DBMS_SQL.execute (l_cursor);
WHILE (sys.DBMS_SQL.fetch_rows (l_cursor) > 0) LOOP
l_rec_count := l_rec_count + 1;
l_line := '';
FOR i IN 1 .. l_column_count LOOP
DBMS_SQL.COLUMN_VALUE (l_cursor, i, l_column_value);
l_column_value := TRANSLATE (l_column_value, CHR (10), CHR (200));
l_column_width := get_length (l_column_descriptions (i));
IF l_column_value IS NULL THEN
l_line := l_line || RPAD (' ', l_column_width);
ELSE
l_line := l_line || RPAD (l_column_value, l_column_width);
END IF;
l_line := l_line || ' ';
END LOOP;
DBMS_OUTPUT.put_line (l_line);
END LOOP;
IF l_rec_count = 0 THEN
DBMS_OUTPUT.put_line ('No data found.');
ELSE
DBMS_OUTPUT.put_line (l_rec_count || ' rows returned.');
END IF;
sys.DBMS_SQL.close_cursor (l_cursor);
-- It would be better to store the current NLS_DATE_FORMAT on entry and restore it here, instead of assuming that it was
-- set to DD-MON-YYYY.
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
EXCEPTION
WHEN OTHERS THEN
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
-- Add your own handling here.
END dump_cursor;
-- Tester code, make sure server output is on
BEGIN
OPEN l_rc FOR 'SELECT object_id, object_name, object_type FROM dba_objects WHERE rownum <= 15';
dump_cursor(l_rc);
END;

ORA-01775: looping chain of synonyms but there are no synonyms

Can't figure out why I'm getting 'SQL Statement ignored' and 'ORA-01775: looping chain of synonyms' on line 52 of this stored procedure. Got any ideas?
CREATE OR REPLACE PACKAGE PURGE_LOG_BY_EVENT_DAYS AS
TYPE dual_cursorType IS REF CURSOR RETURN dual%ROWTYPE;
PROCEDURE log_master_by_event_days (event_id NUMBER, purge_recs_older_than NUMBER, result_cursor OUT dual_cursorType);
END PURGE_LOG_BY_EVENT_DAYS;
/
CREATE OR REPLACE PACKAGE BODY PURGE_LOG_BY_EVENT_DAYS
AS
err_msg VARCHAR2(4000);
PROCEDURE log_master_by_event_days (event_id NUMBER, purge_recs_older_than NUMBER, result_cursor OUT dual_cursorType)
IS
TYPE type_rowid IS TABLE OF ROWID INDEX BY BINARY_INTEGER;
TYPE type_ref_cur IS REF CURSOR;
l_rid type_rowid;
c1 type_ref_cur;
l_sql_stmt VARCHAR2(4000);
proc_start_time DATE := sysdate;
purge_date DATE;
l_bulk_collect_limit NUMBER := 1000;
retry NUMBER := 5;
retry_count NUMBER := 0;
loop_count NUMBER := 0;
err_code VARCHAR2(10);
BEGIN
purge_date := to_date(sysdate - purge_recs_older_than);
l_sql_stmt := '';
l_sql_stmt := l_sql_stmt ||' SELECT rowid FROM LOG_MASTER ';
l_sql_stmt := l_sql_stmt ||' WHERE last_changed_date < :purge_date';
l_sql_stmt := l_sql_stmt ||' AND event_id = :event_id';
-- The following while loop
-- executes the purge code
-- 'retry' number of times in case of ORA-01555
WHILE retry > 0 LOOP
BEGIN
-- START of purge code
OPEN c1 FOR l_sql_stmt USING purge_date, event_id;
LOOP
FETCH c1 BULK COLLECT into l_rid LIMIT l_bulk_collect_limit;
FORALL i IN 1..l_rid.COUNT
DELETE from log_master
WHERE rowid = l_rid(i);
COMMIT;
loop_count := loop_count + 1;
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
-- End of purge code
-- if processing reached this point
-- Process completed successfuly, set retry = 0 to exit loop
retry := 0;
EXCEPTION
WHEN OTHERS THEN
-- ====================================
-- Get error msg
-- ====================================
ROLLBACK;
err_code := sqlcode;
dbms_output.put_line(err_code);
-- ====================================
-- Check if it is 01555
-- if so retry, else exit loop
-- ====================================
retry := retry - 1;
if err_code = '-1555' and retry > 0 THEN
CLOSE c1;
retry_count := retry_count + 1;
else
err_msg := sqlerrm;
exit;
end if;
END;
END LOOP;
IF err_msg IS NULL THEN
open result_cursor for select '1 - PURGE_LOG_BY_EVENT_DAYS ran successfully (event_id : '||event_id||', loop_count : '||loop_count||', bulk_limit : '||l_bulk_collect_limit||', retries : '||retry_count||') ' from dual;
ELSE
open result_cursor for select '2 - PURGE_LOG_BY_EVENT_DAYS After (event_id : '||event_id||', loop_count : '||loop_count||', bulk_limit : '||l_bulk_collect_limit||', retries : '||retry_count||') with Error: ' || err_msg from dual;
END IF;
END log_master_by_event_days;
END PURGE_LOG_BY_EVENT_DAYS;
I have no idea why you're getting the synonym error. But that's a lot of code for something that should be a single DELETE statement. I assume you've changed it to commit-every-n to avoid rollback errors. It would be nice if you could get your DBA to increase the undo space so you can actually do the work you need to do. Failing that, I think you can still make it much simpler:
LOOP
DELETE FROM log_master
WHERE last_changed_date < :purge_date
AND event_id = :event_id
AND rownum <= :batch_delete_limit
USING purge_date, event_id, l_bulk_collect_limit;
EXIT WHEN SQL%NOTFOUND;
END LOOP;
And you can throw your retry logic around that if you want.
Apologies if I've missed some subtlety that makes this different from what you're doing.
SELECT table_owner, table_name, db_link
FROM dba_synonyms
WHERE owner = 'PUBLIC' and db_link is not null
returns 0 rows
as far as i know, there are no synonyms.......

Resources