Why is my PL/SQL trigger raising an "Error(2,2): PL/SQL: Statement ignored" and an "Error(2,5): PLS-00204: function or pseudo-column 'EXISTS' " error? - sql

I am attempting to write a trigger which prevents the insertion into my TA (teaching assistant) table if the student is already taking the class for which they're attempting to become a TA. From what I understand the best way to do this (other than a unique constraint, this has to be a trigger) is through the use of rollback, and I'm also using raiserror.
create or replace trigger CheckTA
after insert
on TA for each row
begin
if exists (select * from Enrolls where Student_ID = :new.Student_ID and Course_ID = :new.Course_ID) then
rollback;
raiserror('TA enrolled in course as student');
end if;
end;
Upon creating the trigger, I'm met with the following errors:
Error(2,2): PL/SQL: Statement ignored
Error(2,5): PLS-00204: function or pseudo-column 'EXISTS' may be used inside a SQL statement only
The trigger still gets created, but it doesn't trigger when improper values are inserted into TA.
Tables:
TA
Create table TA
(
Student_ID int references Student(Student_ID),
Course_ID int references Course(Course_ID),
Courses_taught varchar2(250),
Office_hours varchar2(25)
);
Student
create table Student
(
Student_ID int primary key,
Name varchar2(50),
Start_began int,
Semester_began varchar2(50),
GPA int,
Degree_status varchar2(25),
Degree_type varchar2(50),
Courses_Taken varchar2(250),
JI_list varchar2(250),
Profile_status varchar2(50)
);
Course
create table Course
(
Course_ID int primary key,
Course_Name varchar2(25)
);
Enrolls
create table Enrolls
(
Student_ID int references Student(Student_ID),
Course_ID int references Course(Course_ID)
);
Stored Procedure
This is the SP I use to insert TA values that should trigger the trigger. It doesn't raise errors but I'll put it here in case it's relevant.
create or replace procedure addTA (s_id int, c_id int, course varchar2)
as
begin
insert into TA (Student_ID,Course_ID,Courses_taught)
values (s_id,c_id,course);
end;
Honestly I'm not quite sure what these errors mean, let alone what's causing them, but still I've tried messing with the syntax of raiserror as well as replacing it with raise_application_error with no luck. If anyone could help me resolve or at least explain these errors it would be greatly appreciated. And of course if there is anything obviously wrong with my trigger please let me know.

The error says "function or pseudo-column 'EXISTS' may be used inside a SQL statement only", meaning that you only can use EXISTS inside a SQL statment, while you are using in in a PL/SQL statement.
If you need to check for the existence of some records, you can try something like this:
vNum number;
...
select count(1)
into vNumber
from Enrolls
where Student_ID = ...
if vNumber > 0 then
...
else
...
end if;

Related

How to fix the trigger with check compiler log error?

Please, help!
I have trigger:
CREATE TRIGGER check_reservation BEFORE INSERT ON order
FOR EACH ROW
DECLARE mistake INTEGER;
BEGIN
SELECT count(*) INTO mistake FROM order join reserving
on id_order = reserving.order_id_order
WHERE reserving.room_num_room=:new.room_num_room
AND (order.reservation_from < :new.reservation_from AND :new.reservation_from < order.reservation_to) OR
(order.reservation_from < :new.reservation_from AND :new.reservation_to < order.reservation_to) OR
(:new.reservation_from <= order.reservation_from AND order.reservation_to <= :new.reservation_to);
IF mistake>0 THEN
raise_application_error(-20001,'reservation already exists');
END IF;
END;
The idea of the trigger is not to allow make a reservation on already booked room. When I run it I had check compiler log error message. How can I change trigger?
I have following tables:
CREATE TABLE order (
id_order CHAR(100) NOT NULL,
reservation_from DATE NOT NULL,
reservation_to DATE NOT NULL,
);
ALTER TABLE order ADD CONSTRAINT order_pk PRIMARY KEY ( id_order );
CREATE TABLE room (
num_room CHAR(100) NOT NULL,
type VARCHAR2(100) NOT NULL,
);
ALTER TABLE room ADD CONSTRAINT room_pk PRIMARY KEY ( num_room );
CREATE TABLE reserving (
room_num_room CHAR(100) NOT NULL,
order_id_order CHAR(100) NOT NULL
);
ALTER TABLE reserving ADD CONSTRAINT reserving_pk PRIMARY KEY ( room_num_room,
order_id_order );
ALTER TABLE reserving
ADD CONSTRAINT reserving_order_fk FOREIGN KEY ( order_id_order )
REFERENCES order ( id_order );
ALTER TABLE reserving
ADD CONSTRAINT reserving_room_fk FOREIGN KEY ( room_num_room )
REFERENCES room ( num_room );
I tried recreating the trigger with the statements above. The statements failed with several errors, it looks as if they were not tested before posting them as question. Please take some time posting a quality question.
Example:
CREATE TABLE room (
num_room CHAR(100) NOT NULL,
type VARCHAR2(100) NOT NULL, << this trailing comma makes this statement fail.
);
After fixing all errors I ran the "CREATE TRIGGER" and it errored out with
PLS-00049: bad bind variable 'NEW.ROOM_NUM_ROOM'
That is because the column ROOM_NUM_ROOM does not exist in the "ORDER" table.
If I remove the reference to 'NEW.ROOM_NUM_ROOM' the trigger compiles successfully.
However, as gsalem pointed out, this will not work because it will raise a mutating table error. In the trigger code you cannot execute DML referencing the table that the trigger is on. There is plenty of documentation on how to avoid mutating table errors.

Is it possible to create a cross relationship constraint in postgresql? [duplicate]

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

sql error 04098 invalid trigger

I need some assistance with troubleshooting the trigger that I'm trying to create/use for logging updates and inserts on a table.
I'm using a customers_history table to track all the changes being made on the customers table.
CREATE TABLE customers (
custID INTEGER PRIMARY KEY,
custFName VARCHAR2(30),
custLName VARCHAR2(30),
custState CHAR(20),
custZip NUMBER(5)
);
-- log inserts and updates on customers table
CREATE TABLE customers_history (
histID INTEGER PRIMARY KEY,
cID INTEGER,
cFName VARCHAR2(30),
cLName VARCHAR2(30),
cState CHAR(20),
cZip NUMBER(5)
);
Also, for the histID I'm using a sequence to auto increment the histID on customers_history table.
CREATE SEQUENCE ch_seq
MINVALUE 1
START WITH 1
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
INSERT INTO customers_history(histID,cID,cFName,cLName,cState,cZip)
VALUES(ch_seq.nextval,:NEW.custID,:NEW.custFName,:NEW.custLName,
:NEW.custState,:NEW.custZip);
END;
/
I have been inserting two rows on customers prior to creating the trigger, and they work fine. After I create the trigger, it will not allow me to insert anymore rows on customers and it also throws the ORA-04098: trigger 'SYSTEM.AUDIT_CUSTOMERS' is invalid and failed re-validation 04098. 00000 - "trigger '%s.%s' is invalid and failed re-validation" error message.
I've tried to see if there is any code errors using select * from user_errors where type = 'TRIGGER' and name = 'audit_customers'; and it returned no lines. Not sure if that helps or not. Thanks.
you have created your trigger for multiple DML operations (Insert and Update) so you need to specipy the DML operation by using IF INSERTING THEN....END IF; and IF UPDATING THEN....END IF; for example:
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO customers_history(histID,cID,cFName,cLName,cState,cZip)
VALUES(ch_seq.nextval,:NEW.custID,:NEW.custFName,:NEW.custLName,
:NEW.custState,:NEW.custZip);
END IF;
END;
/
I went through and changed a couple things and it seems to work for both insert and update operations. I dropped the tables, sequence, and trigger, then did the tried the following code and it works now. Thank you all for your time and inputs!
CREATE TABLE customers (
custID INTEGER,
custFName VARCHAR2(30),
custLName VARCHAR2(30),
custState CHAR(20),
custZip NUMBER(5)
);
CREATE TABLE customers_history (
histID INTEGER,
cID INTEGER,
cFName VARCHAR2(30),
cLName VARCHAR2(30),
cState CHAR(20),
cZip NUMBER(5)
);
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
INSERT INTO customers_history(
histID,
cID,
cFName,
cLName,
cState,
cZip
)
VALUES(
ch_seq.nextval,
:new.custID,
:new.custFName,
:new.custLName,
:new.custState,
:new.custZip
);
END;
/
I used the same sequence code as before, added a couple of rows, and it worked. Then I altered the two tables, by adding the primary keys,
ALTER TABLE customers ADD PRIMARY KEY(custID);
ALTER TABLE customers_history ADD PRIMARY KEY(histID);
inserted a couple other columns, modified a few rows, and it still worked. I'm a happy camper, although I'm not certain what or how it actually got fixed.

SQL trigger to limit the number of users

I'm still learning how to use SQL, and I need some help creating this database.
I've created the following tables:
create table Worker
(Num_Worker number(20) PRIMARY KEY,
Name varchar2(30),
ID number(8),
Password varchar2(20));
create table Area
(Code_Area number(10) PRIMARY KEY,
Name varchar2(30),
Current_workers number(2)
Max_workers number(2));
create table Timetable
(ID_Timetable number(10) PRIMARY KEY,
Time_Entrance date,
Time_Exit date,
Code_Area number(10),
Num_Worker number(20),
Foreign Key (Num_Worker) references Worker(Num_Worker),
Foreign Key (Code_Area) references Area(Code_Area));
Supposedly, each worker can choose a area to work at, but each area has a limit or workers at the same time.
What would happen is a worker would create a new timetable, but before that timetable is created it should check the area that he chose to check if the "Current_workers" is the same value as "Max_workers", and if it is it shouldn't let it happen.
I've been trying to create a trigger for that, but I've had no luck in finding the correct syntax for it, and I'm not sure how to do it either, or if there's a better way to do it than with a trigger.
This is all I've done so far:
create trigger limit_worker_per_zone
before insert
on Timetable
for each row
as
BEGIN
if (select Current_Workers from Area) >= (select Max_workers from Area) <-Not sure...
BEGIN
???
END
END
I'd really appreciate if you can help me in this. I'll still be looking for more info myself meanwhile, but the more help the better.
CREATE TRIGGER limit_worker_per_zone
BEFORE INSERT
ON Timetable
FOR EACH ROW
DECLARE
v_total NUMBER;
BEGIN
BEGIN
SELECT max_workers - current_workers
INTO v_total
FROM Area
WHERE Code_Area = :NEW.Code_Area;
exception when no_data_found then
v_total := 1;
END;
IF v_total <= 0 THEN
raise_application_error(990000, 'workers full for this area');
END IF;
END limit_worker_per_zone;
Try this as a starting point:
CREATE OR REPLACE TRIGGER LIMIT_WORKER_PER_ZONE
BEFORE INSERT ON TIMETABLE
FOR EACH ROW
AS
nCurrent_workers NUMBER;
nMax_workers NUMBER;
BEGIN
SELECT CURRENT_WORKERS,
MAX_WORKERS
INTO nCurrent_workers,
nMax_workers
FROM AREA
WHERE CODE_AREA = :NEW.CODE_AREA;
IF nCurrent_workers = nMax_workers THEN
NULL; -- add code here to do the appropriate thing when the area is already full
ELSE
NULL; -- add code here to to the appropriate thing if the area is not full
END IF;
END LIMIT_WORKER_PER_ZONE;
I'll leave it to you to flesh this out.
Share and enjoy.

Unable to create trigger in Oracle/iSQL

I made a table student, in which there is an attribute evaluation.
Now i am making a trigger in which i want that if i insert a value of zero in evaluation then a trigger should fire and store some values in audit_table.
here is my code
Create table Student ( Student_Id Number(8,2), Student_Name Varchar2(50), Gender Varchar(8), Telephone_No Number(15), Location Varchar2(200), Education Varchar2(100), Company_name Varchar2(200), No_of_attempt Number(2), Offering_Id Number(3) , EVALUATION NUMBER(2), Primary Key (Student_Id), Constraint fk_Oid Foreign Key (Offering_Id) References Course(Offering_Id) );
And now the audit table code
CREATE TABLE AUDIT_TABLE ( STUD_NAME VARCHAR2(100), COURSE_NAME
VARCHAR2(100), INSTRUCTOR_NAME VARCHAR2(200), EVALUATION NUMBER (2)
);
Now my main question is this my trigger not working
create trigger tr_student
after insert or update on student
for each row
declare
s_name student.student_name%type;
s_eval student.evaluation%type;
s_offr_id student.offering_id%type;
s_course_name student.student_name%type;
s_instr student.student_name%type;
begin
if evaluation == 0;
s_offr_id =(select offering_id from student where evaluation==0);
s_eval =0;
s_name =(select student_name from student where evaluation==0);
s_course_name =(select course_name from course where offering_id==s_offr_id);
s_instr=(select name from instructor where offering_id==s_offr_id);
insert into AUDIT_TABLE values(s_name,s_course,s_instr,s_eval);
end;
Is there some problem with my trigger?
This is not possible:
s_name =(select student_name from student where evaluation==0);
Change it to
select student_name into s_name from student where evaluation = 0;
Note there are two problems. First, you need into to select a value into a variable. Second, the comparison operator in SQL is only a single =.
The same goes for
if evaluation == 0;
..
end;
It should be
if :new.evaluation = 0 then
...
end if;
So there, the you forgot the then and end if, the comparison operator is wrong and you need :new.fieldname to get the value of the field, instead of a local variable.
The assignment operator in PLSQL is :=, so
s_eval =0;
should be changed to
s_eval := 0;
It seems you need to grab the schoolbook again and do some more reading. :)