I want to fill the value of product_id. If article_code is not in the table, it executes the insert, but if record exists I don't know how to select the id of that record and assign to product_id.
The table "core_product" looks like that:
id
article_code
Here the code (inside of a function):
DECLARE
product_id int;
BEGIN
INSERT INTO core_product(article_code)
SELECT NEW.article_code
WHERE NOT EXISTS (
SELECT id INTO product_id
FROM core_product
WHERE article_code = NEW.article_code
)
RETURNING id INTO product_id;
END
Use a special variable FOUND:
DECLARE
product_id int;
BEGIN
SELECT id INTO product_id
FROM core_product
WHERE article_code = NEW.article_code;
IF NOT FOUND THEN
INSERT INTO core_product(article_code)
SELECT NEW.article_code
RETURNING id INTO product_id;
END IF;
END
If there is an unique constraint on article_code, you can harden the function against a race condition using retry loop (as Craig suggested in a comment):
BEGIN
LOOP
SELECT id INTO product_id
FROM core_product
WHERE article_code = NEW.article_code;
IF FOUND THEN
EXIT; -- exit loop
END IF;
BEGIN
INSERT INTO core_product(article_code)
SELECT NEW.article_code
RETURNING id INTO product_id;
EXIT; -- exit loop
EXCEPTION WHEN unique_violation THEN
-- do nothing, go to the beginning of the loop
-- and check once more if article_code exists
END;
END LOOP;
-- do something with product_id
END;
Related
Can I update the result of query easily?
Assume I have big query which returns salary column and I need update salaries based on this query results.
ID- is primary key for my table
Now I,m doing it like this:
STEP 1
select id from mytable ...... where something
STEP 2
update mytable set salary=1000 where id in (select id from mytable ...... where something)
Is there exists alternative to do that easily?
Try for update and current of. You said that you are looking for something like "updating data on grid"
create table my_table( id number, a varchar2(10), b varchar2(10));
insert into my_table select level, 'a', 'b' from dual connect by level <=10;
select * from my_table;
declare
rec my_table%rowtype;
cursor c_cursor is select * from my_table for update;
begin
open c_cursor;
loop
fetch c_cursor into rec;
exit when c_cursor%notfound;
if rec.id in (1,3,5) then
rec.a := rec.a||'x';
rec.b := rec.b||'+';
update my_table set row = rec where current of c_cursor;
else
delete from my_table where current of c_cursor;
end if;
end loop;
commit;
end;
select * from my_table;
Yes , you can directly update the result easily.
Here is example :
update
(
select salary from mytable ...... where something
) set salary=1000
I am trying to write trigger which will control if a record is already in table or not. If is the record already in table (compare for example by name), so current record set valid='False' and insert new. Is there any way?
This is my idea, but it doesn't work.
create or replace TRIGGER
Check_r
before insert on t$customer
FOR each ROW
declare
v_dup number;
v_com number;
v_id number;
v_id_new number;
begin
v_date:=SYSDATE;
select count(id) INTO v_dup from t$customer where surname=:NEW.surname ;
select count(id) INTO v_com from t$customer where firstname =:NEW.firstname and
address=:NEW.address;
select id into v_id from t$customer where surname=:NEW.surname;
if v_dup > 0 and v_com=0 then
v_id_new:= m$_GET_ID; -- get id
update t$customer set valid = 'False' where id = v_id;
insert into t$customer ( id, surname ,firstname, valid, address ) values (v_id_new,:NEW.surname ,:NEW.firstname, :NEW.valid, :NEW.address);
end if;
if v_dup = 0 then
v_id_new:= m$_GET_ID; -- get id
insert into t$customer ( id, surname ,firstname, valid , address) values (v_id_new,:NEW.surname ,:NEW.firstname, :NEW.valid, :NEW.address);
end if;
end;
You can use a compound trigger, for example:
CREATE OR REPLACE TRIGGER Check_r
FOR INSERT ON t$customer
COMPOUND TRIGGER
TYPE customerRecordType IS RECORD(
surname t$customer.surname%TYPE,
firstname t$customer.firstname%TYPE,
address t$customer.address%TYPE,
ID nubmer);
TYPE customerTableType IS TABLE OF customerRecordType;
customerTable customerTableType := customerTableType();
n NUMBER;
BEFORE STATEMENT IS
BEGIN
customerTable.DELETE; -- not requried, just for better understanding
END STATEMENT;
BEFORE EACH ROW IS
BEGIN
customerTable.EXTEND;
customerTable(customerTable.LAST).surname := :NEW.surname;
customerTable(customerTable.LAST).firstname := :NEW.firstname;
customerTable(customerTable.LAST).address := :NEW.address;
customerTable(customerTable.LAST).ID := m$_GET_ID;
:NEW.ID := customerTable(customerTable.LAST).ID;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN customerTable.FIRST..customerTable.LAST LOOP
SELECT COUNT(*) INTO n
FROM t$customer
WHERE surname = customerTable(i).surname;
IF n > 1 THEN
UPDATE t$customer
SET valid = 'False'
WHERE surname = customerTable(i).surname;
END IF;
SELECT COUNT(*) INTO n
FROM t$customer
WHERE firstname = customerTable(i).firstname
AND address = customerTable(i).address;
IF n > 1 THEN
UPDATE t$customer
SET valid = 'False'
WHERE firstname = customerTable(i).firstname
AND address = customerTable(i).address
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
Please note, this solution is ugly and poor in terms of performance!
But it should give you an impression how it works.
In general you should put all this into a PL/SQL Procedure instead of a trigger.
First, this is trigger for insert. you don't need to write insert statement.
Second, you need to update old record. Just update it with your where clause.
CREATE OR REPLACE TRIGGER Check_r
before insert on t$customer
FOR each ROW
BEGIN
UPDATE t$customer set valid = 'False'
WHERE surname = :NEW.surname
AND firstname =:NEW.firstname;
:NEW.id := m$_GET_ID;
END;
I am running the following SP but getting the error c1.pyid is invalid identifier. I am trying to use two different query results from one cursor. If there is any other way of using IF-else clause in a cursor, i am open to that too.
CREATE OR REPLACE
PROCEDURE FIX_DOCUMENT_RECORDS ( i_flag in varchar)
AS
Op_ID VARCHAR(8);
Op_Name VARCHAR(32);
skill VARCHAR(32);
temp_count VARCHAR(8);
temp_status VARCHAR(8):='Submitted';
QRYSTR VARCHAR2(400);
TYPE REF_CUR IS REF CURSOR;
c1 REF_CUR;
BEGIN
IF (i_flag='1') THEN
QRYSTR:='SELECT *
FROM dims_doc_master
WHERE concat_prod_id IS NULL
OR documenttypeid IS NULL
AND (pystatuswork = temp_status);';
ELSE
QRYSTR:='SELECT *
FROM dims_doc_master
WHERE (documentimageid IS NULL
AND p8id IS NULL)
AND (pystatuswork = temp_status);';
END IF;
open c1 FOR QRYSTR;
LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE('loop begin');
UPDATE DIMS_DOC_MASTER
SET pystatuswork ='Cancelled',
documentstatus ='Cancelled',
cancellationdate='31-JAN-14',
cancelledbysid = c1.pxcreateoperator,
cancelreason ='Cancelled due to corruption.'
WHERE pyid =c1.pyid;
DBMS_OUTPUT.PUT_LINE('After updation'||c1.pyid );
--Begin PC_DOCUMENT UPDATION
UPDATE PC_DOCUMENT
SET pystatuswork ='Cancelled',
cancellationdate='31-JAN-14'
WHERE pyid =c1.pyid;
--Begin insert into History
--Select Operator name and ID
SELECT skill
INTO skill
FROM operator_map_skill
WHERE pyuseridentifier=c1.pxcreateoperator
AND rownum =1;
INSERT
INTO DIMS_DOC_HIST
(
DIMS_DOC_ID,
DOC_CHG_USR,
DOC_CHG_DT,
DOC_NEW_STS,
DOC_CHG_CMNT,
CRE_TS,
ROLE,
RSN_DESC,
TARGETROLE,
DOC_CHG_USR_ID,
DOC_ASG_USR_ID,
DOC_ASG_USR,
PREVSTATUS,
PREVSTATUSDT,
ASSIGNEDTODT,
TODISPLAY,
ACTIVITY_NAME
)
VALUES
(
c1.pyid,
'DIMS',
systimestamp,
'Cancelled',
'Cancelled due to corruption',
'31-JAN-14',
skill,
NULL,
skill,
c1.pxcreateoperator,
c1.pxcreateoperator,
c1.pxcreateopname,
'Submitted',
NULL,
systimestamp,
'Y',
'Updation through Script'
);
dbms_output.put_line
(
'Document ID= '||c1.pyid
)
;
SELECT COUNT(*)
INTO temp_count
FROM PC_ASSIGN_WORKBASKET
WHERE pxrefobjectinsname=c1.pyid;
IF(temp_count IS NOT NULL) THEN
DELETE FROM PC_ASSIGN_WORKBASKET WHERE pxrefobjectinsname=c1.pyid;
ELSE
DELETE FROM PC_ASSIGN_WORKLIST WHERE pxrefobjectinsname=c1.pyid;
END IF;
COMMIT;
END;
END LOOP;
CLOSE c1;
END;
You seem confusing cursor and fetched row.
In your current procedure: you open a cursor, do a loop (which looks to be endless since there is no EXIT statement), and after the loop you close the cursor (but it looks it will never happen)
To fetch results from a cursor, do the following:
CREATE OR REPLACE PROCEDURE ...
...
c1 REF_CUR;
ddm_record dims_doc_master%rowtype;
BEGIN
...
OPEN c1;
LOOP
FETCH c1 INTO ddm_record;
EXIT WHEN c1%NOTFOUND;
...
DBMS_OUTPUT.PUT_LINE('Document ID= ' || ddm_record.pyid); -- not c1.pyid
END LOOP;
CLOSE c1;
END;
/
Inspired from examples here: http://plsql-tutorial.com/plsql-explicit-cursors.htm
Try embedding the flag in your where clause:
open c1 FOR
SELECT *
FROM dims_doc_master
WHERE (i_flag='1' AND
(concat_prod_id IS NULL
OR documenttypeid IS NULL
AND (pystatuswork = temp_status))
OR (i_flag<>'1' AND
(documentimageid IS NULL
AND p8id IS NULL)
AND (pystatuswork = temp_status));
The logic can probably be simplified but logically that would work.
I have 2 tables- student and studLoad both having 2 fields studID and studName. I want to load data from student table into stuLoad table.
If the data already exists in the studLoad table, then it should be updated else it should be inserted. following is my code to do so:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
else
insert into studLoad values(v_id,v_name);
dbms_output.put_line(v_id || ' ' || v_name);
end if;
end loop;
close cur_load;
end;
It's not working. the rows in studLoad table are noT updated. How do I solve this? In SQL server we use IF EXISTS(select...from stuLoad..) to check if the record exists in the table, is there a way to do the same in Oracle? if yes then please let me know the same.
This is a highly inefficient way of doing it. You can use the merge statement and then there's no need for cursors, looping or (if you can do without) PL/SQL.
MERGE INTO studLoad l
USING ( SELECT studId, studName FROM student ) s
ON (l.studId = s.studId)
WHEN MATCHED THEN
UPDATE SET l.studName = s.studName
WHERE l.studName != s.studName
WHEN NOT MATCHED THEN
INSERT (l.studID, l.studName)
VALUES (s.studId, s.studName)
Make sure you commit, once completed, in order to be able to see this in the database.
To actually answer your question I would do it something like as follows. This has the benefit of doing most of the work in SQL and only updating based on the rowid, a unique address in the table.
It declares a type, which you place the data within in bulk, 10,000 rows at a time. Then processes these rows individually.
However, as I say this will not be as efficient as merge.
declare
cursor c_data is
select b.rowid as rid, a.studId, a.studName
from student a
left outer join studLoad b
on a.studId = b.studId
and a.studName <> b.studName
;
type t__data is table of c_data%rowtype index by binary_integer;
t_data t__data;
begin
open c_data;
loop
fetch c_data bulk collect into t_data limit 10000;
exit when t_data.count = 0;
for idx in t_data.first .. t_data.last loop
if t_data(idx).rid is null then
insert into studLoad (studId, studName)
values (t_data(idx).studId, t_data(idx).studName);
else
update studLoad
set studName = t_data(idx).studName
where rowid = t_data(idx).rid
;
end if;
end loop;
end loop;
close c_data;
end;
/
If you would like to use your procedure, consider to change some lines:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
begin
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
end if;
exception
when no_data_found then
insert into studLoad values(v_id,v_name);
end;
dbms_output.put_line(v_id || ' ' || v_name);
end loop;
close cur_load;
end;
I think it should work, didn't test it.
create or replace procedure BAS_NUM_UPD is
cursor cur is
select distinct o.oi_b,mpr.pa_ke_i,ltrim(substr(convert_171_to_711(cp.p_t_num),1,7),'0') bs_nbr
from t_obj o, mat_pa_rel mp, cor_pa cp
where o.ob_t = 'something'
and o.oi_b = mp.oi_b
and mp.pa_ke_i = cp.pa_ke_i;
l_ba_num_at_i number(10) := get_attribute_id('Ba timber');
flag1 VARCHAR2(10);
type t1 is table of varchar2(10);
par_k t1;
BEGIN
for x in cur loop
BEGIN
select pa_ke_i into par_k from mat_pa_rel where oi_b=x.oi_b ;
if par_k.count=null then
insert into cs_val (oi_b, at_i, value, flag, ) values (x.oi_b, l_ba_num_at_i, null, 1);
end if;
select flag into flag1 from cs_val where at_i = l_ba_num_at_i and oi_b = x.oi_b
and value = x.bs_nbr;
EXCEPTION
when NO_DATA_FOUND THEN
insert into cs_val (oi_b, at_i, value, flag, )
values (x.oi_b, l_ba_num_at_i, x.bs_nbr, 1);
flag1 :='Nothing';
when OTHERS
then
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
end loop;
end BAS_NUM_UPD;
error:
PLS-00642: local collection types not allowed in SQL statements
You should get it running if you do a bulk collect
select pa_ke_i bulk collect into par_k from mat_pa_rel where oi_b=x.oi_b ;
Then I think the if is not right. I think you need to do
if par_k.count = 0 then
But to be honest you might just make a count
select count(*) into l_cnt from mat_pa_rel where oi_b=x.oi_b;
If l_cnt = 0 then ...
Of course l_cnt has to be defined.
You should create type t1 in the schema and not in the pl/sql block.