PL/SQL Statement ignored : missing expression - sql

I have a little DB with 3 tables: account, account_statements and account operations. I need to know:
all credit operations of the period
all debet operations of the perion
a balance on any date
My script is:
CREATE TABLE account
(
id number(10) NOT NULL,
account number(20) NOT NULL,
CONSTRAINT account_id PRIMARY KEY (id)
);
CREATE TABLE account_statements
(
id number(10) NOT NULL,
account_id number(10) NOT NULL,
statement_date date,
inbalance number(20,2),
outbalance number (20,2),
CONSTRAINT statement_id PRIMARY KEY (id),
CONSTRAINT account_statements_foreign_id FOREIGN KEY (account_id) REFERENCES account(id)
);
CREATE TABLE account_operations
(
id number(10) NOT NULL,
account_id number(10) NOT NULL,
operdate date,
summ number(20,2),
opertype char(6),
CONSTRAINT operations_id PRIMARY KEY (id),
CONSTRAINT account_operations_foreign_id FOREIGN KEY (account_id) REFERENCES account(id)
);
CREATE OR REPLACE FUNCTION Get_debet_on_period (
v_startdate IN date,
v_enddate IN date,
v_account IN account.account%TYPE)
RETURN number
IS
v_debet_summ number(20,2);
BEGIN
SELECT SUM(summ) INTO v_debet_summ
FROM account_operations ao,
account a
WHERE ao.operdate between v_startdate AND v_enddate
AND ao.opertype='DEBET'
AND a.account=v_account
AND ao.account_id=a.id;
RETURN v_debet_summ;
END;
CREATE OR REPLACE FUNCTION Get_credit_on_period (
v_startdate IN date,
v_enddate IN date,
v_account IN account.account%TYPE)
RETURN number
IS
v_credit_summ number(20,2);
BEGIN
SELECT SUM(summ) INTO v_credit_summ
FROM account_operations ao,
account a
WHERE ao.operdate between v_startdate AND v_enddate
AND ao.opertype='CREDIT'
AND a.account=v_account
AND ao.account_id=a.id;
RETURN v_credit_summ;
END;
CREATE OR REPLACE FUNCTION Get_balance_on_date (
v_date IN date,
v_account IN account.account%TYPE)
RETURN number
IS
v_balance_summ number(20,2);
v_startdate date;
v_startsumm number;
BEGIN
SELECT MAX(as.statement_date) INTO v_startdate
FROM account_statements as, account a
WHERE a.id=as.account_id
AND a.account=v_account
AND as.statement_date<v_date;
IF v_startdate IS NOT NULL THEN
SELECT as.outbalance INTO v_startsumm
FROM account_statement as, account a
WHERE a.id=as.account_id
AND a.account=v_account
AND as.statement_date=v_statement_date;
v_balance_summ:=v_startsumm+Get_credit_on_period(v_startdate, v_date, v_account)-Get_debet_on_period(v_startdate, v_date, v_account);
ELSE
v_startsumm:=0;
v_balance_summ:=Get_credit_on_period(v_startdate, v_date, v_account)-Get_debet_on_period(v_startdate, v_date, v_account);
END IF;
RETURN v_balance_summ;
END;
I have an error in function Get_balance_on_date:
Errors: FUNCTION GET_BALANCE_ON_DATE Line/Col: 10/4 PL/SQL: SQL
Statement ignored Line/Col: 10/15 PL/SQL: ORA-00936: missing
expression Line/Col: 16/7 PL/SQL: SQL Statement ignored Line/Col:
16/14 PL/SQL: ORA-00936: missing expression

Your function get_balance_on_date does not compile.
You're using table account_statement, however you created table named in plural form account_statements.
You're using reserved keyword AS as an alias "account_statements as". You should change the alias to something different.
You're using undeclared variable "v_statement_date".
Fixed function for you:
CREATE OR REPLACE FUNCTION get_balance_on_date
(
v_date IN DATE
,v_account IN account.account%TYPE
) RETURN NUMBER IS
v_balance_summ NUMBER(20, 2);
v_startdate DATE;
v_startsumm NUMBER;
v_statement_date DATE; -- Remove this if you don't need, created this for function to compile
BEGIN
SELECT MAX(stm.statement_date)
INTO v_startdate
FROM account_statements stm
,account a
WHERE a.id = stm.account_id
AND a.account = v_account
AND stm.statement_date < v_date;
IF v_startdate IS NOT NULL
THEN
SELECT stm.outbalance
INTO v_startsumm
FROM account_statements stm
,account a
WHERE a.id = stm.account_id
AND a.account = v_account
AND stm.statement_date = v_statement_date;
v_balance_summ := v_startsumm +
get_credit_on_period(v_startdate, v_date, v_account) -
get_debet_on_period(v_startdate, v_date, v_account);
ELSE
v_balance_summ := get_credit_on_period(v_startdate, v_date, v_account) -
get_debet_on_period(v_startdate, v_date, v_account);
END IF;
RETURN v_balance_summ;
END;

You are using a reserved keyword as an alias
FROM account_statements as, account a
as has to be changed to something else since it is part of the SQL language.

Related

How to create the correct trigger instead of insert or update? ORACLE. Trigger. pl/sql

Help create a trigger for a non-updatable view.
The tables look like this.
CREATE TABLE Users(id_user INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
surname VARCHAR2(30) NOT NULL,
name VARCHAR2(30) NOT NULL,
patronymic VARCHAR2(30) NULL,
tel_no VARCHAR2(17) NOT NULL,
CONSTRAINT ch_telno_users
CHECK(REGEXP_LIKE(tel_no, '^\+375\(\d{2}\)\d{3}-\d{2}-\d{2}$')),
CONSTRAINT uni_telno_users
UNIQUE(tel_no)
);
CREATE TABLE Software(id_sw INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
name_sw VARCHAR2(40) NOT NULL,
version VARCHAR2(10) NULL,
license_period VARCHAR2(10) NOT NULL,
id_mfr INTEGER NOT NULL,
release_date DATE NOT NULL,
price NUMBER(9,2),
total_amount INTEGER NOT NULL,
CONSTRAINT id_mfr_fk
FOREIGN KEY(id_mfr)
REFERENCES Manufacturer,
);
CREATE SYNONYM sw for Software;
CREATE TABLE Sales(id_sale INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id_user INTEGER NOT NULL,
id_sw INTEGER NOT NULL,
quantity INTEGER NOT NULL,
total_cost NUMBER(9,2),
order_date DATE NOT NULL CHECK (order_date >= to_date('01/01/2019', 'dd/mm/yyyy')),
expiration_date DATE NOT NULL,
CONSTRAINT id_user_fk
FOREIGN KEY(id_user)
REFERENCES Users,
CONSTRAINT id_sw_fk
FOREIGN KEY(id_sw)
REFERENCES Software
);
The view itself looks like this
CREATE OR REPLACE VIEW SalesView AS
SELECT Sales.id_sale,sw.name_sw||' '||sw.version sw_full_name,
Users.surname||' '||Users.name||' '||Users.patronymic user_full_name,
sales.quantity, sales.order_date
FROM sw INNER JOIN (Users INNER JOIN Sales ON Sales.id_user = Users.id_user) ON sw.id_sw = sales.id_sw;
I tried to create a trigger like this. But for some reason it is not created.(
Error at line 86: PL/SQL: SQL Statement ignored
Error at line 94: PL/SQL: ORA-00917: missing comma)
it seems to me that I went the hard way. help fix this code.
CREATE OR REPLACE TRIGGER sales_view_instead_of_trig INSTEAD OF
UPDATE OR INSERT ON salesview
FOR EACH ROW
DECLARE
sw_new sw.id_sw%TYPE;
user_new Users2.id_user%TYPE;
check_excep_sw VARCHAR2(51);
check_excep_user VARCHAR2(92);
BEGIN
IF updating THEN --* update
IF :new.sw_full_name != :old.sw_full_name THEN
SELECT
sw.name_sw||' '||sw.version
INTO check_excep_sw
FROM
sw
WHERE
sw.name_sw||' '||sw.version = :new.sw_full_name;
SELECT
id_sw
INTO sw_new
FROM
sw
WHERE
sw.name_sw||' '||sw.version = :new.sw_full_name;
ELSE
SELECT
id_sw
INTO sw_new
FROM
sw
WHERE
sw.name_sw||' '||sw.version = :old.sw_full_name;
END IF;
IF :new.user_full_name != :old.user_full_name THEN
SELECT
Users2.surname||' '||Users2.name||' '||Users2.patronymic
INTO check_excep_user
FROM
Users2
WHERE
Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
SELECT
id_user
INTO user_new
FROM
Users2
WHERE
Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
ELSE
SELECT
id_user
INTO user_new
FROM
Users2
WHERE
Users2.surname||' '||Users2.name||' '||Users2.patronymic = :old.user_full_name;
END IF;
UPDATE sales
SET
id_user = user_new,
id_sw = sw_new,
quantity = :new.quantity,
order_date = :new.order_date,
expiration_date = :new.expiration_date,
total_cost = :new.total_cost
WHERE
id_sale = :old.id_sale;
END IF;
IF inserting THEN --* insert
SELECT sw.name_sw||' '||sw.version INTO check_excep_sw
FROM sw
WHERE sw.name_sw||' '||sw.version = :new.sw_full_name;
SELECT id_sw
INTO sw_new
FROM sw
WHERE sw.name_sw||' '||sw.version = :new.sw_full_name;
SELECT
Users2.surname||' '||Users2.name||' '||Users2.patronymic
INTO check_excep_user
FROM
Users2
WHERE
Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
SELECT id_user
INTO user_new
FROM Users2
WHERE Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
INSERT INTO sales(
id_user,
id_sw,
quantity,
order_date,
expiration_date,
total_cost
) VALUES (
id_user = user_new,
id_sw = sw_new,
quantity = :new.quantity,
order_date = :new.order_date,
expiration_date = :new.expiration_date,
total_cost = :new.total_cost
);
END IF;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('-------------------------------------------');
dbms_output.put_line('| ERROR! |');
dbms_output.put_line('-------------------------------------------');
END;
/
You say that you're getting an error creating the trigger. But if I try to replicate your problem, I can't because the definition of the Software table is invalid. First, the foreign key references a table Manufacturer that doesn't exist in your example. And second, the foreign key constraint definition has an extra comma at the end.
If I remove the foreign key constraint entirely
CREATE TABLE Software(id_sw INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
name_sw VARCHAR2(40) NOT NULL,
version VARCHAR2(10) NULL,
license_period VARCHAR2(10) NOT NULL,
id_mfr INTEGER NOT NULL,
release_date DATE NOT NULL,
price NUMBER(9,2),
total_amount INTEGER NOT NULL
);
That allows the script to get as far as trying to create the trigger. At that point, you'll get errors which include (but are not limited to) the ORA-00917 error you mentioned.
Errors: TRIGGER SALES_VIEW_INSTEAD_OF_TRIG
Line/Col: 62/27 PLS-00049: bad bind variable 'NEW.EXPIRATION_DATE'
Line/Col: 63/22 PLS-00049: bad bind variable 'NEW.TOTAL_COST'
Line/Col: 86/5 PL/SQL: SQL Statement ignored
Line/Col: 94/17 PL/SQL: ORA-00917: missing comma
Line/Col: 98/27 PLS-00049: bad bind variable 'NEW.EXPIRATION_DATE'
Line/Col: 99/22 PLS-00049: bad bind variable 'NEW.TOTAL_COST'
The PLS-00049: bad bind variable errors occur because there are no columns called expiration_date or total_cost in the definition of the view. Thus, there is no such thing as :new.expiration_date or :new.total_cost because DML against the view cannot specify values for those columns. I have no idea how you want to handle that-- either omit the values, provide default values, read the values from somewhere else, add the columns to the view and provide them in the DML against the view, etc. The fact that expiration_date is defined as not null seemingly limits your options.
If you look at line 86 of your trigger, you'll see this statement
INSERT INTO sales(
id_user,
id_sw,
quantity,
order_date,
expiration_date,
total_cost
) VALUES (
id_user = user_new,
id_sw = sw_new,
quantity = :new.quantity,
order_date = :new.order_date,
expiration_date = :new.expiration_date,
total_cost = :new.total_cost
);
which isn't syntactically valid.
INSERT INTO sales(
id_user,
id_sw,
quantity,
order_date,
expiration_date,
total_cost
) VALUES (
user_new,
sw_new,
:new.quantity,
:new.order_date,
:new.expiration_date,
:new.total_cost
);
would be valid if :new.expiration_date and :new.total_cost were valid identifiers. But, as mentioned above, they're not valid. And it isn't obvious what you would want the values to be on insert.
Here is a liveSQL example that you can play with to make sure what you have is a reproducible test case.
There are missing colons on VALUES of "INSERT INTO sales" statement:
id_user = :user_new,
id_sw = :sw_new,

I have a problem with triggers i sql Oracle

(zaposlenik means emplpoyee, godisnji_odmor means vacation)
So I want to make a trigger that triggers if an employee(here is called zaposlenik) has more than 21 days of vacation after an insert in year 2020. An employee can have multiple vacations(like 2 days, 10 days...) one to many relation between tables zaposlenik(employee) and godisnji_odmor(vacation). My problem here is that I don't know how to get that zaposlenik_id from the insert on which the trigger tiggers so I can sum up the vacation days of that employee on the ID = zaposlenik_id.
Here are my tables and trigger.
CREATE TABLE zaposlenik
(
zaposlenik_id INTEGER CONSTRAINT zaposlenik_pk PRIMARY KEY,
posao VARCHAR(30) NOT NULL,
ime VARCHAR(30) NOT NULL,
prezime VARCHAR(30) NOT NULL,
broj_tel INTEGER NOT NULL,
email VARCHAR(50) NOT NULL,
adresa VARCHAR(100) NOT NULL,
mjesecni_iznos_place FLOAT NOT NULL,
IBAN VARCHAR(34) NOT NULL,
budzet FLOAT,
parking_mjesto_id VARCHAR(5) CONSTRAINT zaposlenik_parking_mjesto_fk REFERENCES
parking_mjesto(etaza_i_br_mjesta),
zaposlenik_id_2 INTEGER CONSTRAINT zaposlenik_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE TABLE godisnji_odmor
(
godisnji_odmor_id INTEGER CONSTRAINT godisnji_odmor_pk PRIMARY KEY,
pocetak DATE NOT NULL,
kraj DATE NOT NULL,
zaposlenik_id INTEGER NOT NULL CONSTRAINT godisnji_odmor_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020' AND zaposlenik_id = v_id;
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
You need to use the :new for this as follows:
replace
SELECT zaposlenik_id INTO v_id FROM INSERTED;
with
v_id := :new.zaposlenik_id;
In short, You can have your code without v_id variable as follows:
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
--v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
--SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020'
AND zaposlenik_id = :new.zaposlenik_id; -- see the usage of :new here
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/

ORA-06553: PLS-801:Internal Error [55018]

I want to make a function call like SELECT URUN_GETIR('test1') FROM DUAL; but i got ORA-06553: PLS-801: Internal Error [55018].
I tried ORA-06553: PLS-801: internal error [55018] when testing function returning ROWTYPE this like for ex URUN_GETIR('test1').KULUSERNAME but getting same error. It didn't work for me.Thanks in advance.
My db table :
My plsql function code :
create or replace FUNCTION URUN_GETIR(KULADI VARCHAR2)
RETURN URUN%ROWTYPE
AS
URUN_TABLO URUN%ROWTYPE;
BEGIN
SELECT * INTO URUN_TABLO FROM URUN ur WHERE ur.kulusername = KULADI;
RETURN URUN_TABLO;
END;
You can achieve your goal with using table functions. Other than that you can not call directly your function like you wanted. Here is another choice for you Use Stackoverflow Wisely
CREATE TABLE URUN
(
CREATED_BY VARCHAR2 (50 CHAR),
CREATED_DATE DATE,
UPDATED_BY VARCHAR2 (50 CHAR),
KULUSERNAME VARCHAR2 (50 CHAR),
ID NUMBER (10)
);
INSERT INTO URUN (CREATED_BY, CREATED_DATE, UPDATED_BY, KULUSERNAME, ID) VALUES('TEST1',TO_DATE('19000101','YYYYMMDD') ,'TTEST1','USER1',1);COMMIT;
INSERT INTO URUN (CREATED_BY, CREATED_DATE, UPDATED_BY, KULUSERNAME, ID) VALUES('TEST2',TO_DATE('19000102','YYYYMMDD') ,'TTEST2','USER2',2);COMMIT;
INSERT INTO URUN (CREATED_BY, CREATED_DATE, UPDATED_BY, KULUSERNAME, ID) VALUES('TEST3',TO_DATE('19000103','YYYYMMDD') ,'TTEST3','USER3',3);COMMIT;
INSERT INTO URUN (CREATED_BY, CREATED_DATE, UPDATED_BY, KULUSERNAME, ID) VALUES('TEST4',TO_DATE('19000104','YYYYMMDD') ,'TTEST4','USER4',4);COMMIT;
CREATE OR REPLACE TYPE URUN_OBJ AS OBJECT
(
CREATED_BY VARCHAR2 (50 CHAR),
CREATED_DATE DATE,
UPDATED_BY VARCHAR2 (50 CHAR),
KULUSERNAME VARCHAR2 (50 CHAR),
ID NUMBER (10)
);
CREATE OR REPLACE TYPE URUN_OBJ_TAB AS TABLE OF URUN_OBJ;
CREATE OR REPLACE FUNCTION URUN_GETIR (KULADI IN VARCHAR2)
RETURN URUN_OBJ_TAB
PIPELINED
AS
REC_OBJ URUN_OBJ;
CURSOR DATA
IS
SELECT *
FROM URUN UR
WHERE UR.KULUSERNAME = KULADI;
BEGIN
FOR REC IN DATA
LOOP
REC_OBJ :=
URUN_OBJ (REC.CREATED_BY,
REC.CREATED_DATE,
REC.UPDATED_BY,
REC.KULUSERNAME,
REC.ID);
PIPE ROW (REC_OBJ);
END LOOP;
RETURN;
END;
/
SELECT * FROM TABLE(URUN_GETIR('USER1'));
CREATED_BY CREATED_DATE UPDATED_BY KULUSERNAME ID
TEST1 1.01.1900 TTEST1 USER1 1

How to create an Oracle audit trigger?

CREATE OR REPLACE TRIGGER EVALUATION
BEFORE INSERT OR UPDATE OR DELETE ON BOOKING
FOR EACH ROW
DECLARE
BEGIN
SELECT BOOKING_EVALUATION FROM BOOKING WHERE BOOKING_EVALUATION > 2;
SELECT BOOKING_EVALUATION INTO EVALUATIONAUDIT FROM BOOKING;
IF INSERTING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:NEW.VOYAGES_ID,:NEW.CUSTOMER_NAME,:NEW.START_DATE,:NEW.SHIP_NAME,:NEW.BOOKING_EVALUATION);
END IF;
IF UPDATING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION);
END IF;
IF DELETING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION);
END IF;
END;
This is my audit table:
desc evaluationaudit;
Name Null? Type
----------------------------------------- -------- -----------------------
AUDITT_ID NOT NULL NUMBER(10)
VOYAGES_ID NOT NULL NUMBER(10)
CUSTOMER_NAME NOT NULL VARCHAR2(20)
START_DATE NOT NULL DATE
SHIP_NAME NOT NULL VARCHAR2(20)
BOOKING_EVALUATION NOT NULL NUMBER(20)
and this is my show error output:
SQL> SHOW ERRORS;
Errors for TRIGGER EVALUATION:
LINE/COL ERROR
-------- -------------------------------------------------------------
12/24 PLS-00049: bad bind variable 'NEW.CUSTOMER_NAME'
12/43 PLS-00049: bad bind variable 'NEW.START_DATE'
12/59 PLS-00049: bad bind variable 'NEW.SHIP_NAME'
18/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME'
18/43 PLS-00049: bad bind variable 'OLD.START_DATE'
18/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME'
24/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME'
24/43 PLS-00049: bad bind variable 'OLD.START_DATE'
24/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME'
I wanted to add to the audit table. If a customer gives a poor evaluation of 2 or less, the details of their voyage (customer_name, name and date of the cruise, ship name and evaluation) but I'm getting error
Warning: Trigger created with compilation errors.
And the audit table is empty, showing no rows selected. I can't seem to find the problem where I am going wrong.
Maybe the following code snippets will help you. Suppose we have 2 tables: BOOKING, and EVALUATION_AUDIT (everything written in uppercase letters is taken from the code in your question).
Tables
create table booking (
VOYAGES_ID number primary key
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, START_DATE DATE NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
create table evaluationaudit (
AUDITT_ID number generated always as identity start with 5000 primary key
, trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!'
, VOYAGES_ID NUMBER(10) NOT NULL
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, START_DATE DATE NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
Trigger
-- "updating" and "deleting" code omitted for clarity
CREATE OR REPLACE TRIGGER EVALUATION_trigger
BEFORE INSERT OR UPDATE OR DELETE ON BOOKING
FOR EACH ROW
BEGIN
case
when INSERTING then
if :new.booking_evaluation <= 2 then
INSERT INTO EVALUATIONAUDIT
( trg_cond_pred,
VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
VALUES (
'INSERTING'
, :NEW.VOYAGES_ID
, :NEW.CUSTOMER_NAME
, :NEW.START_DATE
, :NEW.SHIP_NAME
, :NEW.BOOKING_EVALUATION
);
end if ;
end case ;
END ;
/
Testing
One of your requirements (in your question) is:
I wanted to add to the audit table. If a customer gives a poor
evaluation of 2 or less, the details of their voyage (customer_name,
name and date of the cruise, ship name and evaluation)
delete from evaluationaudit ;
delete from booking ;
-- booking_evaluation greater than 2 -> no entry in audit table
insert into booking
( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
values ( 1111, 'customer1', date '2018-05-24', 'ship1', 9999 ) ;
select * from evaluationaudit ;
-- no rows selected
-- booking_evalution = 2 -> insert a row into the audit table
insert into booking
( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
values ( 1112, 'customer1', date '2018-05-24', 'ship1', 2 ) ;
select * from evaluationaudit ;
AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME START_DATE SHIP_NAME BOOKING_EVALUATION
5000 INSERTING 1112 customer1 24-MAY-18 ship1 2
__Update__
If - as you wrote in your comment - you need to pull in some more data from other tables, maybe you want to try the following approach: keep the trigger code rather brief, and use it to call a procedure for the more complicated stuff eg
Tables
create table evaluationaudit (
AUDITT_ID number generated always as identity start with 7000 primary key
, trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!'
, VOYAGES_ID NUMBER NOT NULL
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
create table ships ( name varchar2( 64 ), id number unique ) ;
create table customers ( name varchar2( 64 ), id number unique ) ;
insert into ships ( name, id ) values ( 'ship1', 501 );
insert into ships ( name, id ) values ( 'ship2', 502 );
insert into ships ( name, id ) values ( 'ship3', 503 );
insert into customers ( name, id ) values ( 'customer1', 771 ) ;
insert into customers ( name, id ) values ( 'customer2', 772 ) ;
insert into customers ( name, id ) values ( 'customer3', 773 ) ;
create table bookings (
id number generated always as identity start with 5000 primary key
, voyagesid number
, shipid number
, customerid number
, evaluation number
, bookingdate date
);
Trigger
create or replace trigger bookingeval
before insert on bookings
for each row
when ( new.evaluation <= 2 ) -- Use NEW without colon here! ( see documentation )
begin
auditproc( :new.voyagesid, :new.customerid, :new.shipid, :new.evaluation ) ;
end ;
/
Procedure
create or replace procedure auditproc (
voyagesid_ number
, customerid_ number
, shipid_ number
, evaluation_ number
)
as
customername varchar2( 64 ) := '' ;
shipname varchar2( 64 ) := '' ;
begin
-- need to find the customername and shipname before INSERT
select name into customername from customers where id = customerid_ ;
select name into shipname from ships where id = shipid_ ;
insert into evaluationaudit
( trg_cond_pred,
voyages_id, customer_name, ship_name, booking_evaluation )
values (
'INSERTING'
, voyagesid_
, customername
, shipname
, evaluation_
);
end ;
/
Testing
-- evaluation > 2 -> no INSERT into evaluationaudit
insert into bookings
( voyagesid, customerid, shipid, evaluation, bookingdate )
values ( 1111, 771, 501, 9999, sysdate ) ;
select * from evaluationaudit ;
-- no rows selected
-- evaluation = 2
-- -> trigger calling audit procedure -> inserts into evaluationaudit
insert into bookings
( voyagesid, customerid, shipid, evaluation, bookingdate )
values ( 1112, 772, 502, 2, sysdate ) ;
select * from evaluationaudit ;
AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME SHIP_NAME BOOKING_EVALUATION
7000 INSERTING 1112 customer2 ship2 2

Find if two dates are more than 6 months apart with Oracle

so for my university coursework, i have to create an oracle database for an airline. (this is my first time working with oracle)
part of my requirement is to identify whether pilots are fit to fly. to do this, my employee and pilot_test tables are set up as follows :
CREATE TABLE PILOT_TEST (
TEST_ID NUMBER(11) PRIMARY KEY,
TEST_DATE DATE NOT NULL,
EMPLOYEE_ID NUMBER(11) NOT NULL
);
CREATE TABLE EMPLOYEE (
EMPLOYEE_ID NUMBER(5) PRIMARY KEY,
FIRST_NAME VARCHAR2(15) NOT NULL,
LAST_NAME VARCHAR2(20) NOT NULL,
MIDDLE_NAME VARCHAR2(15) NULL,
POSITION VARCHAR2(13) NOT NULL, /* pilot, booking clerk, maintenance staff*/
EMPLOYED_FROM DATE NOT NULL,
EMPLOYED_TO DATE NOT NULL,
TELEPHONE NUMBER(11) NOT NULL,
EMAIL VARCHAR2(40) NULL,
ADDRESS_LINE1 VARCHAR2(20) NOT NULL,
ADDRESS_LINE2 VARCHAR2(20) NULL,
TOWN VARCHAR2(20) NOT NULL,
POST_CODE VARCHAR2(9) NOT NULL,
SALARY NUMBER(6,2) NOT NULL,
STATUS VARCHAR2(16) NOT NULL /*working, on leave, contract expired, fired*/
);
whenever a new flight is added to the system, i use a trigger to check whether the staff allocated to fly the plane is actually a pilot.
into this trigger i would like to check that if he IS a pilot, then whether his last TEST_DATE is less than 6 months old or not. any ideas on how this could be done?
my current trigger looks like this
CREATE OR REPLACE TRIGGER CHECK_PILOT_ALLOCATION
BEFORE INSERT ON FLIGHT
FOR EACH ROW
DECLARE
TEMP_POSITION VARCHAR2(13);
BEGIN
SELECT POSITION INTO TEMP_POSITION
FROM EMPLOYEE
WHERE EMPLOYEE_ID = :NEW.PILOT_ID;
IF UPPER (TEMP_POSITION) <> 'PILOT' THEN
RAISE_APPLICATION_ERROR (-20001, 'Assigned employee is not a pilot');
ELSIF
END IF;
END;
/
try
DECLARE
TEMP_POSITION VARCHAR2(13);
TEMP_TESTDATE DATE;
BEGIN
SELECT E.POSITION,
(SELECT MAX (TEST_DATE)
FROM PILOT_TEST PT
WHERE PT.TEST_DATE <= SYSDATE
AND PT.EMPLOYEE_ID = E.EMPLOYEE_ID) TEST_DATE
INTO TEMP_POSITION,
TEMP_TESTDATE
FROM EMPLOYEE E
WHERE E.EMPLOYEE_ID = :NEW.PILOT_ID;
IF UPPER (TEMP_POSITION) <> 'PILOT'
THEN
RAISE_APPLICATION_ERROR (-20001, 'Assigned employee is not a pilot');
END IF;
IF ( SYSDATE - TEMP_TESTDATE ) > 182
THEN
RAISE_APPLICATION_ERROR (-20001, 'Assigned pilot has not been tested in the last 6 months');
END IF;
END;
I would just outer join the two tables together and check if they're a pilot. Checking the cert date can be done with a case statement. Note the existence of the built-in ADD_MONTHS function to do the date arithmetic for you. I like to do as much as I can within the select statement. Finally, I would check for the existence of the employee as well with the exception handler. Generally speaking, you should always have exception handlers for your SQL. I've seen too many cases where someone assumed that since a record should be there they didn't handle the exception, and of course it eventually did happen. At that point you get Oracle generating the exception in an ungraceful way.
BEGIN
SELECT e.employee_id, NVL(UPPER(e.position),'xx')
, CASE WHEN ADD_MONTHS(MAX(test_date), 6) > SYSDATE THEN 1 ELSE 0 END date_ck
INTO v_id, v_pos, v_date_ck
FROM employee e LEFT JOIN pilot_test p ON e.employee_id = p.employee_id
WHERE e.employee_id = :new.pilot_id
GROUP BY e.employee_id, NVL(UPPER(e.position),'xx');
IF v_pos <> 'PILOT' THEN
RAISE_APPLICATION_ERROR (-20001, 'Not a pilot');
ELSIF v_date_ck = 0 THEN
RAISE_APPLICATION_ERROR (-20002, 'Not recently tested');
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20003, 'Not an employee');
END;