Using Oracle database , I have created a table :
CREATE TABLE PLAYER (
PID number NOT NULL,
FIRSTNAME varchar2(100) NOT NULL,
SURNAME varchar2(100) NULL,
DATEOFBIRTH date NULL,
EXPERIENCE integer NULL,
CONSTRAINT PLAYER_PK PRIMARY KEY (PID)
) ;
I would like to make a trigger which will checks the age of player if is more then 16. I have this trigger, it does not work.
CREATE OR REPLACE TRIGGER t_DATE_BIRTH
BEFORE INSERT ON PLAYER
FOR EACH ROW
BEGIN
if (extract(year from (sysdate-:new.DATEOFBIRTH) year to month )<16) then
dbms_output.put_line ('Player must be above 16 years old ');
rollback;
end if
END;
/
Maybe because I have changed format of the date using
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY';
Any suggestions?
The format of the date is irrelevant. Instead, raise an error:
CREATE OR REPLACE TRIGGER t_DATE_BIRTH
BEFORE INSERT ON PLAYER
FOR EACH ROW
BEGIN
if (extract(year from (sysdate-:new.DATEOFBIRTH) year to month ) < 16) then
RAISE_APPLICATION_ERROR -20002, 'Player must be above 16 years old');
end if;
END;
In general, a better way to check for age is not to just use the year. I would recommend:
if add_months(:new.DateOfBirth, 16*12) >= sysdate then
. . .
Related
I have recently started studying PostgreSQL and am having trouble creating triggers.
In the specific case I should check that a male athlete cannot participate in a competition for women and vice versa; in the match_code attribute an 'M' or an 'F' is inserted as the third to last character to identify that the race is for males or females (for example: 'Q100x4M06'); only one character, 'M' or 'F', is stored in the gender attribute.
I would therefore need to understand how to compare them and activate the trigger when they are not correctly entered in the participation table.
This is what i have assumed but i know it is wrong, it is just an idea, can someone help me?
CREATE FUNCTION check()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS $BODY$
DECLARE
x VARCHAR;
y VARCHAR;
BEGIN
SELECT match_code INTO x FROM race;
SELECT gender INTO y FROM athlete;
IF $x LIKE '&$y__' == athlete.gender
THEN
RETURN new;
ELSE
RAISE EXCEPTION $$It is not possible to add an athlete of a gender that is not compatible with the competition$$;
RETURN NULL;
END IF;
END;
$BODY$
CREAT TRIGGER triggerCheck
BEFORE INSERT OR UPDATE ON participation
FOR EACH ROW
EXECUTE PROCEDURE check();
below are the definitions of the tables:
CREATE TABLE race (
ID_g SERIAL NOT NULL,
code_race VARCHAR (20) PRIMARY KEY,
r_date DATE,
discipline VARCHAR (20) NOT NULL
);
CREATE TABLE athlete (
ID_a SERIAL NOT NULL,
code_athlete INT CHECK (codice_atleta >= 0 AND codice_atleta <= 15000) PRIMARY KEY,
name VARCHAR (30),
surname VARCHAR (30) NOT NULL,
nation VARCHAR (3) NOT NULL,
gender CHAR CHECK (gender = 'M' OR gender = 'F'),
b_date DATE,
sponsor VARCHAR (20)
);
CREATE TABLE participation (
ID_p SERIAL NOT NULL,
codR VARCHAR (20) REFERENCES race (code_race) ON DELETE CASCADE ON UPDATE CASCADE,
codA INT REFERENCES athlete (code_athlete) ON DELETE CASCADE ON UPDATE CASCADE,
arrival_order INT CHECK (arrival_order > 0),
r_time TIME DEFAULT '00:00:00.00',
PRIMARY KEY (codG, codA)
);
According to the tables definition, your trigger function should be someting like :
CREATE FUNCTION check()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS $BODY$
BEGIN
IF EXISTS ( SELECT 1
FROM race AS r
INNER JOIN athlete AS a
ON r.code_race ~ (a.gender || '..$')
WHERE r.code_race = NEW.codR
AND a.code_athlete = NEW.codA
)
THEN
RETURN NEW ;
ELSE
RAISE EXCEPTION 'It is not possible to add an athlete of a gender that is not compatible with the competition';
RETURN NULL;
END IF;
END;
$BODY$
In a function called by a trigger, you can (have to) use the variable NEW (resp. OLD) so that to refer to the new row (resp. the old row) to be inserted or updated (resp. only to be updated) in the targeted table ("participation" in your case) see the manual.
The proposed function doesn't need to declare any variable, as the test can be performed directly through the proposed sql query.
The proposed sql query :
First search for the rows in table "race" whose code_race equals the inserted/updated value of participation.codR = NEW.codR
Then search for the rows in table "athlete" whose code_athlete equals
the inserted/updated value of participation.codA = NEW.codA
Finally compare the selected rows from both tables "race" and
"athlete" using a regular expression to compare the code_race with
the gender values, see the manual
By the way,
(a) I don't see the added value of the columns ID of type serial in the tables definition, especially as they are not used in any primary key nor foreign key.
(b) The significant codification defined for the code_race attribute (for instance : 3rd last character defining the gender, an other character defining the competition level, ...) is a quite old-fashion practice that was used 30 years ago when the computer sciences had limited capacities and performances. In a more up-to-date approach, I would suggest you to manage these meaningful information in dedicated columns of the table "race", and then, if you really need a significant composite code_race, to implement it as a generated column :
CREATE TABLE race
( ID_g SERIAL NOT NULL PRIMARY KEY
, gender_race CHAR(1) NOT NULL CHECK (gender_race IN ('M', 'F'))
, competition_level VARCHAR(12) NOT NULL CHECK (competition_level IN ('Q-Qualifiers', 'H-Heats', 'S-Semifinals', 'F=Finals'))
, code_race VARCHAR (20) GENERATED ALWAYS AS (ID_g :: text || '-' || discipline || '-' || gender_race || '-' || left(competition_level, 1)) STORED
, r_date DATE NOT NULL
, discipline VARCHAR (20) NOT NULL
);
see the result in db<>fiddle.
How can I create a trigger to increase a date data from my table with each next row? I had an attempt, is below the table
What I want to do is to increase date from training_date_end by 1 week. But I don't know how to do it, just studying. Can anyone help?
CREATE TABLE training
(
coach_id int NOT NULL,
customer_id int NOT NULL,
training_date_start date NULL,
training_date_end date NULL,
training_place_id int NOT NULL,
CONSTRAINT training_pk PRIMARY KEY (training_place_id)
);
create or replace trigger lucky_week
before insert or update on training
for each row
begin
update training
set training_date_end = :new.training_date_end + 7
where training_date_end = :new.training_date_end;
end;
Most probably like this:
create or replace trigger lucky_week
before insert or update on training
for each row
begin
:new.training_date_end := :new.training_date_end + 7;
end;
Because, your trigger will suffer from the mutating table error (if you insert more than a single row), and - won't do anything in that case (because row you'd like to update doesn't exist yet).
Im working on a database for school, everything is working except the triggers.
For example:
create or replace TRIGGER T_CATEGORY
before insert on GAMECATEGORY
for each row
BEGIN
select G_Category_ID_SEQ.nextval into new.Category_ID from dual;
END;
its giving the SQL statement ignored and its not creating the triggers?
Is there anyone who can help me? :D
:EDIT
CREATE TABLE LIVESTREAM
(
Stream_ID number(13),
LivestreamURL varchar2(50),
primary key(Stream_ID, LivestreamURL),
Channel_ID number(13) not null,
Category_ID number(13) not null,
Title varchar2(50) not null,
Organisation_Name varchar2(50),
Viewers number(13) not null,
LivestreamStatus varchar2(10) check (UPPER(LivestreamStatus) IN ('ONLINE','OFFLINE')),
Followers number(13) not null,
"Views" number(13) not null
);
ALTER TABLE LIVESTREAM ADD constraint FK_LIVESTREAM_Category_ID foreign key(Category_ID) REFERENCES GAMECATEGORY(Category_ID)on delete cascade;
ALTER TABLE LIVESTREAM ADD constraint FK_LIVESTREAM_Channel_ID foreign key(Channel_ID) REFERENCES USERCHANNEL(Channel_ID)on delete cascade;
CREATE SEQUENCE LIVESTREAM_Stream_ID_SEQ
start with 1
increment by 1;
CREATE OR REPLACE TRIGGER T_LIVESTREAM
before insert on LIVESTREAM
for each row
BEGIN
select LIVESTREAM_Stream_ID_SEQ.nextval into :new.Stream_ID from dual;
END;
/
When i insert the data into the table it gives me this error:
INSERT INTO LIVESTREAM(LivestreamURL, Channel_ID, Category_ID, Title, Organisation_Name, Viewers, LivestreamStatus, Followers, "Views")
VALUES('http://www.twitch.tv/nightblue3, 2, 1,Next Stream: Friday # 4 AM PST / 7 AM EST / NOON GMT, The Round Table, , OFFLINE, 1052215, 115257581')
Error at Command Line : 253 Column : 1
Error report -
SQL Error: ORA-00947: not enough values
00947. 00000 - "not enough values"
*Cause:
*Action:
Check out the new should be :new.
The reason is you're inserting this whole string: 'http://www.twitch.tv/nightblue3, 2, 1,Next Stream: Friday # 4 AM PST / 7 AM EST / NOON GMT, The Round Table, , OFFLINE, 1052215, 115257581'
into the LivestreamURL column alone. You must change your query to this:
INSERT INTO LIVESTREAM(LivestreamURL,
Channel_ID, Category_ID,
Title, Organisation_Name,
Viewers, LivestreamStatus,
Followers, "Views")
VALUES
('http://www.twitch.tv/nightblue3',
2, 1,
'Next Stream: Friday # 4 AM PST / 7 AM EST / NOON GMT',
'The Round Table', ,
'OFFLINE', 1052215,
115257581)
Then change your triggers into this:
CREATE TRIGGER T_LIVESTREAM
before insert on LIVESTREAM
for each row
BEGIN
if :new.Stream_ID is null then
:new.Stream_ID := LIVESTREAM_Stream_ID_SEQ.nextval;
end if;
END;
/
create or replace TRIGGER T_CATEGORY
before insert on GAMECATEGORY
for each row
BEGIN
if :new.Category_ID is null then
:new.Category_ID := G_Category_ID_SEQ.nextval;
end if;
END;
/
I know this error issue was been addressed before, but I can't seem to find any relevant solution so I'm posting this question.
create table subscribers(
num_s number(6,0) ,
name varchar2(30) constraint nameM not null,
surname varchar2(20),
town varchar2(30),
age number(3,0) ,
rate number(3,0) ,
reduc number(3,0) ,
CONSTRAINT subscriber_pk primary key (num_s),
constraint age_c check (age between 0 and 120)
);
create or replace type copy_bookT as object(
num number(6),
loancode varchar2 (10),
book_ref ref bookT
);
create table copy_books of copy_bookT(
constraint pk_cb primary key (num),
constraint chk_st check (loancode in('Loan', 'Not')),
loancode default 'Loan' not null
);
create table Lending(
cb_num number(6),
sb_num number(6),
date_L date,
constraint fk_cb foreign key (cb_num) references copy_books(num),
constraint fk_sb foreign key (sb_num) references Subscribers(num_s)
);
create or replace trigger chk_DateL
for insert or update on lending
COMPOUND TRIGGER
--declare
L_Date int;
avail varchar2(10);
subtype copy_booksRec is lending%ROWTYPE;
type copied_bks is table of copy_booksRec;
cbks copied_bks := copied_bks();
before each row is
begin
cbks.extend;
cbks(cbks.last).cb_num := :new.cb_num;
cbks(cbks.last).sb_num := :new.sb_num;
end before each row;
before statement is
begin
for i in cbks.first .. cbks.last loop
select loancode into avail from copy_books where num = cbks(i).cb_num;
select count(date_L) into L_Date from lending where sb_num = cbks(i).sb_num and date_L = cbks(i).date_L;
if (L_Date = 0 and avail = 'Loan') then
update copy_books set loancode = 'Not' where num = cbks(i).cb_num;
cbks.delete;
-- cbks(i).date_L := cbks(i).date_L;
else
dbms_output.put_line('You can only make ONE LOAN at a time! You have already loaned a book on ' || L_Date);
cbks.delete;
end if;
end loop;
-- FORALL i IN cbks.first .. cbks.last
-- insert into lending values cbks(i);
cbks.delete;
end before statement;
end chk_DateL;
/
show errors
It all compiles successfully, but when I try to insert a sample record like:
insert into lending values (2, 700, '10-MAR-14');
it raises a numeric error which comes from the trigger line 18. I don't know what needs fixing despite my efforts.
You should not count on Oracle's default date format to translate your string literal to a date value , you should define the format you're using explicitly:
insert into lending values (2, 700, to_date('10-MAR-14', 'DD-MON-YY'));
While the date format issue is a valid point, that isn't causing your error. It's coming from line 18, which is the for ... loop line:
before statement is
begin
for i in cbks.first .. cbks.last loop
You've got cbks being extended and populated from the before row part of the trigger. When the before statement part fires, cbks is empty, as the row-level trigger hasn't fired yet. It's the first and last references that are throwing the ORA-06502: PL/SQL: numeric or value error error.
You can demonstrate the same thing with a simple anonymous block:
declare
type my_type is table of dual%rowtype;
my_tab my_type := my_type();
begin
for i in my_tab.first .. my_tab.last loop
null;
end loop;
end;
/
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 5
SQL Fiddle; you can see you can avoid it by adding an extend, but that doesn't really help you in your version, since you seem to want the row values. (You can eliminate the error in your code with an extend, but it's unlikely to do what you want still).
I'm really not sure what you're trying to achieve here, so I don't really have any advice on what you need to do differently.
as Mureinik already told, Oracle does not know about how to transform your varchar2 into date datatype and you should use date explicitly. But instead of making to_date use date literal - in my opinion it is more clear than using of to_date function
insert into lending values (2,700,date '2014-03-10');
by the way, you can simply change your NLS settings by altering the current session and installing the date format you need
First and foremost, this is for an assignment, so most of the really cool pre-written functions you'll want to suggest, I will not be allowed to use.
I have a couple tables that all have fields
creation_date
created_by
last_update_date
last_updated_by
I need to write a trigger that fills these in for creation or updating. The problem is, these tables have null values that are problematic to me. Example:
CREATE TABLE parts
(
pno NUMBER,
pname VARCHAR2(50) NOT NULL,
qoh NUMBER NOT NULL,
price NUMBER(5,2),
reorder_level NUMBER(2),
creation_date DATE NOT NULL,
created_by VARCHAR2(10) NOT NULL,
last_update_date DATE NOT NULL,
last_updated_by VARCHAR2(10) NOT NULL,
CONSTRAINT parts_PK PRIMARY KEY (pno))
These NOT NULL's are given to me and I'm not allowed to change them. So I'm having trouble conceptualizing this.
If my trigger adds these values before the field is created, I can't do an INSERT INTO with those fields blank because they're NOT NULL.
If my trigger adds these values after the field is created, I get compilation errors ORA-00903 and 00922. Invalid table name and invalid option.
I was thinking my trigger would look like
CREATE OR REPLACE TRIGGER pcreate
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
UPDATE
SET creation_date = SYSDATE;
SET created_by = USER;
SET last_update_date = SYSDATE;
SET last_updated_by = USER;
END;
/
CREATE OR REPLACE TRIGGER pchange
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
UPDATE
SET last_update_date = SYSDATE;
SET last_updated_by = USER;
END;
/
repeat for the other tables
I might be allowed to use UPSERT but I don't really know how that works.. Any suggestions are welcome. I'm really in this to learn so any other advice is appreciated.
EDIT:
My package that will not acknowledge the trigger is as follows. Do I need to call the trigger inside the package?
CREATE OR REPLACE PACKAGE process_orders
AS
PROCEDURE add_order (p_cno IN NUMBER, p_eno IN NUMBER, p_received IN DATE);
PROCEDURE add_order_details (p_ono IN NUMBER, p_pno IN NUMBER, p_qty IN NUMBER);
PROCEDURE ship_order (p_ono IN NUMBER, p_sdate IN DATE);
PROCEDURE delete_order (p_ono IN NUMBER);
FUNCTION total_emp_sales (f_eno IN NUMBER) RETURN NUMBER;
END process_orders;
/
CREATE OR REPLACE PACKAGE BODY process_orders
AS
PROCEDURE add_order (p_cno IN NUMBER, p_eno IN NUMBER, p_received IN DATE)
IS
ao_emsg VARCHAR2(100);
p_rec_today DATE;
BEGIN
SELECT SYSDATE INTO p_rec_today FROM dual;
IF p_received is null THEN
INSERT INTO orders (ono, cno, eno, received)
VALUES(order_number_seq.NEXTVAL,p_cno,p_eno,p_rec_today);
ELSE
INSERT INTO orders (ono, cno, eno, received)
VALUES(order_number_seq.NEXTVAL,p_cno,p_eno,p_received);
END IF;
EXCEPTION
WHEN OTHERS THEN
ao_emsg := substr(SQLERRM,1,100);
INSERT INTO orders_errors (ono,transaction_date,message)
VALUES(order_number_seq.CURRVAL,SYSDATE,ao_emsg);
END;
--Etc. Procedures
END;
/
In your trigger, you simply want to modify the :new record. Your triggers would look something like
CREATE OR REPLACE TRIGGER parts_before_insert
BEFORE INSERT on parts
FOR EACH ROW
BEGIN
:new.creation_date := SYSDATE;
:new.created_by := USER;
:new.last_update_date := SYSDATE;
:new.last_updated_by := USER;
END;
and
CREATE OR REPLACE TRIGGER parts_before_update
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
:new.last_update_date := SYSDATE;
:new.last_updated_by := USER;
END;
In your INSERT statement, you would omit these four columns and let the trigger fill in the values. For example (obviously, your primary keys would be coming from something like a sequence rather than being hard-coded)
SQL> insert into parts( pno, pname, qoh, price, reorder_level )
2 values( 1, 'Widget', 10, 100, 75 );
1 row created.
SQL> select *
2 from parts;
PNO PNAME QOH
---------- -------------------------------------------------- ----------
PRICE REORDER_LEVEL CREATION_ CREATED_BY LAST_UPDA LAST_UPDAT
---------- ------------- --------- ---------- --------- ----------
1 Widget 10
100 75 26-SEP-12 SCOTT 26-SEP-12 SCOTT
There is no need of executing UPDATE statement in triggers in your case. Just assign new values to the columns using :new.
CREATE OR REPLACE TRIGGER pchange
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
:new.last_update_date = SYSDATE;
:new.last_updated_by = USER;
END;