I have the following code which produces a few compiler errors. I've followed several examples online to use these declare and the FROM DUAL concepts, but can't ID the problem. Thanks!
Error1: SQL statement Ignored (referencing line 9 - "DECLARE")
Error2: missing VALUE keyword (referencing line 15 - "FROM DUAL;")
---delete or replace trigger------------------------------
CREATE OR REPLACE TRIGGER videoAuditDeleteTrigger
BEFORE DELETE OR UPDATE
ON video
FOR EACH ROW
DECLARE
v_username VARCHAR2(20);
BEGIN
SELECT USER INTO v_username
FROM DUAL;
INSERT INTO videoAudit
(videoID
,title
,releaseDate
,category
,username
,datetimestamp);
VALUES
(:OLD.videoID
,:OLD.title
,:OLD.releaseDate
,:OLD.category
,v_username
,SYSDATE);
END;
/
Try this, you used extra semicolon in insert statement
CREATE OR REPLACE TRIGGER videoAuditDeleteTrigger
BEFORE DELETE OR UPDATE ON video FOR EACH ROW
DECLARE v_username VARCHAR2(20);
BEGIN
SELECT USER INTO v_username FROM DUAL;
INSERT INTO videoAudit (videoID ,title ,releaseDate ,category ,username ,datetimestamp)
VALUES (:OLD.videoID ,:OLD.title ,:OLD.releaseDate ,:OLD.category ,v_username ,SYSDATE);
END;
Related
I try to create a custom table, which is created properly. The last 2 fields are matching the type from Orders table. The trigger's idea is when a certain date field is changed to INSERT this into the new table. The trigger is created in Oracle correctly, but once the field in question changes I get ORA errors (below) and cannot find out why they appear.
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "shc.trigger_table_1", line 20
ORA-04088: error during execution of trigger 'shc.trigger_table_1'
I tried changing the datatypes, tried introducing an EXCEPTION, tried removing the :old.info23 check.
Here is the table I am creating:
CREATE TABLE table1(
id number generated by default as identity,
table_name varchar2(20),
field_changed varchar2(20),
old_value date,
new_value date,
changed_by varchar(20),
date_of_change date,
orderRef varchar(50),
Ref varchar2(256));
Here is the problematic trigger:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
DECLARE
my_AEXTERNAUFTRAGSNR varchar2(50);
my_AUFTRAGSREFERENZ2 varchar2(256);
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
Select AEXTERNAUFTRAGSNR, AUFTRAGSREFERENZ2
Into my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2
From orders
Where orders.nr = :old.ordernr;
insert into table1(table_name, field_changed, old_value, new_value, changed_by, date_of_change, orderRef, Ref)
values ('consignment', 'ANKUNFTBELDATUMVON', :OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON, sys_context('userenv','OS_USER'), SYSDATE, my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2);
END IF;
END;
END;
The immediate problem is that you don't commit your autonomous transaction. If you change it to commit at the end:
...
END IF;
END;
commit;
END;
/
then it will work - fiddle. (You don't need the nested block, but it doesn't stop it working... and you could do insert ... select ... to avoid needing any local variables...)
But as you can see from the db<>fiddle result, if the update on consignment is rolled back then the insert into table1 is retained, since that was committed independently.
If you remove the PRAGMA AUTONOMOUS_TRANSACTION; instead then that won't happen. Then you won't need to, and indeed can't, commit within the trigger. You would just need:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
DECLARE
my_AEXTERNAUFTRAGSNR varchar2(50);
my_AUFTRAGSREFERENZ2 varchar2(256);
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
Select AEXTERNAUFTRAGSNR, AUFTRAGSREFERENZ2
Into my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2
From orders
Where orders.nr = :old.ordernr;
insert into table1(table_name, field_changed, old_value, new_value, changed_by, date_of_change, orderRef, Ref)
values ('consignment', 'ANKUNFTBELDATUMVON', :OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON, sys_context('userenv','OS_USER'), SYSDATE, my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2);
END IF;
END;
/
or with insert ... select ...:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
insert into table1(table_name, field_changed,
old_value, new_value,
changed_by, date_of_change,
orderRef, Ref)
select 'consignment', 'ANKUNFTBELDATUMVON',
:OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON,
sys_context('userenv','OS_USER'), SYSDATE,
o.AEXTERNAUFTRAGSNR, o.AUFTRAGSREFERENZ2
from orders o
where o.nr = :old.ordernr;
END IF;
END;
/
fiddle
Incidentally, if you only want the trigger to fire if a specific column (or columns) was included in an update statement then you could do:
AFTER UPDATE OF ANKUNFTBELDATUMVON ON consignment
but you would still need to check it had actually changed. Your current check doesn't cover ANKUNFTBELDATUMVON being update to or from null, which is only potentially an issue if the column is nullable.
I have this simple stored procedure, where it would add a column to my Orders table
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
DECLARE
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1;
BEGIN
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C')
END ADD_ORDER;
It is saying the the declare part is not at the correct place (I think), and also it should not end there. Here is what it is saying at the error screen:
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following:
begin function pragma procedure subtype type current cursor delete exists prior external language The symbol "begin was inserted before "DECLARE" to continue.
Error(11,1): PLS-00103: Encountered the symbol "END" when expecting one of the following: , ; return returning
Can anyone tell me what is going wrong here ?
As has been mentioned, it is a bad idea to select the maximum order number and then use that to insert a row. If two processes do this at the same time, they try to insert rows with the same order number.
Better use Oracle's built-in features SEQUENCE or IDENTITY.
Here is how you could create the table:
CREATE TABLE orders
(
order_no NUMBER(8) GENERATED ALWAYS AS IDENTITY,
rep_no NUMBER(3) DEFAULT 36 NOT NULL,
cust_no NUMBER(8) NOT NULL,
order_dt DATE DEFAULT SYSDATE NOT NULL,
status VARCHAR2(1) DEFAULT 'C' NOT NULL
);
And this is what your procedure would look like then:
CREATE OR REPLACE PROCEDURE add_order
(
in_cust_no IN NUMBER,
out_order_no OUT NUMBER
) AS
BEGIN
INSERT INTO ORDERS(cust_no) VALUES (in_cust_no)
RETURNING order_no INTO out_order_no;
END add_order;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4b49723c15eb810c01077286e171bc95
There is a syntax error in your code.
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1; --not be used liked it.
Use below code
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
V_NEW_ORDER_ID NUMBER;
BEGIN
SELECT NVL(MAX(ORDER_NO),0)+1 INTO V_NEW_ORDER_ID FROM ORDERS;
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( V_NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C');
NEW_ORDER_ID:=V_NEW_ORDER_ID;
/*
* CURDATE() -> I am assuming it is user defined function. You can also use SYSDATE, CURRENT_DATE istead of CURDATE()
* OUT Parameter is a write-only parameter. You cannot read value from OUT Parameter
*/
END ADD_ORDER;
Few things need to be correct.
If you're expecting to write a PROCEDURE or a FUNCTION you don't have to use the DECLARE keyword. In writing a test script or something, you should use the DECLARE keyword to declare variables.
When writing a procedure,
All the parameters should be inside the brackets.
Variables should define between AS and BEGIN keywords and should give the datatype.
If you need to fetch the MAX number of ORDERS TAB you have to write a SQL query for that. Because the MAX function only can be used inside a SQL. Additionally, if you interest there is an in-built feature call SEQUENCE in ORACLE which can use for NEW_ORDER_ID. You can check with the link below.
adding a sequence for oracle plsql
I did some changes to your code. Hope it's working fine now. Please take a visit here.
CREATE or REPLACE PROCEDURE ADD_ORDER (
CUSTOMER_ID IN NUMBER
NEW_ORDER_ID OUT NUMBER
) AS
CURSOR get_max_order_no IS
SELECT MAX(order_no)
FROM ORDERS;
rec_ NUMBER := 0;
BEGIN
OPEN get_max_order_no;
FETCH get_max_order_no INTO rec_;
CLOSE get_max_order_no;
NEW_ORDER_ID := rec_ + 1;
INSERT INTO ORDERS
(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES
(NEW_ORDER_ID, 36, CUSTOMER_ID, SYSDATE, 'C');
END ADD_ORDER;
Here I have a question that I need to write a PL/SQL. The structure of the database is also linked. The question requires to use a sequence inside a procedure. I'm new to this and don't know if this works properly and my exec command doesn't seem working please help me out. Also is this how to look up the max shareholder_id that the sequence should start with, or can I a select inside create sequence?
Write a PL/SQL procedure called INSERT_DIRECT_HOLDER which will be used to insert new direct holders. Create a sequence to automatically generate shareholder_ids. Use this sequence in your procedure.
-Input parameters: first_name, last_name
DROP SEQUENCE shareholder_id_seq;
SELECT
MAX(shareholder_id)
FROM shareholder;
CREATE SEQUENCE shareholder_id_seq
INCREMENT BY 1
START WITH 25;
CREATE OR REPLACE PROCEDURE insert_direct_holder(
p_first_name in direct_holder.first_name%type,
p_last_name in direct_holder.last_name%type)
IS
v_shareholder_id NUMBER(6);
BEGIN
INSERT INTO DIRECT_HOLDER(direct_holder_id,first_name,last_name) values(shareholder_id_seq.nextval, p_first_name, p_last_name);
INSERT INTO shareholder (shareholder_id, type) VALUES (shareholder_id_seq.nextval,'Direct_Holder');
COMMIT;
END;
/
/* test command*/
exec insert_direct_holder( p_first_name, p_last_name );
You need to insert the record with same ID in both the tables.
also, you need to insert record into your parent table (shareholder) first and then child table(direct_holder).
CREATE OR REPLACE PROCEDURE insert_direct_holder(
p_first_name in direct_holder.first_name%type,
p_last_name in direct_holder.last_name%type)
IS
v_shareholder_id NUMBER(6);
BEGIN
v_shareholder_id := shareholder_id_seq.nextval;
INSERT INTO shareholder (shareholder_id, type) VALUES (v_shareholder_id,'Direct_Holder');
INSERT INTO DIRECT_HOLDER(direct_holder_id,first_name,last_name) values(v_shareholder_id, p_first_name, p_last_name);
COMMIT;
END;
/
/* test command*/
exec insert_direct_holder( p_first_name, p_last_name );
Sequence is created once to implement auto incrementing feature for any numeric column.
For the current use case it has to be created just once and left forever hopefully.Sequence can be modified in future if required.
If shareholder table has records in it already, then create the sequence with start value as SELECT MAX(shareholder_id) + 1 FROM shareholder; to avoid primary key constraint violation.
A slight modification is required for the stored procedure to use the same SHAREHOLDER.SHAREHOLDER_ID as the column has foreign key relationship with DIRECT_HOLDER.
Use INSERT ALL to insert to both the tables for the same sequence.nextval.
CREATE OR REPLACE PROCEDURE insert_direct_holder(
p_first_name in direct_holder.first_name%type,
p_last_name in direct_holder.last_name%type)
IS
BEGIN
INSERT ALL
INTO SHAREHOLDER
(shareholder_id, type) values(shareholder_id_seq.nextval,'Direct_Holder')
INTO DIRECT_HOLDER
(direct_holder_id,first_name,last_name) values
(shareholder_id_seq.nextval,p_first_name,p_last_name)
SELECT 'DUMMY' FROM dual;
COMMIT;
END;
/
dbfiddle demo with working code : https://dbfiddle.uk/?rdbms=oracle_18&fiddle=5d80488fb69d78d4b5087f06a5becf96
I don't know what my problem when created new trigger.
Is my syntax correct? Thanks! Console Logging pane
p/s: This my console display when I try to insert values
CREATE OR REPLACE TRIGGER EX03_3
BEFORE INSERT ON HR.CHITIETDATHANG
FOR EACH ROW
DECLARE
TONGHANG NUMBER; -- Total Items
HANGHIENCO NUMBER; -- Items present
HANGDABAN NUMBER; -- Items was sales.
BEGIN
-- Get total Items
SELECT SUM(MH.SOLUONG) INTO TONGHANG
FROM HR.MATHANG MH;
-- Get total Items was sales
SELECT SUM(CTDH.SOLUONG) INTO HANGDABAN
FROM HR.CHITIETDATHANG CTDH;
-- Items present
HANGHIENCO := TONGHANG - HANGDABAN;
IF(HANGHIENCO >= HANGDABAN) THEN
HANGHIENCO := HANGHIENCO-1;
INSERT INTO HR.CHITIETDATHANG VALUES(:NEW.SOHOADON,:NEW.MAHANG,
:NEW.GIABAN,:NEW.SOLUONG,:NEW.MUCGIAMGIA);
ROLLBACK;
END IF;
NULL;
END;
Seem two critical mistakes
1) trigger tries to insert into HR.CHITIETDATHANG in the body of insert trigger of HR.CHITIETDATHANG.
2) to use rollback after an insert statement is useless.
Note : I can see nothing relevant to make raise no_data_found in those select statements. EX03_3 and EX04_4 are confused, as Kaushik Nayak says.
I've been working around this trigger and when I run the script it tells me the previous error message. I can't seem to figure out why it won't compile correctly, every pl/sql trigger tutorial seems to have the structure my trigger has. Code is the following:
create
or replace trigger new_artist before insert
on
Artist referencing new as nvartist declare counter number;
begin select
count( * ) into
counter
from
Performer
where
Stage_name = nvartist.Stage_name;
if counter > 0 then signal sqlstate '45000';
else insert
into
Artist
values(
nvartist.Stage_name,
nvartist.Name
);
insert
into
Performer
values(nvartist.Stage_name);
end if;
end;
It checks if the new artist already exists in its supertype (Performer), if it does exist it gives an error if it doesn't it inserts both into artist(Stage_name varchar2, Name varchar2) and Performer(Stage_name). Another subtype of Performer (and sibling to Artist) is Band(Stage_name), which in turn has a relationship with Artist. Why does the compiler yell at me for this trigger?
Thanks in advance
You may want to try this variant (I slightly modified names of your tables).
Creating tables with sample data:
CREATE table test_artist(
stage_name varchar2(100)
, name varchar2(100)
);
create table test_performer(
stage_name varchar2(100)
);
/*inserting test performer on which trigger will rise an error*/
insert into test_performer
select 'performer_1' from dual;
Creating trigger:
create or replace trigger new_artist
before insert
on TEST_ARTIST
referencing new as nvartist
for each row
declare
counter number;
begin
select count(*)
into counter
from test_performer
where Stage_name = :nvartist.Stage_name;
if counter > 0 then
--signal sqlstate '45000' ;
raise_application_error( -20001, 'No insertion with existing Performer');
else
/*you cant update the same table, in other case you'll get
ora-04091 table mutating error.
But nevertheless this values will be inserted by sql triggered this trigger.*/
--insert into test_artist values(:nvartist.Stage_name, :nvartist.Name);
insert into test_performer values(:nvartist.Stage_name);
end if;
end new_artist;
After that this insert will work, cause the is no 'performer_2' in 'test_performer' table:
insert into test_artist
select 'performer_2', 'name_2' from dual;
And this will fail:
insert into test_artist
select 'performer_1', 'name_1' from dual;