How to do condition check in PLSQL - oracle

I have a table like below:
CREATE TABLE BRANDACTIVITYTYPE(
BRANDID VARCHAR2(50),
ACTIVITYCODE VARCHAR2(10),
CONSTRAINT PK_BRANDACTY primary key(BRANDID, ACTIVITYCODE),
CONSTRAINT FK_BRAND_BRANDACTY FOREIGN KEY (BRANDID) REFERENCES BRAND(BID) ON DELETE CASCADE,
CONSTRAINT FK_ACTIVITYCODE_BRANDACTY FOREIGN KEY (ACTIVITYCODE) REFERENCES ACTIVITYTYPE(ACTIVITYCODE) ON DELETE CASCADE
);
And the current data the table holds below:
BRANDID
ACTIVITYCODE
b01
A01
b01
A02
b02
A01
b02
A02
Now, I have written a stored procedure to check if new inserted row brandId and ActivityCode is already present or not in BRANDACTIVITYTYPE table. If present, then there's no need to insert the tuple in another table.
create or replace PROCEDURE add_re_rule
(
brandId IN VARCHAR2,
activityCode IN VARCHAR2,
points IN NUMBER,
ret OUT INT
)
AS
SAMERULECOUNT INT;
ACTYPECOUNT INT;
BEGIN
SELECT COUNT(BRANDID) INTO ACTYPECOUNT FROM BRANDACTIVITYTYPE WHERE BRANDID = brandId AND ACTIVITYCODE = activityCode;
SELECT COUNT(BRANDID) INTO SAMERULECOUNT FROM RERULE WHERE BRANDID = brandId AND ACTIVITYCODE = activityCode;
Dbms_Output.Put_Line(ACTYPECOUNT);
IF SAMERULECOUNT > 0 THEN
ret := 0;
ELSIF ACTYPECOUNT = 0 THEN
ret := 2;
ELSE
-- Insert into rerule table
INSERT INTO RERULE(BRANDID, ACTIVITYCODE, POINTS, VERSIONNO) values (brandId, activityCode, points, 1);
ret := 1;
END IF;
END;
/
When I run below query, it return 0 which is correct.
SELECT COUNT(BRANDID) FROM BRANDACTIVITYTYPE WHERE BRANDID = 'b01' AND ACTIVITYCODE = 'A05';
But, when I run the stored procedure, it returns 4 means all the tuple count of the BRANDACTIVITYTYPE table which is wrong and goes into the else condition. It should return 0.
SET SERVEROUTPUT ON;
DECLARE ret int;
BEGIN
add_re_rule('b01', 'A05', 60, ret);
END;
Error starting at line : 32 in command -
DECLARE ret int;
BEGIN
add_re_rule('b01', 'AOS', 60, ret);
END;
Error report -
ORA-02291: integrity constraint (C##SCOTT.FK_ACTIVITYCODE_RE) violated - parent key not found
ORA-06512: at "C##SCOIT.ADD RE RULE", line 23
ORA-06512: at line 3
02291. 00000 - "integrity constraint (%s.%s) viclated - parent key not found"
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
Am I missing anything?

You have used the same names for the parameter of the procedure and the column names and therefore the where clause of for example SELECT COUNT(BRANDID) INTO SAMERULECOUNT FROM RERULE WHERE BRANDID = brandId AND ACTIVITYCODE = activityCode; will not do as intended.
Just change the names of the parameter.

Related

“How to fix trigger in oracle pl/sql?

Problem:
Need a TRIGGER for the Table A which does the following:
Each time an INSERT is made into Table A on column TRT_PROCEDURE value, add 1
to the column 'TRT_INS_COUNT' in the table B. If the TRT_PROCEDURE value does
not exist in the table B, add a row to the table B for the procedure setting
TRT_INS_COUNT to 1.
Each time a DELETE is made on Table A, add 1 to the column TRT_DEL_COUNT in
table B for that Procedure value if it exists in the table B. If it does not
exist in the table B, add a row to the table B for the procedure and set
TRT_DEL_COUNT to 1.
Each time an UPDATE occurs for Column TRT_PROCEDURE in TABLE A, add 1 to the
column TRT_UPD_COUNT in table B. If it is not in the table B, add a row to
the table B for the procedure value and set TRT_UPD_COUNT to 1. If the Column
TRT_PROCEDURE value in TABLE A is changed, add 1 to the TRT_UPD_COUNT for the
old procedure value.
Test by doing several INSERTs, DELETEs, and UPDATEs and then display the table B.
Updates: whenever an value of trt_procedure is inserted into table B, the second same trt_procedure (e.g '88-20') cannot be correctly counted into its designated count field. Not sure where went wrong.
TABLE A
Name Null Type
TRT_ID NOT NULL NUMBER(3)
PAT_NBR NUMBER(4)
PHYS_ID NUMBER(3)
TRT_PROCEDURE VARCHAR2(5)
TRT_DATE DATE
TABLE B
Name Null Type
TRT_PROCEDURE NOT NULL VARCHAR2(5)
TRT_INS_COUNT NUMBER(3)
TRT_DEL_COUNT NUMBER(3)
TRT_UPD_COUNT NUMBER(3)
TEST SAMPLES
INSERT INTO A VALUES (11, 8031,101,'88-20',sysdate );
INSERT INTO A VALUES (12, 5872,101,'60-00',sysdate );
UPDATE A SET trt_procedure = '88-20' WHERE trt_id=6;
/*row trt_id =6 cloumn trt_procedure old value '54-60'
new value '88-20', which means Table B row '88-20' and
row '54-60 both trt_upd_count should add 1*/
DELETE FROM A WHERE trt_id=1;
/*row trt_id =1 cloumn trt_procedure value also '88-20', which means
that Table B row '88-20' trt_del_count should also add 1*/
CREATE OR REPLACE TRIGGER trt_stats_trg
BEFORE INSERT OR UPDATE OR DELETE OF TRT_procedure ON A FOR EACH ROW
BEGIN
IF INSERTING THEN
UPDATE B SET trt_ins_count = trt_ins_count+1
WHERE B.trt_procedure = :new.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_ins_count)
VALUES (:new.trt_procedure, 1);
END IF;
ELSIF UPDATING THEN
UPDATE B SET trt_upd_count = trt_upd_count+1
WHERE B.trt_procedure = :old.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_upd_count)
VALUES (:old.trt_procedure, 1);
END IF;
UPDATE B SET trt_upd_count = trt_upd_count+1
WHERE B.trt_procedure = :new.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_upd_count)
VALUES (:new.trt_procedure, 1);
END IF;
ELSIF DELETING THEN
UPDATE B SET trt_del_count = trt_del_count+1
WHERE B.trt_procedure = :old.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_del_count)
VALUES (:old.trt_procedure, 1);
END IF;
END IF;
END trt_stats_trg;
Here you go, with a few fixes.
The tables:
create table a(
TRT_ID NUMBER(3) NOT NULL ,
PAT_NBR NUMBER(4),
PHYS_ID NUMBER(3),
TRT_PROCEDURE VARCHAR2(5),
TRT_DATE DATE
);
create table b(
TRT_PROCEDURE VARCHAR2(5) NOT NULL,
TRT_INS_COUNT NUMBER(3),
TRT_DEL_COUNT NUMBER(3),
TRT_UPD_COUNT NUMBER(3)
);
And the trigger
CREATE OR REPLACE TRIGGER trt_stats_trg
BEFORE INSERT OR UPDATE OR DELETE ON A FOR EACH ROW
BEGIN
IF INSERTING THEN
UPDATE B SET trt_ins_count = nvl(trt_ins_count,0)+1
WHERE B.trt_procedure = :new.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_ins_count)
VALUES (:new.trt_procedure, 1);
END IF;
ELSIF UPDATING('TRT_PROCEDURE') THEN
if nvl(:old.trt_procedure, 'X') <> nvl(:new.trt_procedure, 'X') then
UPDATE B SET trt_upd_count = nvl(trt_upd_count,0)+1
WHERE B.trt_procedure = :old.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_upd_count)
VALUES (:old.trt_procedure, 1);
END IF;
UPDATE B SET trt_upd_count = nvl(trt_upd_count,0)+1
WHERE B.trt_procedure = :new.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_upd_count)
VALUES (:new.trt_procedure, 1);
END IF;
end if;
ELSIF DELETING THEN
UPDATE B SET trt_del_count = nvl(trt_del_count, 0)+1
WHERE B.trt_procedure = :old.trt_procedure;
IF SQL%NOTFOUND THEN
INSERT INTO B (trt_procedure, trt_del_count)
VALUES (:old.trt_procedure, 1);
END IF;
END IF;
END trt_stats_trg;
/
Key points:
You can use UPDATING('COLUMN_NAME') to detect when the SQL affects a specific column.
When UPDATING, you may wish to check for non-changing updates, where a column with value "ABC" is being updated to "ABC". This often happens in frameworks that simply include all columns in an update even when they are not really changing. Detect this with something similar to if nvl(:old.trt_procedure, 'X') <> nvl(:new.trt_procedure, 'X') then.
Table B does not have default values for the counts, so when the original trigger tried to update table B, it succeeded...in updating NULL to NULL, as that's what happens when you add 1 to NULL. This can be addressed by either assigning a default value of 0 to the columns in the table or by using NVL(trt_upd_count,0) + 1 instead of trt_upd_count + 1.
Finally, watch out for usages of :new and :old. It looks like they are correct in this trigger, but it will depend on your business case.
In the IF INSERTING section of your trigger you have
UPDATE B
SET trt_ins_count = trt_ins_count+1
WHERE :old.trt_procedure = :new.trt_procedure;
When inserting a new row all values in the :OLD pseudorow are NULL and thus your UPDATE will never update anything. I think you meant
UPDATE B
SET trt_ins_count = trt_ins_count+1
WHERE B.trt_procedure = :new.trt_procedure;
Give that a try and see if that helps solve your problem.

Trigger is not working when I'm trying to insert a value into a table

I am trying to make an Insert Trigger for a table called Marks which has id, id_student, id_course, value, data_notation, created_at, updated_at.
I need to make an Update on the old value, if the value I want to insert is higher than the one already exists in the column, and if there are no values in the column you would do an Insert with the new value.
I created the Trigger and there are no compilation errors.
CREATE OR REPLACE TRIGGER insert_value
before INSERT ON Marks
FOR EACH ROW
BEGIN
IF (:OLD.value IS NULL) THEN
DBMS_OUTPUT.PUT_LINE('Inserting.. because value is null');
UPDATE Marks SET value = :NEW.value where id_student = :NEW.id_student;
ELSE
DBMS_OUTPUT.PUT_LINE('Updating old value.. if old value is smaller than the one we want');
IF (:OLD.value < :NEW.value) THEN
UPDATE Marks SET value = :NEW.value where :OLD.id_student = :NEW.id_student;
END IF;
END IF;
END;
I want to change the old value from an existing value 5 to null for a specific id.
update Marks set value = null where id = 692;
select * from Marks where id = 692;
But when I'm trying to insert a value into the table so I can change the value null into 6 via the trigger
INSERT INTO Marks
VALUES (692, 43, 12, 6, '13-02-2018', '13-02-2018', '13-02-2018');
I am receiving an error.
Error report -
SQL Error: ORA-00001: unique constraint (STUDENT.SYS_C007784) violated
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
And it prints one time:
Inserting.. because value is null
But when I'm trying to check if the trigger did its job, using:
SELECT * from Marks where id = 692;
It doesn't update anything.
It has to be a trigger triggered by an insert operation. So I can't make the insert into the table, but how else should I write it so it works?
You problem comes from recursive calling the trigger due to the insert. The following would work. It does not catch update statements. It only cares for inserts. If the row exists already the row gets deleted first and the existing value is used for the insert if the existing value is higher.
set lin 20000
drop table marks;
create table Marks(
id number,
id_student number,
id_course number,
value number,
data_notation varchar2(40),
created_at timestamp,
updated_at timestamp,
CONSTRAINT marks#u UNIQUE (id, id_student, id_course)
);
create or replace trigger mark_trigger
before insert on marks
for each row
declare
l_value number;
l_data_notation varchar2(40);
l_created_at timestamp;
begin
select value, data_notation, created_at
into l_value, l_data_notation, l_created_at
from
(select *
from marks
where marks.id = :new.id
and marks.id_student = :new.id_student
and marks.id_course = :new.id_course
order by created_at desc)
where rownum=1;
if l_value is null then
return;
end if;
if l_value > :new.value then
:new.value := l_value;
:new.data_notation := l_data_notation;
:new.created_at := l_created_at;
else
:new.updated_at := systimestamp;
end if;
delete from marks
where marks.id = :new.id
and id_student = :new.id_student
and id_course = :new.id_course;
exception
when no_data_found then
null;
end;
create or replace procedure marks_insert(
i_id number,
i_id_student number,
i_id_course number,
i_value number,
i_data_notation varchar2
)
is
begin
INSERT INTO marks
VALUES (i_id, i_id_student, i_id_course, i_value, i_data_notation, systimestamp, null);
END marks_insert;
begin
delete from marks;
marks_insert(1,1,1,5,'1 first entry');
marks_insert(1,1,1,6,'1 second entry');
marks_insert(1,1,2,3,'2 first entry');
marks_insert(1,1,2,2,'2 second entry');
end;
select * from marks;
Output:
Table dropped.
Table created.
Trigger created.
Procedure created.
PL/SQL procedure successfully completed.
ID ID_STUDENT ID_COURSE VALUE DATA_NOTATION CREATED_AT UPDATED_AT
---------- ---------- ---------- ---------- ---------------------------------------- -------------------------------------------------- --------------------------------------------------
1 1 1 6 1 second entry 07/05/2019 13:31:31.266817 07/05/2019 13:31:31.266928
1 1 2 3 2 first entry 07/05/2019 13:31:31.268032
2 rows selected.
You are inserting into the Marks when you insert into the Marks (the insert statement in the trigger before inserting) and so on in a recursive way. Hence the direct cause of error.

Ajust table rows after after modifications (Delete, Insert Update) on Oracle

On table "Types", the rows order shall be controled by the field "ordem".
After any data modifications(Delete, Insert or Update), the others rows should be ajusted to assure correct exbitions of the content.
To implement this funcionality, I tried to code a trigger to correct the values in two similar ways (I, II).
CREATE TABLE TYPES (
ID NUMBER PRIMARY KEY,
ARG_1 VARCHAR2(20) NOT NULL,
ARG_2 VARCHAR2(20) NOT NULL,
ORDEM NUMBER NOT NULL
);
-----------------------------------------------
-- I
-----------------------------------------------
CREATE OR REPLACE TRIGGER TGR_TYPES
AFTER INSERT OR UPDATE OR DELETE ON TYPES
DECLARE
V_NORDEM NUMBER := NEW.ORDEM;
CURSOR C_TYPES IS
SELECT ID, ORDEM
FROM TYPES
WHERE ORDEM >= V_NORDEM;
BEGIN
IF UPDATING OR INSERTING THEN
BEGIN
FOR R_TYPE IN C_TYPES LOOP
UPDATE TYPEA SET ORDEM = (ORDEM + 1) WHERE ID = R_TYPE.ID;
END LOOP;
END;
ELSE
DECLARE V_ORDEM NUMBER := 0;
BEGIN
FOR R_TYPE IN C_TYPES LOOP
UPDATE OSP_TP_ADDR_COMPLEMENTOS SET ORDEM = (V_ORDEM + 1) WHERE ID = R_COMPLEMENTO.ID;
END LOOP;
END;
END IF;
END;
/*
ERROR ON COMPILE:
ORA-04082: referências NEW ou OLD não permitidas nos gatilhos de nível de tabela
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
*/
-----------------------------------------------
-- II
-----------------------------------------------
CREATE OR REPLACE
TRIGGER TRG_TYPES
AFTER INSERT OR UPDATE OR DELETE ON TYPES
FOR EACH ROW
BEGIN
IF UPDATING OR INSERTING THEN
BEGIN
FOR TP IN (
SELECT *
FROM TYPES
WHERE ORDEM >= :NEW.ORDEM
) LOOP
UPDATE TYPES SET ORDEM = (ORDEM + 1) WHERE ID = TP.ID;
COMMIT;
END LOOP;
END;
ELSE
DECLARE V_ORDEM NUMBER := 0;
BEGIN
FOR TP IN (
SELECT *
FROM TYPES
ORDER BY ORDEM
) LOOP
UPDATE TYPES SET ORDEM = (V_ORDEM + 1) WHERE ID = TP.ID;
END LOOP;
END;
END IF;
END;
/*
ERROR ON UPDATE:
UPDATE TYPES
SET ORDEM = 14
WHERE ID=26
Relatório de erros -
ORA-04091: a tabela TYPES é mutante; talvez o gatilho/função não possa localizá-la
ORA-06512: em "", line 9
ORA-04088: erro durante a execução do gatilho ''
*/
I expect one solution to assure the trigger operation or other approach to the integrity of the order field. I have thinking about tring the Index-Organized Table structure, but not sure.

PL/Sql query execution error

This is my query. A Trigger which automatically stores in a separate table called ‘ExcellentSale’ the Sales Agent
name, car model and manufacturer name, each time the agreed price of a
SalesTransaction is more than 20% of the car’s asking price. (Note: You need to create
the ‘ExcellentSale’ table before implementing this trigger. To create the primary key, use a
sequence that starts at 1 and increments by 1). It shows error like this
ERROR at line 23: PL/SQL: ORA-00942: table or view does not exist
1. create or replace trigger filltable
2. after insert on salestransaction
3. for each row
4. declare
The code for the trigger is:
create or replace trigger filltable
after insert on salestransaction
for each row
declare
aname varchar2(25);
modname varchar2(25);
manfname varchar2(25);
askpr number;
agrpr number;
begin
select sa.name,m.name,mf.name
into aname,modname,manfname
from manufacturer mf,model m, car c, salestransaction st, salesagent sa
where mf.manufacturerid = m.manufacturerid and
m.modelno = c.modelno and
c.vin = st.vin and
st.agentid = sa.agentid;
select askingprice,agreedprice
into askpr,agrpr
from car c,salestransaction st
where c.VIN = St.vin;
if(agrpr > askpr*1.2) then
insert into excellentsales values(agent_seq.nextval,aname,modname,manfname);
end if;
end filltable;
/
create table excellentsales
(agentid varchar2(5) not null,
agentname varchar2(25),
carmodel varchar2(25),
mfname varchar2(25),
primary key(agentid))
CREATE SEQUENCE agent_seq START WITH 1 INCREMENT BY 1;
You don't need to query SALESTRANSACTION within a row trigger on SALESTRANSACTION - you should instead be using the :NEW values on the SALESTRANSACTION row for which the trigger was invoked. I suggest rewriting your trigger as:
create table excellentsales
(EXCELLENTSALES_ID NUMBER
CONSTRAINT PK_EXCELLENTSALES
PRIMARY KEY
USING INDEX,
agentid varchar2(5) not null,
agentname varchar2(25),
carmodel varchar2(25),
mfname varchar2(25))
CREATE SEQUENCE EXCELLENTSALES_SEQ
START WITH 1
INCREMENT BY 1;
create or replace trigger filltable
after insert on salestransaction
for each row
declare
aname varchar2(25);
modname varchar2(25);
manfname varchar2(25);
askpr number;
agrpr number;
begin
SELECT m.NAME, mf.NAME, c.ASKINGPRICE
INTO modname, manfname, askpr
FROM CAR c
INNER JOIN MODEL m
ON m.MODELNO = c.MODELNO
INNER JOIN MANUFACTURER mf
ON mf.MANUFACTURERID = m.MANUFACTURERID
WHERE c.VIN = :NEW.VIN;
SELECT sa.NAME
INTO aname
FROM SALESAGENT sa
WHERE sa.AGENTID = :NEW.AGENTID;
agrpr := :NEW.AGREEDPRICE;
if agrpr > askpr * 1.2 then
insert into excellentsales (EXCELLENTSALES_ID, AGENTID, AGENTNAME, CARMODEL, MFNAME)
values(EXCELLENTSALES_SEQ.nextval, :NEW.AGENTID, aname, modname, manfname);
end if;
end filltable;
I also redesigned your EXCELLENTSALES table to include a primary key which is not the AGENTID. As originally designed EXCELLENTSALES did not record the actual AGENTID (which should apparently be coming from SALESTRANSACTION), which in the real world might be needed, for example, for paying commissions.

how to find a record from a table with existing record in oracle?

here's a table with a high number of columns
create table test (
id number,
col_1 varchar2(50),
col_2 varchar2(50),
.
.
col_n varchar2(50)
);
and the table is filled with some example data
insert into test values(1,'a1','a2',...,'an');
insert into test values(2,'a1','a3',...,'am');
insert into test values(3,'a4','a2',...,'ax');
now i need to copy one row (fore example the row with id=1) and change just one column's value, if the result does not be similar to another row (without considering id).
some thing like this:
declare
r test%rowtype;
var1 number;
begin
insert into r from test where id = 1;
r.col_2='a3';
select count (*) into var1 from test where 'the row without id' = r;
if (var1 = 0) then
insert into test values r;
end if;
end;
but i don't know how to write the select part in oracle. consider table test has lots of columns so you can't write all of the columns in where clause.
It is sort of hard to understand what you need. I'll take a shot here. Let me know if this is on track....
To make things easy, can you create a unique index on the table?
create unique index test_uidx on test (col_1, col_2, ... col_n);
And then let oracle do the work:
declare
r test%rowtype;
var1 number;
begin
select * into r from test where id=1; --- get the row
r.col_1 := 'some new value'; --- change 1 value
begin
insert into test values r; --- insert the row
exception
when dup_val_on_index then --- row was a dup
null; --- and did not insert
end;
end;

Resources