sql trigger not firing with insert - sql

DROP TABLE ENROLLMENT CASCADE CONSTRAINTS;
DROP TABLE SECTION CASCADE CONSTRAINTS;
CREATE TABLE SECTION
(
SectionID CHAR(5),
Course VARCHAR2(7),
Students NUMBER(2,0) DEFAULT 0,
CONSTRAINT PK_SECTION
PRIMARY KEY (SectionID)
);
CREATE TABLE ENROLLMENT
(
SectionID CHAR(5),
StudentID CHAR(7),
CONSTRAINT PK_ENROLLMENT
PRIMARY KEY (SectionID, StudentID),
CONSTRAINT FK_ENROLLMENT_SECTION
FOREIGN KEY (SectionID)
REFERENCES SECTION (SectionID)
);
INSERT INTO SECTION (SectionID, Course) VALUES ( '12345', 'CSC 355' );
INSERT INTO SECTION (SectionID, Course) VALUES ( '22109', 'CSC 309' );
INSERT INTO SECTION (SectionID, Course) VALUES ( '99113', 'CSC 300' );
INSERT INTO SECTION (SectionID, Course) VALUES ( '99114', 'CSC 300' );
SELECT * FROM SECTION;
COMMIT;
CREATE TRIGGER AddStudent AFTER INSERT ON ENROLLMENT
BEGIN
DBMS_OUTPUT.PUT_LINE('DONE');
END;
All I am trying to see is if the trigger is fired and when I run a script like INSERT INTO enrollment VALUES('12345','1234567');
I have no output, just "1 row inserted" but I do not get "done" making me thing the trigger is not fired.

The only observable effect of your insert trigger is this:
DBMS_OUTPUT.PUT_LINE('DONE');
This just puts a message into the debug buffer. To see the result, you need to enable DBMS Output.
You can do this in SQL Developer via the View menu, choose Dbms Output, and click the green "+" button. Then run your code and you should see the output, if any.
If you use SQL*Plus instead, you would run SET SERVEROUT ON.

Related

Violating foreign key constraint with deferred constraint

I'm trying to set my sql scripts into a transaction to achieve atomicity with my database.
The table structure is (simplified):
CREATE TABLE foo (
id serial NOT NULL,
foo varchar(50) NOT NULL,
CONSTRAINT foo_pk PRIMARY KEY (id)
);
CREATE TABLE access (
id serial NOT NULL,
foo_id int NULL
CONSTRAINT access_pk PRIMARY KEY (id)
);
ALTER TABLE access ADD CONSTRAINT access_foo
FOREIGN KEY (foo_id)
REFERENCES foo (id)
ON DELETE CASCADE
ON UPDATE CASCADE
DEFERRABLE
INITIALLY DEFERRED;
In my code I first declare:
client.query('BEGIN'); (I'm using npm library 'pg') then
insert a row into a table 'foo', then another insert to 'access' with a foo_id from the first insert. After that there is client.query('COMMIT');
All of this is in a try catch, and in the catch is client.query('ROLLBACK'); and the rolling back seems to be working if there is an issue either of the inserts. When everything should be committed I still end up in the catch block for this:
message: "insert or update on table "access" violates foreign key constraint "access_foo""
detail: "Key (foo_id)=(20) is not present in table "foo"."
I thought that deferring constraint would be enough to do this, but I guess I'm wrong. Any help is welcome.
You probably have some issue with the transaction demarcation. I ran a simple test and works wells.
insert into foo (id, foo) values (1, 'Anne');
start transaction;
insert into access (id, foo_id) values (101, 1);
insert into access (id, foo_id) values (107, 7); -- 7 does not exist yet...
insert into foo (id, foo) values (7, 'Ivan'); -- 7 now exists!
commit; -- at this point all is good
See running example at DB Fiddle.

Check if many-to-many relationship exists before insert or delete

I have 3 tables
For example:
Book
id
title
Tag
id
name
BookTag
book_id
tag_id
The goal to disallow having Book without Tag. i.e. when I try insert/delete data I need something to check on database level that Book has at least one Tag through many-to-many. If such validation fails it should throw constaint violation error or some sort of that. How should I implement that? Can it be reached by check constraint or should I create some trigger, if so then how?
please help me. thanks for your help in advance
You can enforce this at the pure database level by adding a foreign key in the book table that points back to a tag (any tag) in the book_tag table. As of now, your database model looks like:
create table book (
id int primary key not null,
title varchar(50)
);
create table tag (
id int primary key not null,
name varchar(50)
);
create table book_tag (
book_id int not null,
book_tag int not null,
primary key (book_id, book_tag)
);
Now, add the extra foreign key that points back to a tag:
alter table book add column a_tag int not null;
alter table book add constraint fk1 foreign key (id, a_tag)
references book_tag (book_id, tag_id) deferrable initially deferred;
Now when you insert a book, it can temporarily not have a tag, but only while the transaction hasn't finished yet. You need to insert a tag before committing. If you don't the constraint will fail, the transaction will rollback, and the insert won't happen.
Note: Please notice that this requires the use of deferrable constraints (look at deferrable initially deferred), something that is part of the SQL Standard but seldomly implemented. Fortunately, PostgreSQL does.
EDIT - Adding an example
Considering the previous modified tables you can try inserting a book without tags (will fail) and with tags (succeeding) as shown below:
insert into tag (id, name) values (10, 'classic');
insert into tag (id, name) values (12, 'action');
insert into tag (id, name) values (13, 'science fiction');
-- begin transaction
insert into book (id, title, a_tag) values (1, 'Moby Dick', 123);
commit; -- fails
-- begin transaction
insert into book (id, title, a_tag) values (2, 'Frankenstein', 456);
insert into book_tag (book_id, book_tag) values (2, 10);
insert into book_tag (book_id, book_tag) values (2, 13);
update book set a_tag = 10;
commit; -- succeeds

Sql (Oracle)-- cannot insert value(could be the constraint factor)

As you can see in the code. three table have its own primary key. "protectmedalno" and "mastermedalno" are the foreign key of the player table.
protectmedalno could not be null. masterdealno could be null. I drop table protector first, then drop master , the last drop player.
There is weak relationship between table player and table master.
There is no problem with inserting the value of protector and master.
But inserting the value into table player, it will occur:
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
I think that it is a problem with constraint.
insert into player values('01','Joe','101','');
insert into player values('02','Elsa','102','201');
insert into protector values('101','Dragon');
insert into protector values('102','Lion');
insert into master values('201','Fairy')
commits;
It could display the protector table and the master table.
But it could not show the player table.
drop table protector;
drop table master;
drop table player;
CREATE TABLE player (
playno NUMBER(2) NOT NULL,
playname VARCHAR2(30) NOT NULL,
protectmedalno CHAR(10) NOT NULL,
mastermedalno CHAR(10)
);
ALTER TABLE player ADD CONSTRAINT play_pk PRIMARY KEY ( playno );
CREATE TABLE protector (
protectmedalno CHAR(3) NOT NULL,
protectname VARCHAR2(30) NOT NULL
);
ALTER TABLE protector ADD CONSTRAINT protector_pk PRIMARY KEY ( protectmedalno );
CREATE TABLE master (
mastermedalno CHAR(3) NOT NULL,
mastername VARCHAR2(30) NOT NULL
);
ALTER TABLE master ADD CONSTRAINT master_pk PRIMARY KEY ( mastermedalno );
ALTER TABLE player
ADD CONSTRAINT player_protector_fk FOREIGN KEY ( protectmedalno )
REFERENCES protector ( protectmedalno );
ALTER TABLE player
ADD CONSTRAINT player_master_fk FOREIGN KEY ( mastermedalno )
REFERENCES master ( mastermedalno );
Since protector and master are the primary tables, you should populate the records there first. Then, insert into player and refer to those records:
insert into protector values('101','Dragon');
insert into protector values('102','Lion');
insert into master values('201','Fairy');
insert into player values('01','Joe','101','201'); -- refer to master
insert into player values('02','Elsa','102','201'); -- refer to master
Note that I edited the inserts into the player table such that both records refer to a record in the master table which actually exists.
You first have to insert protector and master, afterwards insert player since player refers to master and protector and values have to be inside there.
Do otherwise round on delete...
insert into protector values('101','Dragon');
insert into protector values('102','Lion');
insert into master values('201','Fairy');
insert into player values('01','Joe','101','');
insert into player values('02','Elsa','102','201');
If you delete first delete from player, then from protector and master.
To insert data in the player table, you need a record in the protector table. This is because of the foreign key restriction.
When inserting data in a table that has a foreign key(Which in this case of protector vs player, cannot be null), you have to create the foreign record first.
1. insert into protector values('101','Dragon');
2. insert into player values('01','Joe','101','');
3. insert into protector values('102','Lion');
4. insert into master values('201','Fairy');
5. insert into player values('02','Elsa','102','201');
commits;
I hope this helps, happy debugging :)
You are inserting in the wrong order: you must insert the master and protector first so that when you insert the player it can reference them:
insert into protector values('101','Dragon');
insert into protector values('102','Lion');
insert into master values('201','Fairy');
insert into player values('01','Joe','101',NULL);
insert into player values('02','Elsa','102','201');
Edit: '' is not NULL it's an empty String. To insert null use the explicit NULL jeyword.
Always include the columns when doing an insert. You should also use single quotes for only string and date constants.
insert into player(playno, playname, protectmedalno, mastermedalno)
values(1, 'Joe', '101', '');
I don't think the problem is specifically on player, but you should do this for all your inserts and you'll find the problem.
Populating the "parent" tables first (as our colleagues have suggested) is a step towards solving the problem. In order to get the foreign key constraint to work properly, I suggest modifying the DDL code, too.
With your tables in place (using Oracle 12c), we can do the following:
begin
insert into master ( mastermedalno, mastername ) values ('201','Fairy') ;
insert into protector ( protectmedalno, protectname )
values( '101', 'Dragon');
insert into protector ( protectmedalno, protectname )
values( '102', 'Lion');
end ;
/
SQL> select * from master;
MAS MASTERNAME
--- ------------------------------
201 Fairy
SQL> select * from protector ;
PRO PROTECTNAME
--- ------------------------------
101 Dragon
102 Lion
So far so good. When INSERTing into PLAYER, we get:
insert into player ( playno, playname, protectmedalno, mastermedalno )
values('02', 'Elsa', '102', '201');
-- ORA-02291: integrity constraint (...PLAYER_MASTER_FK) violated - parent key not found
Suggestion: use the same datatype for the foreign key and the referenced key. Just drop the PLAYER table (including its constraints), and create it afresh:
drop table player cascade constraints ;
CREATE TABLE player (
playno NUMBER(2) NOT NULL,
playname VARCHAR2(30) NOT NULL,
protectmedalno CHAR(3) NOT NULL, -- changed (was: CHAR(10))
mastermedalno CHAR(3) -- changed (was: CHAR(10))
);
ALTER TABLE player
ADD CONSTRAINT player_protector_fk FOREIGN KEY ( protectmedalno )
REFERENCES protector ( protectmedalno );
ALTER TABLE player
ADD CONSTRAINT player_master_fk FOREIGN KEY ( mastermedalno )
REFERENCES master ( mastermedalno );
Now execute the INSERTs.
begin
insert into player values('01','Joe','101',''); -- original INSERT
insert into player values('02','Elsa','102','201'); -- original INSERT
commit;
end;
/
-- PL/SQL procedure successfully completed.
The tables contain the following data now:
SQL> select * from player ;
PLAYNO PLAYNAME PROTECTMEDALNO MASTERMEDALNO
1 Joe 101 NULL
2 Elsa 102 201
SQL> select * from master ;
MASTERMEDALNO MASTERNAME
201 Fairy
SQL> select * from protector;
PROTECTMEDALNO PROTECTNAME
101 Dragon
102 Lion
Are both foreign key constraints working now? Yes.
SQL> insert into player values('03','Fifi','101','202');
Error starting at line : 1 in command -
insert into player values('03','Fifi','101','202')
Error report -
ORA-02291: integrity constraint (...PLAYER_MASTER_FK) violated - parent key not found
SQL> insert into player values('03','Fifi','103','201');
Error starting at line : 1 in command -
insert into player values('03','Fifi','103','201')
Error report -
ORA-02291: integrity constraint (...PLAYER_PROTECTOR_FK) violated - parent key not found

Trigger in Oracle

I create the following tables:
create table Tasks(
code varchar2(9),
name varchar2(40),
start_date date,
end_date date,
constraint pk_code primary key (code)
);
create table secondary_Tasks(
code varchar2(9),
code_primary varchar2(9),
name varchar2(40),
start_date date,
end_date date,
constraint pk_code2 primary key (code),
constraint fk_code foreign key (code_primary) references Tasks (code)
);
I inserted the following datas:
insert into Tasks values ('001','Task1',to_date('2010-02-15','YYYY-MM-DD'),to_date('2011-04-12','YYYY-MM-DD'));
insert into Tasks values ('002','Task2',to_date('2015-08-11','YYYY-MM-DD'),to_date('2015-09-25','YYYY-MM-DD'));
insert into Tasks values ('003','Task3',to_date('2016-05-09','YYYY-MM-DD'),null);
insert into Tasks values ('004','Task4',to_date('2014-01-23','YYYY-MM-DD'),to_date('2014-06-04','YYYY-MM-DD'));
insert into secondary_Tasks values ('s01','001','Secundary_Task1',to_date('2010-03-16','YYYY-MM-DD'),to_date('2011-05-13','YYYY-MM-DD'));
insert into secondary_Tasks values ('s02','002','Secundary_Task2',to_date('2015-09-12','YYYY-MM-DD'),to_date('2015-10-26','YYYY-MM-DD'));
insert into secondary_Tasks values ('s04','004','Secundary_Task4',to_date('2014-02-24','YYYY-MM-DD'),to_date('2014-07-05','YYYY-MM-DD'));
The question is how to make a trigger that does not allow me to add a secondary task to task '003' because It hasnt finished.
I'm unable to test right now, but I'd try something like
create or replace trigger secondary_tasks_bi
before insert or update on secondary_tasks for each row
declare
v_dummy varchar2(1);
begin
select null
into v_dummy
from tasks
where code = :new.code_primary
and end_date is null;
raise_application_error(-20001, 'Can''t add a secondary task to task ' || :new.code_primary || ' because it hasn''t finished');
exception
when no_data_found then
null;
end;
/
Note that this assumes that code_primary is never null: you probably want to add a NOT NULL clause to it.

Basic primary key / foreign key with constraint, sequence, trigger

Learner here in Oracle 11g. I'm having an issue with INSERTing some rows into two tables that are linked by a primary/foreign key relationship.
Basically I create a sequence to start with 1000 and increment by 1.
Then create a 'STORE' table with a ST_ID column
The ST_ID column is linked to the SEQUENCE with a TRIGGER.
Then I have an 'EMPLOYEE' table that has a EST_ID field that is a foreign key to the ST_ID column in the STORE table.
However, when I tried to insert rows I initially got a error saying EST_ID could not be null. So I created a sequence and trigger for EST_ID and now I'm getting an error saying the foreign key constraint is being violated.
I think that was maybe the wrong thing to do. Do I really want E_ID and EST_ID to be identical and how would I get that to happen? With some kind of trigger?
The actual code:
CREATE SEQUENCE "STORSEQ" MINVALUE 1000 MAXVALUE 9999 INCREMENT BY 1 START WITH 1000 NOCACHE NOORDER
NOCYCLE ;
CREATE TABLE "STORE"
( "ST_ID" CHAR(4) NOT NULL ENABLE,
"STADDR_ID" CHAR(4) NOT NULL ENABLE,
CONSTRAINT "STORE_PK" PRIMARY KEY ("ST_ID") ENABLE
) ;
CREATE TABLE "EMPLOYEE"
( "E_ID" CHAR(8) NOT NULL ENABLE,
"EF_NAME" VARCHAR2(20) NOT NULL ENABLE,
"EL_NAME" VARCHAR2(20) NOT NULL ENABLE,
"EST_ID" CHAR(4) NOT NULL ENABLE,
CONSTRAINT "EMPLOYEE_PK" PRIMARY KEY ("E_ID") ENABLE
) ;
alter table "EMPLOYEE" add CONSTRAINT "EMPLOYEE_CON" foreign key ("EST_ID") references
"STORE" ("ST_ID")
/
CREATE OR REPLACE TRIGGER "BI_STORE"
before insert on "STORE"
for each row
begin
if :NEW."ST_ID" is null then
select "STORSEQ".nextval into :NEW."ST_ID" from dual;
end if;
end;
/
At the moment my INSERT code looks like this:
INSERT INTO STORE
(ST_ID, STADDR_ID)
VALUES
(DEFAULT, DEFAULT);
INSERT INTO EMPLOYEE
(EF_NAME, EL_NAME)
VALUES
('James', 'Smith');
When you try to insert data into table that has foreign key reference, it will not get value for id automatically, you need to pass that value.
You can do this:
declare
v_store_id integer;
begin
INSERT INTO STORE (ST_ID, STADDR_ID) VALUES (DEFAULT, DEFAULT)
RETURNING ST_ID INTO v_Store_id;
INSERT INTO EMPLOYEE (EF_NAME, EL_NAME, EST_ID)
VALUES ('James', 'Smith', v_store_id);
end;
You can also insert id in store id table without trigger using this
declare
v_store_id integer;
begin
INSERT INTO STORE (ST_ID, STADDR_ID) VALUES ("STORSEQ".nextval, DEFAULT)
RETURNING ST_ID INTO v_Store_id;
INSERT INTO EMPLOYEE (EF_NAME, EL_NAME, EST_ID)
VALUES ('James', 'Smith', v_store_id);
end