Batch process for oracle sql - sql

I am copying data from one table to another table in the same database using the following query "insert into table select * from table where time_stamp <= (select ADD_MONTHS(sysdate,-1) from dual)" but i need to restrict it to 1000 rows at a time then commit the changes and continue copying. Can someone help me out with this?

Why would you do that? It makes the process slower, harder to code, harder to recover from in the event of an error.
Unless you've got an exceptional reason, commit only at the end. Otherwise you have to write PL/SQL to do it.
Your code should be:
insert into table
select *
from table
where time_stamp <= ADD_MONTHS(sysdate,-1);

This is PL/SQL block you can use to achieve your goal
declare
cursor c is
select *
from tableName
where time_stamp <= add_months(sysdate,-1);
type tbl is table of c%rowtype;
t tbl;
begin
open c;
loop
fetch c bulk collect into t limit 1000;
exit when c%notfound;
forall i in 1..t.count
insert into tableName2 values t(i);
commit;
end loop;
close c;
end;
Unfortunately I do not know your columns so you can rewrite this part
insert into tableName2 values t(i);
e.g. like that
insert into tableName2(col1,col2) values t(i).col1,t(i).col2;
I have not tested, but this is more about an idea how to do it.

Related

Oracle Procedure to insert all records from one staging table into main table

I wrote Stored Procedure (SP) where inside SP, 2 SP separated for 2 insertion from table. Both table contains more than 25 columns in each temp & main table. Below is query-
create or replace procedure sp_main as
procedure tbl1_ld as
cursor c1 is select * from tmp1;
type t_rec1 is table of c1%rowtype;
v_rec1 t_rec1;
begin
open c1;
loop
fetch c1 bulk collect into v_rec1 limit 1000;
exit when v_rec1.count=0;
insert into tbl1 values v_rec1;
end loop;
end tbl1_ld;
procedure tbl2_ld as
cursor c2 is select * from tmp2;
type t_rec2 is table of c2%rowtype;
v_rec2 t_rec2;
begin
open c2;
loop
fetch c2 bulk collect into v_rec2 limit 1000;
exit when v_rec2.count=0;
insert into tbl2 values v_rec2;
end loop;
end tbl2_ld;
begin
null;
end sp_main;
/
I used EXECUTE IMMEDIATE 'insert into tbl1 select * from tmp1'; for insertion inside both SP tbl1_ld & tbl2_ld instead of using cursor, SP compiled but no record has been inserted.
Well, you didn't actually run any of these procedures. The last few lines of your code should be
<snip>
end tbl2_ld;
begin
tbl1_ld; --> this
tbl2_ld --> this
end sp_main;
/
On the other hand, I prefer avoiding insert into ... select * from because it just loves to fail when you modify tables' description and don't fix code that uses those tables.
Yes, I know - it is just boring to name all 25 columns, but - in my opinion - it's worth it. Therefore, I'd just
begin
insert into tbl1 (id, name, address, phone, ... all 25 columns)
select id, name, address, phone, ... all 25 columns
from tmp1;
insert into tbl2 (id, name, address, phone, ... all 25 columns)
select id, name, address, phone, ... all 25 columns
from tmp2;
end;
In other words, no cursors, types, loops, ... nothing. Could have been pure SQL (i.e. no PL/SQL). If you want to restrict number of rows inserted, use e.g. ... where rownum <= 1000 (if that's why you used the limit clause).
As of dynamic SQL you mentioned (execute immediate): why would you use it? There's nothing dynamic in code you wrote.

How to store multiple rows in a variable in pl/sql function?

I'm writing a pl/sql function. I need to select multiple rows from select statement:
SELECT pel.ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
if i use:
SELECT pel.ceid
INTO v_ceid
it only stores one value, but i need to store all values that this select returns. Given that this is a function i can't just use simple select because i get error, "INTO - is expected."
You can use a record type to do that. The below example should work for you
DECLARE
TYPE v_array_type IS VARRAY (10) OF NUMBER;
var v_array_type;
BEGIN
SELECT x
BULK COLLECT INTO
var
FROM (
SELECT 1 x
FROM dual
UNION
SELECT 2 x
FROM dual
UNION
SELECT 3 x
FROM dual
);
FOR I IN 1..3 LOOP
dbms_output.put_line(var(I));
END LOOP;
END;
So in your case, it would be something like
select pel.ceid
BULK COLLECT INTO <variable which you create>
from pa_exception_list
where trunc(pel.creation_Date) >= trunc(sysdate-7);
If you really need to store multiple rows, check BULK COLLECT INTO statement and examples. But maybe FOR cursor LOOP and row-by-row processing would be better decision.
You may store all in a rowtype parameter and show whichever column you want to show( assuming ceid is your primary key column, col1 & 2 are some other columns of your table ) :
SQL> set serveroutput on;
SQL> declare
l_exp pa_exception_list%rowtype;
begin
for c in ( select *
from pa_exception_list pel
where trunc(pel.creation_date) >= trunc(SYSDATE-7)
) -- to select multiple rows
loop
select *
into l_exp
from pa_exception_list
where ceid = c.ceid; -- to render only one row( ceid is primary key )
dbms_output.put_line(l_exp.ceid||' - '||l_exp.col1||' - '||l_exp.col2); -- to show the results
end loop;
end;
/
SET SERVEROUTPUT ON
BEGIN
FOR rec IN (
--an implicit cursor is created here
SELECT pel.ceid AS ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
)
LOOP
dbms_output.put_line(rec.ceid);
END LOOP;
END;
/
Notes from here:
In this case, the cursor FOR LOOP declares, opens, fetches from, and
closes an implicit cursor. However, the implicit cursor is internal;
therefore, you cannot reference it.
Note that Oracle Database automatically optimizes a cursor FOR LOOP to
work similarly to a BULK COLLECT query. Although your code looks as if
it fetched one row at a time, Oracle Database fetches multiple rows at
a time and allows you to process each row individually.

SQL Oracle get the all columns of a cursor to use in a insert into

I want to create a procedure in which I use a cursor to select certain lines and then insert them in another table. I wonder if there is a notation to write it faster.
For instance here is the complete procedure
create or replace procedure myProc as
Cursor lines is
select * from table1 where c = '2';
begin
for line in lines loop
insert into table2 values(line.a, line.b, line.c, line.d ....);
end loop;
end;
/
I want to know if I can replace the 'insert into' line by something like
insert into table2 values(line.something);
or
insert into tables2 values(something(line));
(I think a view could be more effective but it's not the question here.)
Absolutely:
create or replace procedure myProc as
begin
insert into table2( . . .)
select a, b, c, d, . .
from table1
where c = '2';
end;
/
You should list the columns in table2 as well. That is what the table2( . . . ) means.
Despite the following code should work in case structure of both tables were identical...
INSERT INTO target_table
SELECT * FROM source_table;
... you should avoid this way of programming because any column addition to source or target table will end in SQL became invalid.
Looks like you're looking for a short-cut in order to avoid qualifying column names in an insert statement.
Well, although I do not recommend this, it can be done bug it's a little tedious. Here is an example:
if you have a table employee (employee_id, employee_name, designation). You'll need to create two types in the database-
a. An object type which is similar to employee-
create type employee_obj as object (employee_id number(28,0),
employee_name varchar2(100),
designation varchar2(30));
b. Create the set type of this record-
create type employee_obj_set is table of employee_obj;
c. In your PL/SQL procedure you can use something like:
DECLARE
empset employee_obj_set;
-- More variables here
BEGIN
-- other operations
-- populate your empset
-- other operations
INSERT INTO employee
SELECT * FROM table(empset);
-- other operations
END;
/
For faster inserts use bulk collect/FORALL insert instead of singular inserts.Find below sample...
DECLARE
CURSOR lines is select * from table1 where c = '2';
type lines_ty is table of table2%rowtype;
l_lines_t2 lines_ty;
BEGIN
OPEN lines;
LOOP
FETCH lines BULK COLLECT INTO l_lines_t2 LIMIT 500;
FORALL i IN 1..l_lines_t2.COUNT
INSERT INTO table2 VALUES l_lines_t2(i);
EXIT WHEN lines%NOTFOUND;
END LOOP;
CLOSE lines;
END;

How to insert one row at a time in SQL using cursor

Hi i am using the below PLSQL script to insert rows in new table new_table.
set serveroutput on SIZE 1000000;
DECLARE
CURSOR get_record IS
SELECT * from cycle_table ;
BEGIN
FOR rec IN get_record
LOOP
DBMS_OUTPUT.put_line('Inserting Record into new_table..');
EXECUTE IMMEDIATE('insert into new_table
select cycle_code,cycle_instance,cycle_start_date,cycle_end_date
from cycle_table');
END LOOP;
COMMIT;
END;
/
Now the table cycle_table consist only 4 rows. The loop runs only four times beacuse its printing 'Inserting Record into new_table..' 4 times only.
But when i see the new_table it consist 16 rows. Which means everytime the loop iterates it insert all the 4 rows and thus total 16 rows.
What i want is that it insert single row at a time.
So that i can perform other actions on that row also. Like if the row already exist, insert in some other table or anything.
Please suggest what can I do here? I am using SQL developer on oracle 10g
Thanks in advance
It is very simple:
set serveroutput on SIZE 1000000;
DECLARE
BEGIN
FOR rec in (select * from cycle_table)
LOOP
DBMS_OUTPUT.put_line('Inserting Record into new_table..');
insert into new_table (cycle_code,
cycle_instance,
cycle_start_date,
cycle_end_date)
values (rec.cycle_code,
rec.cycle_instance,
rec.cycle_start_date,
rec.cycle_end_date);
END LOOP;
COMMIT;
END;
/
I would discourage this approach, though, as you could run into a performance issue if there is a large number of records. You have only four, so it's fine.
The reason I'm against this is that there is context switching involved between Oracle's PL/SQL engine and SQL engine. I'd suggest you do an insert into .... select... or use a forall instead, as these are the least resource-consuming approaches.
A more efficient way is to eliminate all of the looping, and allow the SQL to handle everything. Here is my suggestion:
BEGIN
-- Handle matches first, because after you handle non-matches, everything matches
INSERT INTO match_table (cycle_code, cycle_instance, cycle_start_date
, cycle_end_date)
SELECT cycle_table.cycle_code, cycle_table.cycle_instance, cycle_table.cycle_start_date
, cycle_table.cycle_end_date
FROM cycle_table INNER JOIN new_table ON (new_table.cycle_code = cycle_table.cycle_code);
-- Single insert to insert all non matching records
INSERT INTO new_table (cycle_code, cycle_instance, cycle_start_date
, cycle_end_date)
SELECT cycle_code, cycle_instance, cycle_start_date
, cycle_end_date
FROM cycle_table
WHERE NOT EXISTS
(SELECT NULL
FROM new_table
WHERE new_table.cycle_code = cycle_table.cycle_code);
COMMIT;
END;

PLSQL Insert into with subquery and returning clause

I can't figure out the correct syntax for the following pseudo-sql:
INSERT INTO some_table
(column1,
column2)
SELECT col1_value,
col2_value
FROM other_table
WHERE ...
RETURNING id
INTO local_var;
I would like to insert something with the values of a subquery.
After inserting I need the new generated id.
Heres what oracle doc says:
Insert Statement
Returning Into
OK i think it is not possible only with the values clause...
Is there an alternative?
You cannot use the RETURNING BULK COLLECT from an INSERT.
This methodology can work with updates and deletes howeveer:
create table test2(aa number)
/
insert into test2(aa)
select level
from dual
connect by level<100
/
set serveroutput on
declare
TYPE t_Numbers IS TABLE OF test2.aa%TYPE
INDEX BY BINARY_INTEGER;
v_Numbers t_Numbers;
v_count number;
begin
update test2
set aa = aa+1
returning aa bulk collect into v_Numbers;
for v_count in 1..v_Numbers.count loop
dbms_output.put_line('v_Numbers := ' || v_Numbers(v_count));
end loop;
end;
You can get it to work with a few extra steps (doing a FORALL INSERT utilizing TREAT)
as described in this article:
returning with insert..select
T
to utilize the example they create and apply it to test2 test table
CREATE or replace TYPE ot AS OBJECT
( aa number);
/
CREATE TYPE ntt AS TABLE OF ot;
/
set serveroutput on
DECLARE
nt_passed_in ntt;
nt_to_return ntt;
FUNCTION pretend_parameter RETURN ntt IS
nt ntt;
BEGIN
SELECT ot(level) BULK COLLECT INTO nt
FROM dual
CONNECT BY level <= 5;
RETURN nt;
END pretend_parameter;
BEGIN
nt_passed_in := pretend_parameter();
FORALL i IN 1 .. nt_passed_in.COUNT
INSERT INTO test2(aa)
VALUES
( TREAT(nt_passed_in(i) AS ot).aa
)
RETURNING ot(aa)
BULK COLLECT INTO nt_to_return;
FOR i IN 1 .. nt_to_return.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
'Sequence value = [' || TO_CHAR(nt_to_return(i).aa) || ']'
);
END LOOP;
END;
/
Unfortunately that's not possible. RETURNING is only available for INSERT...VALUES statements. See this Oracle forum thread for a discussion of this subject.
You can't, BUT at least in Oracle 19c, you can specify a SELECT subquery inside the VALUES clause and so use RETURNING! This can be a good workaround, even if you may have to repeat the WHERE clause for every field:
INSERT INTO some_table
(column1,
column2)
VALUES((SELECT col1_value FROM other_table WHERE ...),
(SELECT col2_value FROM other_table WHERE ...))
RETURNING id
INTO local_var;
Because the insert is based on a select, Oracle is assuming that you are permitting a multiple-row insert with that syntax. In that case, look at the multiple row version of the returning clause document as it demonstrates that you need to use BULK COLLECT to retrieve the value from all inserted rows into a collection of results.
After all, if your insert query creates two rows - which returned value would it put into an single variable?
EDIT - Turns out this doesn't work as I had thought.... darn it!
This isn't as easy as you may think, and certainly not as easy as it is using MySQL. Oracle doesn't keep track of the last inserts, in a way that you can ping back the result.
You will need to work out some other way of doing this, you can do it using ROWID - but this has its pitfalls.
This link discussed the issue: http://forums.oracle.com/forums/thread.jspa?threadID=352627