need to get the output of 2 cursors in one temp table - sql

Here is my first procedure (sample)
CREATE OR REPLACE PROCEDURE GPTOWNER_CORP_AMF.testt1
AS
po_status VARCHAR2(100);
po_cur_1 SYS_REFCURSOR;
po_cur_2 SYS_REFCURSOR;
BEGIN
OPEN po_cur_1 FOR
select app_var_row_seq,app_var_name,app_var_value,app_var_description,r_date
from TMP_PMT_APP_VARIABLES_REF
where ROWNUM < 5;
OPEN po_cur_2 FOR
select config_to_lob_row_seq,config_row_seq,lobref_row_seq,r_date
from TMP_PMT_CONFIG_TO_LOB_DAT
where ROWNUM < 6;
TESTT2(po_cur_1,po_cur_2,po_status);
DBMS_output.put_line(po_status);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM||SQLCODE);
END;
Here is my second procedure (sample)
CREATE OR REPLACE procedure GPTOWNER_CORP_AMF.testt2 (pi_cur_1 IN sys_refcursor, pi_cur_2 IN sys_refcursor,po_status OUT VARCHAR2)
AS
app_var_row_seq NUMBER;
app_var_name VARCHAR2(100);
app_var_value VARCHAR2(1000);
app_var_description VARCHAR2(1000);
r_date1 DATE;
config_to_lob_row_seq NUMBER;
config_row_seq VARCHAR2(100);
lobref_row_seq NUMBER;
r_date2 DATE;
BEGIN
LOOP
FETCH pi_cur_1 into app_var_row_seq,app_var_name,app_var_value,app_var_description,r_date1;
FETCH pi_cur_2 into config_to_lob_row_seq,config_row_seq,lobref_row_seq,r_date2;
EXIT WHEN (pi_cur_2%NOTFOUND AND pi_cur_1%NOTFOUND ) ;
INSERT INTO testt1testt2 (colid,col1,col2,col3,col4,col5,col6,col7,col8,col9)
VALUES(colid.nextval,app_var_row_seq,app_var_name,app_var_value,app_var_description,r_date1,config_to_lob_row_seq,config_row_seq,lobref_row_seq,r_date2);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('rows inserted:' || pi_cur_1%ROWCOUNT || 'and' || pi_cur_2%ROWCOUNT);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM||SQLCODE);
END;
My problem statement is that from first procedure I am getting two refcursor as output and in the second procedure I am trying to read them and put them into a temp table which will be used by another procedure. Cant union the two select statements as they are having different set of output. Is there any better mechanism to do so , as by my approach I am facing issue as when I run the first procedure (say first select return 4 row and second select return 6 rows) the need is that 6 rows would be inserted into temp table but the columns that are read from first select will be inserted as NULL when there is now row fetched , but in my case duplicate row is getting inserted. Any help would be appreciated. And do post if anyone needs more info on the same.

If I understand you right, then you don't really need to union them - but join them.
Since there is no really relation between the 2 tables and you want nulls in "both side"s you need to full outer join them.
I will not ask you, why you want them both on the same temp table if there is no relation between them. But if you do this why not just use an insert-select ?
INSERT INTO testt1testt2 (colid,col1,col2,col3,col4,col5,col6,col7,col8,col9)
SELECT colid.nextval, app_var_row_seq,app_var_name,app_var_value,app_var_description, t1.r_date,
config_to_lob_row_seq,config_row_seq,lobref_row_seq, t2.r_date
FROM (select app_var_row_seq,app_var_name,app_var_value,app_var_description,r_date
from TMP_PMT_APP_VARIABLES_REF
where ROWNUM < 5) t1
FULL OUTER JOIN (select config_to_lob_row_seq,config_row_seq,lobref_row_seq,r_date
from TMP_PMT_CONFIG_TO_LOB_DAT
where ROWNUM < 6) t2 on 1=2
UPDATE:
If the requirement is to get 2 refcursors, then my approach isn't relevant...
What you can do though, is have 2 insert commands one like this:
INSERT INTO testt1testt2 (colid,col1,col2,col3,col4,col5,col6,col7,col8,col9)
VALUES (colid.nextval,app_var_row_seq,app_var_name,app_var_value,app_var_descript‌​ion,r_date1,null,null,null,null);
and the other like:
INSERT INTO testt1testt2 (colid,col1,col2,col3,col4,col5,col6,col7,col8,col9)
VALUES (colid.nextval,null,null,null,null,null,config_to_lob_row_seq,config_row_s‌​eq,lobref_row_seq,r_date2);
If you really want to do it nicely, you can use bulk insert for performance, see example here

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 write a procedure to display the contents of a table in sql

I have a created a procedure as
create or replace procedure availability(num in number) as
begin
delete from vehicle_count;
insert into vehicle_count from select engine_no,count(engine_no)
from vehicle
where engine_no = num
group by engine_no;
end;
/
The procedure was created successfully but now i have to write a separate query to view the contents of vehicle_count as
select * from vehicle_count;
I tried inserting the select statement into the procedure after insertion but it showed a error stating "an INTO clause is expected in the select statement".
How can I create procedure to select the required contents and display it in a single execute statement?
Table schema
vehicle(vehicle_no,engine_no,offence_count,license_status,owner_id);
vehicle_count(engine_no,engine_count);
Check this (MS SQL SERVER)-
create or alter procedure availability(#num as int) as
begin
delete from vehicle_count;
insert into vehicle_count
output inserted.engine_no,inserted.count_engine_no
select engine_no,count(engine_no) as count_engine_no
from vehicle
where engine_no=#num
group by engine_no;
end;
If you want to use a SELECT into a PL/SQL block you should use either a SELECT INTO or a loop (if you want to print more rows).
You could use something like this:
BEGIN
SELECT engine_no, engine_count
INTO v_engine, v_count
FROM vehicle_count
WHERE engine_no = num;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_engine := NULL;
v_count := NULL;
END;
v_engine and v_count are two variables. You can declare them in your procedure, and they will contain the values you want to print.
You said that the procedure you wrote (actually, you posted here) compiled successfully. Well, sorry to inform you - that's not true. This is not a valid syntax:
insert into vehicle_count from select engine_no,count(engine_no)
----
from? Here?
Consider posting true information.
As of your question (if we suppose that that INSERT actually inserted something into a table):
at the beginning, you delete everything from the table
as SELECT counts number of rows that share the same ENGINE_NO (which is equal to the parameter NUM value), INSERT inserts none (if there's no such NUM value in the table) or maximum 1 row (because of aggregation)
therefore, if you want to display what's in the table, all you need is a single SELECT ... INTO statement whose result is displayed with a simple DBMS_OUTPUT.PUT_LINE which will be OK if you're doing it interactively (in SQL*Plus, SQL Developer, TOAD and smilar tools). Regarding table description, I'd say that ENGINE_NO should be a primary key (i.e. that not more than a single row with that ENGINE_NO value can exist in a table).
create or replace procedure availability (num in number) as
l_engine_no vehicle_count.engine_no%type;
l_engine_count vehicle_count.engine_count%type;
begin
delete from vehicle_count;
insert into vehicle_count (engine_no, engine_count)
select engine_no, count(engine_no)
from vehicle
where engine_no = num
group by engine_no;
-- This query shouldn't return TOO-MANY-ROWS if ENGINE_NO is a primary key.
-- However, it might return NO-DATA-FOUND if there's no such NUM there, so you should handle it
select engine_no, engine_count
into l_engine_no, l_engine_count
from vehicle_count
where engine_no = num;
dbms_output.put_line(l_engine_no ||': '|| l_engine_count);
exception
when no_data_found then
dbms_output.put_line('Nothing found for ENGINE_NO = ' || num);
end;
/
There are numerous alternatives to that (people who posted their answers/comments before this one mentioned some of those), and the final result you'd be satisfied with depends on where you want to display that information.

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.

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;

Insert values from select query into variables

I'm trying to insert into 2 variables values from select statement but I got an error which says:
exact fetch returns more than requested number of rows
I'm guessing that I need to put that data from select query into some table type?
But I need to process single record for the purpose of comparing to variable v_zmienna with v_zmienna2.
As you can see I try to somehow transfer data into v_zmienna and v_zmienna2 and after that compare that values.
Could anyone take a look and propose what's the best approach?
I need to use execute immediate and variables because I have Table1, Table2 tables as configure tables with names of tables which should be connected etc.
declare
c1 SYS_REFCURSOR;
v_sourcepkcolumn varchar2(50);
v_sourcetable varchar2(50);
v_targetfkcolumn varchar2(50);
v_targetpkcolumn varchar2(50);
v_targettable varchar2(50);
v_sourcecolumn varchar2(50);
v_targetcolumn varchar2(50);
v_verificationdefid varchar2(50);
v_zmienna varchar2(50);
v_zmienna2 varchar2(50);
v_random_number number;
begin
v_random_number := sequence1.nextval;
open c1 for select sourcepkcolumn,
sourcetable,
targetfkcolumn,
targetpkcolumn,
targettable,
sourcecolumn,
targetcolumn,
ID
from table1,table2 where table1.ID=table2.ID;
loop
fetch c1 into v_sourcepkcolumn,
v_sourcetable,
v_targetfkcolumn,
v_targetpkcolumn,
v_targettable,
v_sourcecolumn,
v_targetcolumn,
v_verificationdefid;
exit when c1%notfound;
execute immediate 'select '||v_sourcetable||'.'||v_sourcecolumn||','||v_targettable||'.'||v_targetcolumn||' from '||v_sourcetable||','||v_targettable||' where '||
v_sourcetable||'.'||v_sourcepkcolumn||'='||v_targettable||'.'||v_targetfkcolumn into v_zmienna,v_zmienna2;
if (v_zmienna=v_zmienna2) then
execute immediate 'insert into table3 values ('||v_random_number||','||v_sourcecolumn||','||v_sourcepkcolumn||','||v_zmienna||',1,'||v_targetcolumn||','||v_targetpkcolumn||','||v_zmienna2||',timestamp,'||v_verificationdefid||')';
else
execute immediate 'insert into table3 values ('||v_random_number||','||v_sourcepkcolumn||','||v_zmienna||',0,'||v_targetcolumn||','||v_targetpkcolumn||','||v_zmienna2||',timestamp,'||v_verificationdefid||')';
end if;
end loop;
close c1;
end;
EDIT:
Output of
select sourcepkcolumn,
sourcetable,
targetfkcolumn,
targetpkcolumn,
targettable,
sourcecolumn,
targetcolumn,
from table1,table2 where table1.id=table2.id
Could looks:
PK_ACCOUNT,ACCOUNT,PK_ACC,ID,ACCOUNT2,NAME,NAME
That's the output from table1 and table2,
IMPORTANT: ACCOUNT AND ACCOUNT2 ARE VIEWS! that's no TABLES.
So now I wan't to compare account.name and account2.name from account VIEW with account2 VIEW on account.pk_account=account2.pk_acc.
Generally it works perfectly but sometimes (depends on oracle order) records in VIEW after join can be in other order.
For example:
Account:
PK_ACCOUNT NAME
1 Eva
1 Adam
and
Account2
PK_ACCOUNT NAME
1 Adam
1 Eva
Why in VIEW different PK_ACCOUNT? Because I need to insert into Account and Account2 data from multiple tables. That's why records sometimes multiplies.
When procedure process one by one every records then takes it as divergent
I think you could just rewrite this as:
declare
v_random_number number;
begin
v_random_number := sequence1.nextval;
for rec in (select sourcepkcolumn,
sourcetable,
targetfkcolumn,
targetpkcolumn,
targettable,
sourcecolumn,
targetcolumn,
ID
from table1
inner join table2 on (table1.ID = table2.ID))
loop
execute immediate 'insert into table3 '||chr(10)|| -- where is the list of columns to insert into?!
' select :v_random_number random_number,'||chr(10)||
' :v_sourcecolumn source_col,'||chr(10)||
' :v_sourcepkcolumn source_pk_col,'||chr(10)||
' src.'||v_sourcecolumn||' source_col,'||chr(10)||
' case when src.'||v_sourcecolumn||' = tgt.'||v_targetcolumn||' then 1 else 0 end one_or_zero'||chr(10)||
' :v_targetcolumn target_col'||chr(10)||
' :v_targetpkcolumn target_pk_col'||chr(10)||
' tgt.'||v_targetcolumn||' target_col,'||chr(10)||
' systimestamp tstamp,'||chr(10)||
' :v_verificationdefid'||chr(10)||
' from '||v_sourcetable||' src'||chr(10)||
' inner join '||v_targettable||' tgt on (src.'||v_sourcepkcolumn||' = tgt.'||v_targetfkcolumn||')'
using v_random_number,
rec.sourcecolumn,
rec.sourcepkcolumn,
rec.targetcolumn,
rec.targetpkcolumn,
rec.id;
end loop;
end;
/
N.B. Untested. I guessed that your 2nd insert statement was missing the sourcecolumn, so I added that in.
The error you are getting indicates that this statement is returning more than one row
execute immediate 'select '||v_sourcetable||'.'||v_sourcecolumn||','||v_targettable||'.'||v_targetcolumn||' from '||v_sourcetable||','||v_targettable||' where '||
v_sourcetable||'.'||v_sourcepkcolumn||'='||v_targettable||'.'||v_targetfkcolumn into v_zmienna,v_zmienna2;
You have more than one way to fix that, depending on your goal you may choose.
My reccomended method would be to change the approach a little bit. Instead of selecting into a set of variables, comparing them and doing different things depending on the variables, you can use a different method. First, insert into table3 all the rows where your dynamic condition is met. Then, insert into table3 all the rows where your dynamic condition is not met. This would be something like this:
execute immediate 'insert into table3 values ('||v_random_number||','||v_sourcecolumn||','||v_sourcepkcolumn||','||v_zmienna||',1,'||v_targetcolumn||','||v_targetpkcolumn||','||v_zmienna2||',timestamp,'||v_verificationdefid||') ' ||
' select '||v_sourcetable||'.'||v_sourcecolumn||','||v_targettable||'.'||v_targetcolumn||' from '||v_sourcetable||','||v_targettable||' where '||
v_sourcetable||'.'||v_sourcepkcolumn||'='||v_targettable||'.'||v_targetfkcolumn );
execute immediate 'insert into table3 values ('||v_random_number||','||v_sourcepkcolumn||','||v_zmienna||',0,'||v_targetcolumn||','||v_targetpkcolumn||','||v_zmienna2||',timestamp,'||v_verificationdefid||' ||
' select '||v_sourcetable||'.'||v_sourcecolumn||','||v_targettable||'.'||v_targetcolumn||' from '||v_sourcetable||','||v_targettable||' where '||
v_sourcetable||'.'||v_sourcepkcolumn||'<>'||v_targettable||'.'||v_targetfkcolumn );
Your second option would be to do it basically the same way as before, but use cursors on the dynamic part as well as on the fixed part. I'm not going to try to create the source code for that, but it would mean you need to create a cursor for the dynamic select query and do the "if" part inside the cursor loop