I have set of data filtering from the select statement which I need to delete from the table 'SHOP_MATERIAL_ASSIGN_TAB'. I have used a cursor to delete. but this caused problems when executing. any idea to get it done without using 'for update' method.
DECLARE
CURSOR DELDATA_ IS
SELECT B.*
FROM IC_WQ_OLD_LOCATION_TAB A, SHOP_MATERIAL_ASSIGN_TAB B
WHERE A.LOCATION_NO = B.LOCATION_NO
AND A.SITE = B.CONTRACT
AND B.LOCATION_NO = 'HYLIN001'
FOR UPDATE;
TEMP_ DELDATA_%ROWTYPE;
BEGIN
OPEN DELDATA_;
LOOP
FETCH DELDATA_
INTO TEMP_;
EXIT WHEN DELDATA_%NOTFOUND;
DELETE FROM SHOP_MATERIAL_ASSIGN_TAB WHERE CURRENT OF DELDATA_;
END LOOP;
COMMIT;
END;
Don't use PL/SQL at all.
DELETE FROM shop_material_assign_tab
WHERE EXISTS
(SELECT NULL
FROM ic_wq_old_location_tab a
WHERE a.location_no = b.location_no
AND a.site = b.contract)
AND b.location_no = 'HYLIN001'
Related
There is a table which i want to update each row after inserted, when the user_type of my schedule table is a "superuser". I tried to convert the table/column names to "simplify", so some names may not make sense, it's ok.
The trigger code:
CREATE OR REPLACE TRIGGER "TR_UPT_SCHEDULE"
AFTER INSERT
ON SCHEDULE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE V_UserCode VARCHAR2(20);
BEGIN
--Find "super user" code.
SELECT UserTypeCode INTO V_UserCode FROM
(SELECT UL.User_Code,
UFT.UserTypeCode,
ROW_NUMBER() OVER (PARTITION BY UL.User_Code ORDER BY UF.UserTypeCode DESC) RN
FROM UserLogin UL
JOIN UserFunction UF
ON UL.User_Code = UF.User_Code
JOIN UserFuncType UFT
ON UFT.UserTypeCode = UF.UserTypeCode
WHERE UFT.FuncType = 'S'
) WHERE RN = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_UserCode := NULL;
IF V_UserCode IS NOT NULL
THEN UPDATE SCHEDULE
SET :NEW.UserTypeCode = V_UserCode,
OrigScheType = :NEW.UserTypeCode
WHERE CompCode = :NEW.CompCode
AND UserTypeCode = :NEW.UserTypeCode
AND ScheOrigin = :NEW.ScheOrigin
AND ScheCode = :NEW.ScheCode;
END IF;
END;
When i try to create the trigger, is returned the fallowing error:
ORA-00922
I'm pretty sure the problem is the UPDATE part, where tried compare in the WHERE clause, the Schedule table keys to be sure of which row i'm updating(inserted), but i could not realize the problem.
In triggers, you don't update tablename set :new.columnname = some value, you directly assign values to :new.columnname using PL/SQL assignments or select into. Therefore, from a syntax point of view, the final update should be replaced with something like:
:new.usertypecode := v_usercode;
:new.origschetype := v_usercode;
(I am assuming the update is only intended to apply to the current row. If the idea is to update multiple rows in the triggering table, you can't do that in a row-level trigger.)
However, this logic doesn't look like it will work:
exception
when no_data_found then
v_usercode := null;
if v_usercode is not null then
v_usercode has to be null at this point, so you don't have a value to set :new.usertypecode to, and I'm not sure what you want the trigger to do.
Here there is 3 table
1.employee(eid,ename),
2.address(aid,address),
3.employee_add(eid,aid)
employee and address has many to many relation in it.what I need to do is to clean the duplicate from address table without any data loss from employee_add table. thanks in advance! please help
DECLARE
a ADDRESS.AID%TYPE;
b ADDRESS.ADDRESS%TYPE;
c ADDRESS.AID%TYPE;
d ADDRESS.ADDRESS%TYPE;
CURSOR Cur1 IS
SELECT AID,ADDRESS
FROM ADDRESS;
CURSOR Cur2 IS
SELECT AID,ADDRESS
FROM ADDRESS;
BEGIN
OPEN Cur1;
LOOP
FETCH Cur1 INTO a, b;
EXIT WHEN Cur1%NOTFOUND;
OPEN Cur2;
LOOP
FETCH Cur2 into c,d;
IF (b=d) THEN
IF(a!=c) THEN
update employee_add set aid=a where aid=c;
delete from address where aid=c;
END IF;
END IF;
END LOOP;
CLOSE Cur2;
END LOOP;
CLOSE Cur1;
END;
You should be able to do this using the following SQL statements (which you could put inside a PL/SQL procedure if you wanted to), like so:
-- To update the employee_add tables
MERGE INTO employee_add tgt
USING (SELECT ea.rowid rid,
a.aid,
a.address,
MIN(aid) OVER (PARTITION BY address) new_aid
FROM address a
INNER JOIN employee_add ea ON ea.aid = a.aid) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
UPDATE SET tgt.aid = src.new_aid
WHERE tgt.aid != src.new_aid;
-- Delete any rows now longer in the employee_add table
DELETE FROM address
WHERE aid NOT IN (SELECT aid FROM employee_add);
-- If you need to deduplicate the employee_add table, this should do the trick:
DELETE FROM employee_add ea1
WHERE ROWID > (SELECT MIN(ROWID)
FROM employee_add ea2
WHERE ea1.eid = ea2.eid
AND ea1.aid = ea2.aid;
In general, explicit cursor is slower than implict cursor.
You can try to convert the
OPEN ...
LOOP
FETCH ...
EXIT WHEN ...
...
...
END LOOP;
into
FOR ... LOOP
...
END LOOP;
else, it would help if you provide some DDLs & DMLs (together with PK, indexes and constraints).
I need to delete data from many tables based on one parameter
The problem is that two tables are related to each other so in order to delete data properly i need to store id's somewhere.
-- i would like to store temp data
-- this one is only for convienience to avoid repeating same select many times
create table ztTaryfa as select zt_taryfa from tw_zbiory_taryfy
where 1=2;
-- this one is mandatory but I dont know how to make it work
Create table wnioskiId as select poli_wnio_id_wniosku from polisy
where 1=2;
Begin
-- fill temp tables
insert into ztTaryfa (
select zt_taryfa from tw_zbiory_taryfy
where zt_zbior = :zbiorId);
insert into wnioskiId (
select poli_wnio_id_wniosku from polisy
where poli_taryfa_id in ztTaryfa);
- regular deletion
delete from POLISY_OT where ot_poli_id in (
select poli_id from polisy
where poli_taryfa_id in ztTaryfa);
commit;
delete from DANE_RAPORTOWE where DR_RPU_ID in (
select RPU_ID from ROZLICZ_PLIK_UBEZP where RPU_ROZLICZ_PLIK_ID in (
select RP_ID from ROZLICZ_PLIK
where RP_ZBIOR_ID = :zbiorId ));
commit;
-- and here we go I need to delete data from POLISY first
delete from POLISY where poli_taryfa_id in ztTaryfa;
commit;
-- but by doing it I lose ids which i need here,
-- so I have to store them somehow and use them here.
delete from WNIOSKI where wnio_id in wnioskiId;
commit;
End;
-- and now lets get rid off temp tables
drop table ztTaryfa;
commit;
drop table wnioskiId;
commit;
To sum up i just need to know how to store somewhere between Begin and End a result of a select query which I can later use in delete statement.
Sounds but I tried so many different methods and all seems to not work.
What u see above is just a 1/3 of the script so I rly would like to make it all simple to use with one parameter.
Thanks you in advance.
You can use global types as simple as this:
create or replace type myrec is object (myid number);
create or replace type mytemp_collection is table of myrec;
declare
v_temp_collection mytemp_collection;
begin
v_temp_collection := mytemp_collection();
select myrec (t.field_type_id ) bulk collect into v_temp_collection from fs_field_types t
where mod(t.field_type_id+1,3)=0; -- for example
FOR i IN 1 .. v_temp_collection.count LOOP
DBMS_OUTPUT.put_line(v_temp_collection(i).myid);
End loop;
delete fs_field_types_back t where t.field_type_id in (select myid from table(v_temp_collection));
end;
Change select and where clause according to your business.
Let's have a look on my source code:
CREATE OR REPLACE PROCEDURE MAKE_COPY_OF_CLASSROOMS AUTHID CURRENT_USER AS
TYPE classrooms_table_type IS TABLE OF classrooms%ROWTYPE INDEX BY PLS_INTEGER;
classrooms_backup classrooms_table_type;
CURSOR classrooms_cursor IS
SELECT *
FROM classrooms
WHERE year = 1
ORDER BY name;
v_rowcnt PLS_INTEGER := 0;
BEGIN
OPEN classrooms_cursor;
FETCH classrooms_cursor
BULK COLLECT INTO classrooms_backup;
CLOSE classrooms_cursor;
EXECUTE IMMEDIATE 'CREATE TABLE classrooms_copy AS (SELECT * FROM classrooms WHERE 1 = 2)';
--COPY ALL STORED DATA FROM classrooms_backup TO classrooms_copy
END MAKE_COPY_OF_classrooms;
I'm stucked for hours on trying to insert data from "classrooms_backup" into the table "classrooms_copy", which is created by EXECUTE IMMEDIATE command. It's necessary to create table "classrooms_copy" via EXECUTE IMMEDIATE command. I tried to create another EXECUTE command with for loop in it:
EXECUTE IMMEDIATE 'FOR i IN classrooms_backup.FIRST..classrooms_backup.LAST LOOP
INSERT INTO classrooms_copy(id,room_id,year,name)
VALUES(classrooms_backup(i).id,classrooms_backup(i).room_id,classrooms_backup(i).year,classrooms_backup(i).name);
END LOOP;';
But it's road to the hell. I'm retrieving an invalid SQL statement error.
Thanks for your help!
There's no need for much PL/SQL here. Also, try to avoid the keyword CURSOR - there's almost always a better way to do it.
create or replace procedure make_copy_of_classrooms authid current_user as
begin
execute immediate '
create table classrooms_copy as
select *
from classrooms
where year = 1
order by name
';
end make_copy_of_classrooms;
/
I create a trigger to check COUNT.
create or replace
TRIGGER TEST_TRG before INSERT OR UPDATE ON TEST
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
AVAILABLE INTEGER;
BEGIN
IF UPDATING THEN
IF(:new.STATUS = 600 OR :new.STATUS = 700) THEN
SELECT COUNT(1) INTO AVAILABLE FROM TEST T WHERE T.IDDISPLAY = :new.IDDISPLAY
AND T.STATUS NOT IN (600,700);
IF(AVAILABLE = 0) THEN
InsertOrUpdateAnotherTable(:new.IDDISPLAY, :new.STATUS, 0);
ELSE
RETURN;
END IF;
END IF;
END IF;
After I change the status to 600 in Test Table, there are errors.
ORA-04091: table USER.TEST is mutating, trigger/function may not see it
ORA-06512: at "USER.TEST_TRG ", line 8
ORA-04088: error during execution of trigger 'USER.TEST_TRG'
I will insert or update another table if the condition is met.
The error is because I try to get COUNT of the current table when the trigger is triggering the same table. This will cause mutating happens.
I have try transaction_anonymous and Compound Trigger, but at the end still same error occurs.
Anyone can help me for another solution, please.
Firstly:
This below line
IF(AVAILABLE = 0) THEN
never will be reached, even if you could eliminate mutating error.
Checking count like this: COUNT(1) INTO AVAILABLE will throw exception "no data found" when there is really no records you're quering.
Secondly:
using compund trigger with section AFTER EACH ROW let you avoid mutating as Tom wrote http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm:
Try this:
create or replace
TRIGGER TEST_TRG FOR INSERT OR UPDATE ON TEST
COMPOUND TRIGGER
AVAILABLE INTEGER;
AFTER EACH ROW IS
BEGIN
IF UPDATING THEN
IF(:new.STATUS = 600 OR :new.STATUS = 700) THEN
BEGIN
SELECT COUNT(1) INTO AVAILABLE FROM TEST T WHERE T.IDDISPLAY = :new.IDDISPLAY
AND T.STATUS NOT IN (600,700);
EXCEPTION
WHEN NO_DATA_FOUND THEN
InsertOrUpdateAnotherTable(:new.IDDISPLAY, :new.STATUS, 0);
END;
END IF;
END IF;
END AFTER EACH ROW;
END TEST_TRG;