oracle delete data from table with rownum - oracle

I have a procedure that deletes the data from table1. Five instances of the procedure running the same time and all ran without any error/exception.
But still, there are many records in the table1 where EndTime <= v_purgedate. Please help.
declare
rowcount1 NUMBER;
begin
LOOP
delete table1 eh
where eh.EndTime <= v_purgedate
and rownum <= 30000 ;
rowcount1 := rowcount1 + sql%rowcount;
commit;
exit when rowcount1=0;
rowcount1=0;
END LOOP;
end;
/

You are trying to do what is called "do it yourself parallelism", as opposed to the parallel processing that Oracle can do by itself if you ask nicely.
There is a package called DBMS_PARALLEL_EXECUTE that does this for you. You split the data into chunks (based on ROWIDs or numbers), then call the RUN_TASK procedure to process each chunk, either serially or in parallel. There is one commit per chunk.
Here is a demonstration that deletes 30,000 rows per commit. First, test data:
SQL> create table table1(endtime) as
2 select trunc(sysdate) - level/24 from dual
3 connect by level <= 240000;
Table TABLE1 created.
SQL> insert into table1 select * from table1;
240,000 rows inserted.
SQL> insert into table1 select * from table1;
480,000 rows inserted.
SQL> insert into table1 select * from table1;
960,000 rows inserted.
SQL> select count(*) from table1;
COUNT(*)
----------
1920000
Now, here is the code:
SQL> declare
2 l_task_name varchar2(128) := 'Delete_TABLE1';
3 l_sql_chunk clob := q'§
4 select date '2100-01-01' - min(endtime) start_id,
5 date '2100-01-01' - max(endtime) end_id,
6 chunk
7 from (
8 select endtime,
9 ceil(row_number() over(order by endtime) / 30000) chunk
10 from table1 where endtime < sysdate - 8000
11 )
12 group by chunk
13 §';
14 l_sql_run clob := q'§
15 delete from table1
16 where endtime between
17 date '2100-01-01' - :start_id and
18 date '2100-01-01' - :end_id
19 §';
20 l_boolean boolean := false;
21 task_not_found EXCEPTION;
22 PRAGMA EXCEPTION_INIT(task_not_found, -29498);
23 begin
24 begin
25 DBMS_PARALLEL_EXECUTE.DROP_TASK (l_task_name);
26 exception when task_not_found then null;
27 end;
28 DBMS_PARALLEL_EXECUTE.CREATE_TASK (l_task_name);
29 DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_SQL (
30 task_name => l_task_name,
31 sql_stmt => l_sql_chunk,
32 by_rowid => l_boolean
33 );
34 DBMS_PARALLEL_EXECUTE.RUN_TASK (
35 task_name => l_task_name,
36 sql_stmt => l_sql_run,
37 language_flag => 1,
38 parallel_level => 0 -- 0 for serial, 2+ for parallel
39 );
40 end;
41 /

you have an endless Loop, because rowcount1 will never be 0 when your table contains data that will be deleted.
i think what you what is
declare
rowcount1 NUMBER;
begin
LOOP
delete table1 eh
where eh.EndTime <= v_purgedate -- you should also initialize a variable
and rownum <= 30000 ;
--rowcount1 := rowcount1 + sql%rowcount;
commit;
exit when sql%rowcount = 0;
rowcount1=0;
END LOOP;
end;
/

When you declare the variable rowcount1, you do not assign any number to it, so its value is NULL. Afterwards, when you add a number to NULL the result is always NULL. This is surely not what you want. To illustrate:
SQL> declare
2 rowcount1 NUMBER;
3 begin
4 dbms_output.put_line('<'||rowcount1||'>');
5 rowcount1 := rowcount1 + 1;
6 dbms_output.put_line('<'||rowcount1||'>');
7 end;
8 /
<>
<>
PL/SQL procedure successfully completed.
It seems your code will go in an endless loop. You say that is not the case, so clearly you are not showing us any real code.
Anyway, this is not a good way to do what you want. I'll post another answer saying why.
Regards, Stew Ashton

It's a easy code for delete data in a table .
Try it.
declare
cursor c is
select * from table1 where rownum<=30000;
begin
for i in c loop
delete from table1 where EndTime <= v_purgedate;
end loop;
commit;
end;

Related

I want to fetch column values from column name rollnoofstud of tableA using PL/SQL

DECLARE
TYPE norollno IS TABLE OF VARCHAR2(100);
rollno norollno;
BEGIN
BEGIN
SELECT token
BULK COLLECT INTO rollno
FROM tableA
WHERE columname='rollnoofstud';
EXCEPTION
WHEN NO_DATA_FOUND THEN
rollno := norollno();
END ;
IF rollno >0 THEN
FOR i IN rollno.FIRST..norollno.LAST
LOOP
<doSomeThing>
END LOOP;
END IF;
END;
I am trying this but I am not getting output. I doubt if my select statement is correct.
I don't have your table so I created one:
SQL> CREATE TABLE tablea
2 AS
3 SELECT ename AS token, 'rollnoofstud' AS columname
4 FROM emp
5 WHERE deptno = 10;
Table created.
Code you posted isn't that wrong; requires a little bit of fixing (see line #17, the way you check whether collection contains something (count it!); typo in FOR loop):
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 TYPE norollno IS TABLE OF VARCHAR2 (100);
3
4 rollno norollno;
5 BEGIN
6 BEGIN
7 SELECT token
8 BULK COLLECT INTO rollno
9 FROM tableA
10 WHERE columname = 'rollnoofstud';
11 EXCEPTION
12 WHEN NO_DATA_FOUND
13 THEN
14 rollno := norollno ();
15 END;
16
17 IF rollno.COUNT > 0
18 THEN
19 FOR i IN rollno.FIRST .. rollno.LAST
20 LOOP
21 DBMS_OUTPUT.put_line (rollno (i));
22 END LOOP;
23 END IF;
24 END;
25 /
CLARK --> here's the result
KING
MILLER
PL/SQL procedure successfully completed.
SQL>
[EDIT: with your sample table and data:]
(note that there's no text datatype in Oracle!)
SQL> CREATE TABLE students
2 (
3 rollnostud INTEGER PRIMARY KEY,
4 name VARCHAR2 (10) NOT NULL,
5 gender VARCHAR2 (1) NOT NULL
6 );
Table created.
SQL> INSERT INTO students
2 VALUES (1, 'Ryan', 'M');
1 row created.
SQL> INSERT INTO students
2 VALUES (2, 'Joanna', 'F');
1 row created.
SQL> INSERT INTO students
2 VALUES (3, 'John', 'M');
1 row created.
Procedure:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 TYPE norollno IS TABLE OF VARCHAR2 (100);
3
4 rollno norollno;
5 BEGIN
6 BEGIN
7 SELECT name
8 BULK COLLECT INTO rollno
9 FROM students;
10 EXCEPTION
11 WHEN NO_DATA_FOUND
12 THEN
13 rollno := norollno ();
14 END;
15
16 IF rollno.COUNT > 0
17 THEN
18 FOR i IN rollno.FIRST .. rollno.LAST
19 LOOP
20 DBMS_OUTPUT.put_line (rollno (i));
21 END LOOP;
22 END IF;
23 END;
24 /
Ryan
Joanna
John
PL/SQL procedure successfully completed.
SQL>

plsql what is wrong with this code error 'INTO list is of wrong type'

Can someone please help me? Why do I get this error "INTO list is of wrong type"?
DECLARE
TYPE v_dept_table IS TABLE OF DEPARTMENTS.DEPARTMENT_NAME%TYPE INDEX BY PLS_INTEGER;
v_record_dept_table v_dept_table;
v_loop_count NUMBER := 10;
v_dept_no NUMBER := 1;
BEGIN
FOR v_dept_no IN 1..v_loop_count LOOP
SELECT DEPARTMENTS.DEPARTMENT_NAME
INTO v_record_dept_table
FROM DEPARTMENTS
WHERE DEPARTMENT_ID = v_dept_no;
v_dept_no := v_dept_no + 1;
INSERT
INTO v_dept_table
VALUES v_record_dept_table;
END LOOP;
FOR indx IN NVL (v_dept_table.FIRST, 0) .. NVL (v_dept_table.LAST, -1)
LOOP
DBMS_OUTPUT.PUT_LINE(v_dept_table(indx));
END LOOP;
END;
ORA-06550: line 16, column 14:
PLS-00597: expression 'V_RECORD_DEPT_TABLE' in the INTO list is of wrong type
Why is it a wrong type? I'm using the hr schema from Oracle
Not exactly like that; you've done several things wrong. I tried to fix your code (with as little modifications as possible); have a look, read comments I wrote, compare it to your code. Side note: I used Scott's DEPT table as I don't have your DEPARTMENTS.
SQL> set serveroutput on
SQL> DECLARE
2 TYPE v_dept_table IS TABLE OF dept.dname%TYPE
3 INDEX BY PLS_INTEGER;
4
5 v_record_dept_table v_dept_table;
6
7 v_loop_count NUMBER := 10;
8 v_dept_no NUMBER := 1;
9 BEGIN
10 FOR v_dept_no IN 1 .. v_loop_count
11 LOOP
12 BEGIN
13 SELECT dname
14 INTO v_record_dept_table (v_dept_no) --> you're missing "(v_dept_no)"
15 FROM dept
16 WHERE deptno = v_dept_no;
17 -- Don't manually increment FOR loop variable; Oracle does it itself
18 -- v_dept_no := v_dept_no + 1;
19
20 -- You can't insert into "type"; besides, you've already inserted into V_RECORD_DEPT_TABLE.
21 -- INSERT INTO v_dept_table VALUES v_record_dept_table;
22 EXCEPTION
23 WHEN NO_DATA_FOUND
24 THEN
25 NULL;
26 END;
27 END LOOP;
28
29 -- loop through V_RECORD_DEPT_TABLE (collection), not V_DEPT_TABLE (type). No need for NVL.
30 FOR indx IN NVL (v_record_dept_table.FIRST, 0) ..
31 NVL (v_record_dept_table.LAST, -1)
32 LOOP
33 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
34 END LOOP;
35 END;
36 /
ACCOUNTING
PL/SQL procedure successfully completed.
SQL>
Alternatively, see whether this helps. I used built-in type (sys.odcivarchar2list) and BULK COLLECT INTO (performs better).
SQL> DECLARE
2 v_record_dept_table SYS.odcivarchar2list;
3 BEGIN
4 SELECT dname
5 BULK COLLECT INTO v_record_dept_table
6 FROM dept;
7
8 FOR indx IN v_record_dept_table.FIRST .. v_record_dept_table.LAST
9 LOOP
10 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
11 END LOOP;
12 END;
13 /
ACCOUNTING
RESEARCH
SALES
OPERATIONS
PL/SQL procedure successfully completed.
SQL>

How to update ref cursor values in oracle?

I want to fetch limited no. of rows using refcursor. then I need to update same set of records. is it possible?
create or replace PROCEDURE myproc (
P_ROWCOUNT IN NUMBER,
OUT_TXN_IDS OUT OF_CR_TYPE,
P_CD_ERROR OUT NUMBER,
P_DS_ERROR OUT VARCHAR2
)
AS
V_TXNID NUMBER;
BEGIN
P_CD_ERROR := 0;
P_DS_ERROR := 'SUCCESS';
OPEN OUT_TXN_IDS for
SELECT id FROM table1 WHERE status='N' AND ROWNUM<=P_ROWCOUNT;
EXCEPTION
WHEN OTHERS THEN
P_CD_ERROR := sqlcode;
P_DS_ERROR := 'WF-ERROR - myproc - ' || substr(SQLERRM, 1, 200);
RETURN;
END myproc;
I need to update same records to status Y after refcursor returns. can we do this. please suggest
I don't have your tables nor data so I simplified it a little bit, but - it should work nonetheless.
Initial statuses:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 7
N 7
Procedure: basically, you'd modify rows represented by ID returned by ref cursor.
SQL> DECLARE
2 out_txn_ids SYS_REFCURSOR;
3 p_rowcount NUMBER := 5;
4 l_id table1.id%TYPE;
5 BEGIN
6 OPEN out_txn_ids FOR SELECT id
7 FROM table1
8 WHERE status = 'N'
9 AND ROWNUM <= p_rowcount;
10
11 LOOP
12 FETCH out_txn_ids INTO l_id;
13
14 EXIT WHEN out_txn_ids%NOTFOUND;
15
16 UPDATE table1
17 SET status = 'Y'
18 WHERE id = l_id;
19 END LOOP;
20
21 CLOSE out_txn_ids;
22 END;
23 /
PL/SQL procedure successfully completed.
Result:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 12
N 2
SQL>

how to get the 2nd row and the last row of a file using UTL_FILE

I have a file which consists of thousand rows and I need to get a portion of the 2nd row (about 50 characters) and the last row of the file. Please advise. Thank you.
Im trying to do something like UTL_FILE.READLINE(fileloc, filename, nrow, lastrow).
SAMPLE:
Filename: CLOSED_SO_20190124.txt
DATA in the FILE:
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
EXPECTED OUTPUT:
650930363|1-8IGO3S82920|
650821459|1-6HWQUF11209|
Here's an example.
Directory name & its location, as well as sample file contents:
SQL> select directory_name, directory_path from all_directories;
DIRECTORY_NAME DIRECTORY_PATH
------------------------------ --------------------
EXT_DIR c:\temp
SQL> $type c:\temp\sofile.txt
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
SQL>
The procedure: a local counter (l_cnt) knows line number; if it is equal to 2, display that line. Also, when nothing's being found (so exception handler section is executed), I've reached the end of the file so I'm displaying the last line as well.
SQL> set serveroutput on
SQL>
SQL> declare
2 l_file utl_file.file_type;
3 l_dir varchar2(20) := 'EXT_DIR';
4 l_name varchar2(20) := 'sofile.txt';
5 l_line varchar2(50);
6 l_cnt number := 0;
7 begin
8 l_file := utl_file.fopen (l_dir, l_name, 'R');
9 loop
10 begin
11 utl_file.get_line(l_file, l_line);
12 l_cnt := l_cnt + 1;
13 if l_cnt = 2 then
14 dbms_output.put_line('2nd : ' || l_line);
15 end if;
16 exception
17 when no_data_found then
18 dbms_output.put_line('last: ' || l_line);
19 exit;
20 end;
21 end loop;
22 utl_file.fclose(l_file);
23 end;
24 /
2nd : 650930363|1-8IGO3S82920|
last: 650821459|1-6HWQUF11209|
PL/SQL procedure successfully completed.
SQL>
In Oracle 11g
select col
from
(
select
rownum AS rnum,
LEFT(myCol, 50) col
from Table1
where Rownum < 3
)
WHERE rnum = 2
In Oracle 12c
select LEFT(mycol, 50) col
from Table1
ORDER BY val
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
For the last row
select LEFT(mycol, 50) col
from my_table
where pk = ( select max(pk) from my_table )
then you can union them

ORACLE SQL%ROWCOUNT doesn't work inside a cursor fetch

In my following code, I'm unable to get the inserted row count within a cursor fetch.
...
row_count NUMBER:= 0;
BEGIN
OPEN object_id_cur(id, c_bool);
LOOP
FETCH object_id_cur INTO l_object_id;
EXIT WHEN object_id_cur%NOTFOUND;
INSERT INTO table1(
id,
num
)
SELECT t2.r_object_id,
t2.num
FROM table2
WHERE t2.r_object_id = l_object_id.r_object_id;
row_count:= row_count + SQL%ROWCOUNT;
-- I also tried dbms_output.put_line(SQL%ROWCOUNT);
END LOOP;
CLOSE object_id_cur;
COMMIT;
dbms_output.put_line('insert count= ' || row_count || ' rows inserted...');
END;
My obtained result is: count = rows inserted... count is blank. If I move the insert outside the cursor fetch, then the row count works just fine. Is there a logical explanation for this? Thanks!
count is a reserved keyword. Use "count" or some other identifer say cnt. Also, add the missing semicolon at then end of the increment statement.
declare
cnt number := 0;
begin
. . .
cnt := cnt + SQL%ROWCOUNT;
. . .
Demo:
SQL> create table t (id int);
Table created.
SQL> declare
2 cursor c is
3 select level n from dual connect by level <= 100; -- produces 100 rows
4 n int;
5 cnt number := 0;
6 begin
7 open c;
8 loop
9 fetch c into n;
10 exit when c%notfound;
11 insert into t (id)
12 select n from dual union all
13 select n from dual; -- insert each value twice
14 cnt := cnt + sql%rowcount;
15 end loop;
16 close c;
17 dbms_output.put_line(cnt); -- should be 200
18 end;
19 /
200 -- correct output
PL/SQL procedure successfully completed.
SQL>
As you can see above, the SQL%rowcount works correctly. It could be that your below select query is not producing any rows.
SELECT t2.r_object_id,
t2.num
FROM table2
WHERE t2.r_object_id = l_object_id.r_object_id;
SQL%ROWCOUNT return the number of rows fetched/processed by the last DML executed. If the DML fails after fetching 1 row, due to any reason, SQL%ROWCOUNT will return only 1, the number of rows fetched/processed so far. It wont give you the total count. I did a simple PLSQL block and getting the SQL%ROWCOUNT working fine. Seems there is something else which is not working in your code.
declare
num number := 0;
begin
for i in 1 .. 10
loop
insert into a_table (id)
values (i);
num := num + sql%rowcount;
end loop;
dbms_output.put_line (num);
end;

Resources