I'm trying to write a procedure that inserts paid invoices from the paid_invoices table into the invoice_archive table. Only those paid invoices that are older than or equal to 31-05-2014 should be transferred.
Here's my procedure:
SQL> create or replace procedure paid_invoice_transfer as
cursor paid is
select *
from paid_invoices
where invoice_total = credit_total + payment_total
and payment_date <= '2014-05-31';
invoice_archive_text paid%rowtype;
begin
for invoice_archive_text in paid loop
dbms_output.put_line(invoice_archive_text.invoice_id);
insert into invoice_archive values invoice_archive_text;
end loop;
end;
/
I'm not sure what to execute at this point:
SQL> set serveroutput on;
SQL> execute paid_invoice_transfer(???);
Now that you know how to execute a procedure without an argument, I would also like to point out the problems with your procedure.
It's not required to define a record variable for looping through the cursor.
invoice_archive_text paid%rowtype
Pl/SQL automatically creates it when you use it in for loop. I will go a step further and ask you to avoid loops to run INSERT. Just use plain INSERT INTO target_table select * from source_table and explicitly specify column names to be safe.
If you want to display the ids that are being inserted through DBMS_OUTPUT.PUT_LINE, store it in a a collection and loop it separately just for display purpose( only if you think display is needed).
I also wanted to show you how to pass a date argument for execution so I will pass payment_date. In your procedure you are wrongly comparing it with a literal string rather than a date. It is bound to fail if NLS_DATE_ parameters don't match with the string.
CREATE OR REPLACE PROCEDURE paid_invoice_transfer ( p_payment_date DATE ) AS
TYPE tab_inv IS TABLE OF paid_invoices.invoice_id%type;
t_tab tab_inv;
BEGIN
SELECT invoice_id BULK COLLECT INTO t_tab
FROM paid_invoices
WHERE invoice_total = credit_total + payment_total
AND payment_date <= p_payment_date;
FOR i IN t_tab.FIRST..t_tab.LAST LOOP
dbms_output.put_line(t_tab(i));
END LOOP;
INSERT INTO invoice_archive ( column1,column2,column3) select col1,col2,col3
FROM paid_invoices
WHERE invoice_total = credit_total + payment_total
AND payment_date <= p_payment_date;
END;
/
set serveroutput on
execute paid_invoice_transfer(DATE '2014-05-31' )
Related
Create or replace procedure PROC AS
V_TABLE_NAME VARCHAR2(255);
V_LIST SYS_REFCURSOR;
DATE_VALUE_INS VARCHAR2(10);
BEGIN
DATE_VALUE_INS:=TO_CHAR(SYSDATE,'YYMMDD');
OPEN V_LIST FOR
SELECT NAME FROM DW.table_name_list ;
LOOP
FETCH V_LIST
INTO V_TABLE_NAME;
EXIT WHEN V_LIST%NOTFOUND;
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME;
END LOOP;
CLOSE V_LIST;
end;
I have created this Proc which takes value from a table which has Table_name and create Backup using Execute Immediate.
Now the requirement has changed that i only need to create backup for partial records (i.e. where clause on each table )
I have 6 tables as such .
New Approach i am thinking is :
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME where some condition;
But the problem becomes all 6 have different column to filter on.
My Ask is How should I change my design of proc to Adjust this new Requirement.
6 tables? Why bother? Create a procedure which - depending on table name passed as a parameter - in IF-THEN-ELSE runs 6 different CREATE TABLE statements.
On the other hand, another approach would be to create backup tables in advance (at SQL level), add BACKUP_DATE column to each of them, and - in procedure - just perform INSERT operation which doesn't require dynamic SQL at all.
For example:
create table emp_backup as select * from emp where 1 = 2;
alter table emp_backup add backup_date date;
create or replace procedure p_backup (par_table_name in varchar2) is
begin
if par_table_name = 'EMP' then
insert into emp_backup (empno, ename, job, sal, backup_date)
select empno, ename, job, sal, trunc(sysdate)
from emp
where deptno = 20; --> here's your WHERE condition
elsif par_table_name = 'DEPT' then
insert into dept_backup (...)
select ..., trunc(sysdate)
from dept
where loc = 'DALLAS';
elsif ...
...
end if;
end;
/
Doing so, you'd easier access backup data as you'd query only one table, filtered by BACKUP_DATE. That's also good if you have to search for some data that changed several days ago, but you don't know exact day. What would you rather do: query 10 tables (and still not find what you're looking for), or query just one table and find that info immediately?
declare
vquery long;
cursor c1 is
select * from temp_name;
begin
for i in c1
loop
vquery :='INSERT INTO ot.temp_new(id)
select '''||i.id||''' from ot.customers';
dbms_output.put_line(i.id);
end loop;
end;
/
Output of select * from temp_name is :
ID
--------------------------------------------------------------------------------
customer_id
1 row selected.
I have customers table which has customer_id column.I want to insert all the customer_id into temp_new table but it is not being inserted. The PLSQL block executes successfully but the temp_new table is empty.
The output of dbms_output.put_line(i.id); is
customer_id
What is wrong there?
The main problem is that you generate a dynamic statement that you never execute; at some point you need to do:
execute immediate vquery;
But there are other problems. If you output the generated vquery string you'll see it contains:
INSERT INTO ot.temp_new(id)
select 'customer_id' from ot.customers
which means that for every row in customers you'll get one row in temp_new with ID set to the same fixed literal 'customer_id'. It's unlikely that's what you want; if customer_id is a column name from customers then it shouldn't be in single quotes.
As #mathguy suggested, long is not a sensible data type to use; you could use a CLOB but only really need a varchar2 here. So something more like this, where I've also switched to use an implicit cursor:
declare
l_stmt varchar2(4000);
begin
for i in (select id from temp_name)
loop
l_stmt := 'INSERT INTO temp_new(id) select '||i.id||' from customers';
dbms_output.put_line(i.id);
dbms_output.put_line(l_stmt);
execute immediate l_stmt;
end loop;
end;
/
db<>fiddle
The loop doesn't really make sense though; if your temp_name table had multiple rows with different column names, you'd try to insert the corresponding values from those columns in the customers table into multiple rows in temp_new, all in the same id column, as shown in this db<>fiddle.
I guess this is the starting point for something more complicated, but still seems a little odd.
// Below update statement is present in my Stored procedure. I am passing two parameters (parameter 1 and parameter 2) while executing the Stored procedure. Once executing the Stored procedure i want the different updated values to be displayed. please provide the code for the below example(my stored procedure)
CREATE OR REPLACE PROCEDURE UPDATE_TABLE(parameter1 IN NUMBER, parameter IN varchar2)
AS
BEGIN
UPDATE Table1 SET column_a = (parameter1 +2) WHERE id= parameter2;
update Table2 set column_b= parameter1 where id=parameter2;
END UPDATE_TABLE
Use returning clause + bulk collect. I don't know for what purpose you need this updated data to be displayed. Below approach for one of your table. You can share it to others.
DECLARE
TYPE t_type is table of VARCHAR2(250);
l_type t_type;
begin
UPDATE Table1 SET column_a = (parameter1 +2) RETURNING column_a BULK COLLECT INTO l_type;
FOR i IN 1..l_type.count
LOOP
DBMS_OUTPUT.PUT_LINE(l_type(i));
END LOOP;
END;
I have got stuck in below and getting syntax error - Please help.
Basically I am using a collection to store few department ids and then would like to use these department ids as a filter condition while inserting data into emp table in FORALL statement.
Below is sample code:
while compiling this code i am getting error, my requirement is to use INSERT INTO table select * from table and cannot avoid it so please suggest.
create or replace Procedure abc(dblink VARCHAR2)
CURSOR dept_id is select dept_ids from dept;
TYPE nt_dept_detail IS TABLE OF VARCHAR2(25);
l_dept_array nt_dept_detail;
Begin
OPEN dept_id;
FETCH dept_id BULK COLLECT INTO l_dept_array;
IF l_dept_array.COUNT() > 0 THEN
FORALL i IN 1..l_dept_array.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO stg_emp SELECT
Dept,''DEPT_10'' FROM dept_emp'||dblink||' WHERE
dept_id = '||l_dept_array(i)||'';
COMMIT;
END IF;
CLOSE dept_id;
end abc;
Why are you bothering to use cursors, arrays etc in the first place? Why can't you just do a simple insert as select?
Problems with your procedure as listed above:
You don't declare procedures like Procedure abc () - for a standalone procedure, you would do create or replace procedure abc as, or in a package: procedure abc is
You reference a variable called "dblink" that isn't declared anywhere.
You didn't put end abc; at the end of your procedure (I hope that was just a mis-c&p?)
You're effectively doing a simple insert as select, but you're way over-complicating it, plus you're making your code less performant.
You've not listed the column names that you're trying to insert into; if stg_emp has more than two columns or ends up having columns added, your code is going to fail.
Assuming your dblink name isn't known until runtime, then here's something that would do what you're after:
create Procedure abc (dblink in varchar2)
is
begin
execute immediate 'insert into stg_emp select dept, ''DEPT_10'' from dept_emp#'||dblink||
' where dept_id in (select dept_ids from dept)';
commit;
end abc;
/
If, however, you do know the dblink name, then you'd just get rid of the execute immediate and do:
create Procedure abc (dblink in varchar2)
is
begin
insert into stg_emp -- best to list the column names you're inserting into here
select dept, 'DEPT_10'
from dept_emp#dblink
where dept_id in (select dept_ids from dept);
commit;
end abc;
/
There appears te be a lot wrong with this code.
1) why the execute immediate? Is there any explicit requirement for that? No, than don't use it
2) where is the dblink variable declared?
3) as Boneist already stated, why not a simple subselect in the insert statement?
INSERT INTO stg_emp SELECT
Dept,'DEPT_10' FROM dept_emp#dblink WHERE
dept_id in (select dept_ids from dept );
For one, it would make the code actually readable ;)
I have to generate a table (contains two columns) of random data from a database table through oracle procedure. The user can indicate the number of data required and we have to use the table data with ID values from 1001 to 1060. I am trying to use cursor loop and not sure dbms_random method dhould I use.
I am using the following code to create procedure
create or replace procedure a05_random_plant(p_count in number)
as
v_count number := p_count;
cursor c is
select plant_id, common_name
from ppl_plants
where rownum = v_count
order by dbms_random.value;
begin
delete from a05_random_plants_table;
for c_table in c
loop
insert into a05_random_plants_table(plant_id, plant_name)
values (c_table.plant_id, c_table.common_name);
end loop;
end;
/
it complied successfully. Then I executed with the following code
set serveroutput on
exec a05_random_plant(5);
it shows anonymous block completed
but when run the following code, I do not get any records
select * from a05_random_plants_table;
The rownum=value would not work for a value greater than 1
hence try the below
create or replace procedure a05_random_plant(p_count in number)
as
v_count number := p_count;
cursor c is
select plant_id, common_name
from ppl_plants
where rownum <= v_count
order by dbms_random.value;
begin
delete from a05_random_plants_table;
for c_table in c
loop
insert into a05_random_plants_table(plant_id, plant_name)
values (c_table.plant_id, c_table.common_name);
end loop;
end;
/
Query by Tom Kyte - will generate almost 75K of rows:
select trunc(sysdate,'year')+mod(rownum,365) TRANS_DATE,
mod(rownum,100) CUST_ID,
abs(dbms_random.random)/100 SALES_AMOUNT
from all_objects
/
You can use this example to write your query and add where clause to it - where id between 1001 and 1060, for example.
I don't think you should use a cursor (which is slow naturally) but do a direct insert from a select:
insert into table (col1, col2)
select colx, coly from other_table...
And, isn't missing a COMMIT on the end of your procedure?
So, all code in your procedure would be a DELETE, a INSERT WITH that SELECT and then a COMMIT.