Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have created two tables A and B with Table A as parent table and Table B as child table having foreign key constraint with Table A.
Following are the contents of Table A
CUS_ID NAME
1 MICHAEL
2 SANDRO
3 ROBERT
Following are the contents of Table B
CUS_ID ORDER
2 PIZZA
3 BURGER
I will get input data in following format to insert in to the above mentioned tables.
NAME ORDERS
SANDRO BURGER
ROBERT PIZZA
I am trying to create a pl/sql procedure to insert data into Table B after validating data in parent table Table A.
scenario 1: If data is available in parent table Table A and then insert data only in Table B.
scenario 2: If data is not available in Parent Table Table A, then insert NAME in to Table A and then insert ORDER data into Table B.
For scenario 2, i am able to achieve it using the following pl/sql code
INSERT INTO TABLE_A (cus_id, name)
VALUES (cus_seq.NEXTVAL, NAME)
RETURNING cus_id INTO l_cus_id;
INSERT INTO TABLE_B (cus_id, order)
VALUES (order_seq.NEXTVAL, l_cus_id, ORDER);
I need help in achieving scenario 1. Even I will look forward to other suggestion in achieving both the scenarios in optimum way.
You need to encapsulate the lookup of table_a in a sub-routine.
create or replace procedure place_order
( p_name table_a.name%type
, p_order table_b.order&type )
is
l_cus_id table_a.cus_id%type;
function get_cus_id
( p_name table_a.name%type )
return table_a.cus_id%type
is
return_value table_a.cus_id%type;
begin
begin
select cus_id into return_value
from table_a
where name = p_name;
exception
when no_data_found then
insert into table_a (cus_id, name)
values (cus_seq.nextval, p+name)
returning cus_id into return_value;
end;
return return_value;
end get_cus_id;
begin
l_cus_id := get_cus_id(p_name);
insert into table_b (cus_id, order)
values (l_cus_id, order);
end place_order;
Try this approach
BEGIN
SELECT cus_id INTO l_cus_id
FROM table_a
WHERE table_a.name = l_cust_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO TABLE_A (cus_id, name)
VALUES (cus_seq.NEXTVAL, l_cust_name)
RETURNING cus_id INTO l_cus_id;
END;
INSERT INTO table_b (ord_id, cus_id, "ORDER")
VALUES (order_seq.NEXTVAL, l_cus_id, l_order);
Can try this
CREATE or REPLACE procedure place_order
( p_name table_a.name%type
, p_order table_b.order&type )
is
v_cnt number;
BEGIN
SELECT count(name) into v_cnt from a where name=p_name;
INSERT into table_b values(p_name,p_order);
EXCEPTION
when NO_DATA_FOUND
insert into table_a values (cus_seq.nextval,p_name);
insert into table_b values(p_name,p_order);
END;
Related
I need some help with PL SQL. I have to insert some data into table. Another application is calling my procedure and caller is passing few details which I also need to insert into my table.
here is the syntax I am struggling with:
PROCEDURE invform_last2orders_item_insert( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2) IS
Begin
insert into mytable
(p_userId , p_accountId , p_site_Id , sku, description, 'Cart', 1, unitId)
as
select sku, description, unitId
from mycatalogtable where site_id= p_site_Id ) ;
End;
Can you help me with syntax? I need to pass three parameters from called in parameter and some values returned from select query. How can I achieve this?
thank you for your help.
That would be something like this; see comments within code:
PROCEDURE invform_last2orders_item_insert
( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2)
IS
Begin
insert into mytable
-- first name all columns you'll be inserting into; I don't know their
-- names so I just guessed
(userid,
accountid,
siteid,
sku,
description,
col1,
col2,
unitid
)
-- if you were to insert only values you got via parameters, you'd use the
-- VALUE keyword and insert those values separately.
-- As some of them belong to a table, use SELECT statement
(select p_userid,
p_accountid,
p_siteid,
c.sku,
c.description,
'Cart',
1,
c.unitid
from mycatalogtable c
where c.site_id = p_site_Id
);
-- I don't know what you are supposed to return; this is just an example
p_return_message := sql%rowcount || ' row(s) inserted';
End;
in your select statement you should have the same number of columns as you are inserting into the table, your code should be something like this example,
DECLARE
userid varchar2(20) := 'Jack';
Begin
INSERT INTO mytable (SELECT userid, SPORT from OLYM.OLYM_SPORTS);
commit;
end;
I'm trying to create a simple procedure to copy some records from Table1 to Table2.
Table1:
id number PK
operation varchar2(50)
position varchar2(50)
code_operation varchar2(50) FK
Table2:
code_operation varchar2(50) PK
operation varchar2(50)
position varchar2(50)
client_number varchar2(50)
Starting from the client_number I have to copy the associated operation and position from Table1 and insert into operation and position of Table2.
I've tried this code but it doesn't work:
CREATE OR REPLACE PROCEDURE COPY_DATA(
BEGIN
DECLARE P_CLIENT_NUMBER VARCHAR2(50);
DECLARE P_CODE_OPERATION VARCHAR2(50);
DECLARE P_DIVISION VARCHAR2(50);
DECLARE P_POSITION VARCHAR2(50)
SELECT CODE_OPERATION INTO P_CODE_OPERATION FROM TABLE2;
SELECT CLIENT_NUMBER INTO P_CLIENT_NUMBER FROM TABLE2;
SELECT DIVISION INTO P_DIVISION FROM TABLE1;
SELECT POSITION INTO P_POSITION FROM TABLE1;
INSERT INTO TABLE2(DIVISION,POSITION)
WHERE CODE_OPERATION=P_COD_OPERATION;
END
);
I've got this error ERROR PLS-00103: Encountered the symbol “DECLARE” but I don't understand why, plus with this error I don't know if my code is correct or not.
Error you got is easy to fix. PL/SQL block has declare-begin-exception-end structure, which means that you first declare variables (in a stored procedure, you don't use declare keyword; strange enough, for triggers - which as kind of stored as well - you do use it). For example:
create or replace procedure copy_data as
p_client_number table2.client_number%type;
begin
select client_number
into p_client_number
from table2;
exception
when too_many_rows then
raise;
end;
However, without WHERE clause (or an aggregate, such as MIN or MAX), this will raise too_many_rows exception because it'll try to fetch all client numbers into a scalar variable, and that won't work. I included the way which shows how to handle it. What will you really do? Restrict number of rows to 1. Similarly, you'd handle no_data_found or other exceptions.
What you described looks strange to me.
you want to insert into table2 - which is a master table
values you'd insert are in table1 - which is a detail table
Looking at tables, I'd say that it should be vice versa.
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = 'ABC';
(I presumed that primary key is populated via a sequence.)
It also means that none of variables you declared is necessary. But, the procedure would accept client number as a parameter.
The whole procedure would then be:
create or replace procedure copy_data
(par_client_number in table2.client_number%type)
is
begin
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = par_client_number;
end;
You'd call it as
begin
copy_data('ABC');
end;
/
So I usually get the Primary Key of a newly inserted record as the following while using a trigger.
insert into table1 (pk1, notes) values (null, "Tester") returning pk1
into v_item;
I am trying to use the same concept but with an insert using a select statement. So for example:
insert into table1 (pk1, notes) select null, description from table2 where pk2 = 2 returning pk1
into v_item;
Note:
1. There is a trigger on table1 which automatically creates a pk1 on insert.
2. I need to use a select insert because of the size of the table that is being inserted into.
3. The insert is basically a copy of the record, so there is only 1 record being inserted at a time.
Let me know if I can provide more information.
I don't believe you can do this with insert/select directly. However, you can do it with PL/SQL and FORALL. Given the constraint about the table size, you'll have to balance memory usage with performance using l_limit. Here's an example...
Given this table with 100 rows:
create table t (
c number generated by default as identity,
c2 number
);
insert into t (c2)
select rownum
from dual
connect by rownum <= 100;
You can do this:
declare
cursor t_cur
is
select c2
from t;
type t_ntt is table of number;
l_c2_vals_in t_ntt;
l_c_vals_out t_ntt;
l_limit number := 10;
begin
open t_cur;
loop
fetch t_cur bulk collect into l_c2_vals_in limit l_limit;
forall i in indices of l_c2_vals_in
insert into t (c2) values (l_c2_vals_in(i))
returning c bulk collect into l_c_vals_out;
-- You have access to the new ids here
dbms_output.put_line(l_c_vals_out.count);
exit when l_c2_vals_in.count < l_limit;
end loop;
close t_cur;
end;
You can't use that mechanism; as shown in the documentation railroad diagram:
the returning clause is only allowed with the values version, not with the subquery version.
I'm interpreting your second restriction (about 'table size') as being about the number of columns you would have to handle, possibly as individual variables, rather than about the number of rows - I don't see how that would be relevant here. There are ways to avoid having lots of per-column local variables though; you could select into a row-type variable first:
declare
v_item number;
v_row table1%rowtype;
begin
...
select null, description
into v_row
from table2 where pk2 = 2;
insert into table1 values v_row returning pk1 into v_item;
dbms_output.put_line(v_item);
...
or with a loop, which might make things look more complicated than necessary if you really only ever have a single row:
declare
v_item number;
begin
...
for r in (
select description
from table2 where pk2 = 2
)
loop
insert into table1 (notes) values (r.description) returning pk1 into v_item;
dbms_output.put_line(v_item);
...
end loop;
...
or with a collection... as #Dan has posted while I was answering this so I won't repeat! - though again that might be overkill or overly complicated for a single row.
Currently trying to create a PL/SQL procedure. I am a complete noob at PL/SQL, as you can tell!
We have had to create a table using SQL, and we are looking to automatically update the table with a procedure. If the customer has requested more than 4 jobs, we are looking to input their details into this table as a frequent customer.
I currently have at the moment:
CREATE TABLE PublisherDetails
(PublisherName VARCHAR2 (40),
City VARCHAR2 (20) ,
PhoneNo NUMBER (11),
jobNo NUMBER (10),
startDate DATE,
completionDate DATE)
;
SELECT Publisher.Name AS PublisherName,
Publisher.City, Publisher.PhoneNo,
COUNT (*) AS PublisherJobCount
FROM Publisher
INNER JOIN PrintJob
ON Publisher.Name = PrintJob.PublisherName
GROUP BY Publisher.Name, Publisher.City, Publisher.PhoneNo;
Create or replace procedure Task3
IS CountPublisherJobs NUMBER;
DECLARE No_data_Found EXCEPTION
BEGIN
SELECT count(*) INTO CountPublisherJobs
OPEN Task3;
LOOP
IF PublisherJobCount < 3
THEN INSERT INTO PublisherDetails (PublisherName, City, PhoneNo)
FROM Publisher
WHERE PublisherName = publisher.name
Else
Insert Into PublisherDetails (JobNo, StartDate, CompletionDate )
SELECT jobNo, startDate, completionDate
FROM PrintJob
Where PublisherName = publishers.name
FETCH Task3 INTO PublisherDetails, publishername, city, phoneNo;
EXIT WHEN c1%NOTFOUND;
INSERT INTO temp VALUES (PublisherName, City, PhoneNo, JobNo, StartDate, CompletionDate);
END IF;
COMMIT;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Sorry no data found');
END;
/
Its churning up errors and I am not sure why. Any help as always is appreciated.
There are a number of things incorrect with the syntax of your procedure.
The basic format for a stored procedure is
Create {Or Replace} Procedure PROCEDURE_NAME {(i_param IN datatype)}
Is
<<Declaration Section>>
Begin
<<code section>>
Exception
<<Exceptions>>
End PROCEDURE_NAME;
From what you have described above, you want to insert a record into a table, when a condition is met in another table.
To accomplish this, I would need to see the underlying data structure, what you have provided doesn't show the tables the data is currently in (is there a JOB table for instance? a Customer table?).
The NO_DATA_FOUND exception does not need to be declared, it is an Oracle exception
Your Select Count(*) Into CountJobs is missing a From TABLE, and any predicates you want to add, although I am not sure what you are trying to accomplish with this.
You are attempting to open a cursor on the procedure name. You have not defined a cursor with the name Task3
You have not declared the CountPublisherJobs variable
I would suggest perhaps revisiting the basic structure for a stored procedure.
Edit
Based on your response, you could achieve the result using the following:
Create Or Replace Procedure addFrequentPublisher
Is
Cursor frequentPublishers Is
Select PUBLISHER_ID
From JOB
Group By
PUBLISHER_ID
Having Count(*) >= 4;
Begin
For i In frequentPublishers
Loop
Insert Into FREQUENT_CUSTOMER ...
End Loop;
End;
create or replace
trigger addpagamento
after insert on marcacoes_refeicoes
for each row
declare
nmarcacaoa number;
ncartaoa number;
begin
select nmarcacao into nmarcacaoa from marcacoes_refeicoes where rownum < (select count(*) from marcacoes_refeicoes);
select ncartao into ncartaoa from marcacoes_refeicoes where rownum < (select count(*) from marcacoes_refeicoes);
insert_pagamentos(nmarcacaoa, ncartaoa); --this is a procedure
exception when others then
raise_application_error(-20001, 'Error in Trigger!!!');
end addpagamento;
when i try to run the insert statement to the table "marcacoes_refeicoes" this procedure gives error: like the table is mutating
create or replace
procedure insert_pagamentos
(nmarcacaoa in number, ncartaoa in number)
AS
BEGIN
insert into pagamentos (nmarcacao, datapagamento, ncartao) values (nmarcacaoa, sysdate, ncartaoa);
commit;
END INSERT_PAGAMENTOS;
Short (oversimplified) answer:
You can't modify a table in a trigger that changes the table.
Long answer:
http://www.oracle-base.com/articles/9i/mutating-table-exceptions.php has a more in-depth explanation, including suggestions how to work around the problem.
You're hitting the mutating-table problem because you're selecting from the same table the trigger is on, but what you seem to be trying to do doesn't make sense. Your queries to get a value for nmarcacaoa and ncartaoa will return a no_data_found or too_many_rows error unless the table had exactly 2 rows in it before your insert:
select nmarcacao from marcacoes_refeicoes
where rownum < (select count(*) from marcacoes_refeicoes);
will return all rows except one; and the one that's excluded will be kind of random as you have no ordering. Though the state is undetermined within the trigger, so you can't really how many rows there are, and it won't let you do this query anyway. You won't normally be able to get a single value, anyway, and it's not obvious which value you actually want.
I can only imagine that you're trying to use the values from the row you are currently inserting, and putting them in your separate payments (pagamentos) table. If so there is a built-in mechanism to do that using correlation names, which lets you refer to the newly inserted row as :new (by default):
create or replace
trigger addpagamento
after insert on marcacoes_refeicoes
for each row
begin
insert_pagamentos(:new.nmarcacaoa, :new.ncartaoa);
end addpagamento;
The :new is referring to the current row, so :new.nmarcacaoa is the nmarcacaoa being inserted. You don't need to (and can't) get that value from the table itself. (Even with the suggested autonomous pragma, that would be a separate transaction and would not be able to see your newly inserted and uncommitted data).
Or you can just do the insert directly; not sure what the procedure is adding here really:
create or replace
trigger addpagamento
after insert on marcacoes_refeicoes
for each row
begin
insert into pagamentos(nmarcacao, datapagamento, ncartao)
values (:new.nmarcacaoa, sysdate, :new.ncartaoa);
end addpagamento;
I've removed the exception handler as all it was doing was masking the real error, which is rather unhelpful.
Editing this answer in view of the comments below:
You can use PRAGMA AUTONOMOUS_TRANSACTION to get rid of the error, but DONOT USE it as it will NOT solve any purpose.
Use PRAGMA AUTONOMOUS_TRANSACTION:
create or replace
trigger addpagamento
after insert on marcacoes_refeicoes
for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
nmarcacaoa number;
ncartaoa number;
begin
select nmarcacao into nmarcacaoa from marcacoes_refeicoes where rownum < (select count(*) from marcacoes_refeicoes);
select ncartao into ncartaoa from marcacoes_refeicoes where rownum < (select count(*) from marcacoes_refeicoes);
insert_pagamentos(nmarcacaoa, ncartaoa); --this is a procedure
exception when others then
raise_application_error(-20001, 'Error in Trigger!!!');
end addpagamento;
However in this case , select count(*) from marcacoes_refeicoes will give you the new count after the current insertion into the table.