Can i use NOT NULL constraint with a condition? in PostgreSQL - sql

im a newbie to PostgreSQL, is there any way that i can make some tuples not deletable if some condition holds? to be specific, suppose i have:
Table Males( Name_A, Profession)
Table Students( Names_B, Date_birth)
where Names_B references Names_A, how can i make sure that only those Names_A are "not deletable" whose Date_birth="xx/yy/zz"
sorry if i couldnt clearly explain it, havnt found anything in DDL using NOT NULL constraint to write this up.
Thanks in advance for the help!

CREATE FUNCTION protect_delete() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF OLD.date_birth = 'xx/yy/zz' THEN -- don't actually use this date format
RETURN NULL; -- don't delete
ELSE
RETURN OLD;
END IF;
END;
$$;
CREATE TRIGGER protect_delete BEFORE DELETE ON students FOR EACH ROW
EXECUTE PROCEDURE protect_delete();

You can use a PostgreSQL rule:
create rule rule_test as
on delete to test
-- old is a reference to your table
where old.birth = '2011-1-1' -- or whatever condition you want
do instead nothing;
One a big table this may run faster since this will modify the query itself and rewrite the query with the condition instead of checking each row. (Triggers may be more powerful and easier to understand if you are planning to do a lot of this type of stuff.)
create rule - http://www.postgresql.org/docs/current/static/sql-createrule.html
rules vs. triggers - http://www.postgresql.org/docs/current/static/rules-triggers.html

See Postgres trigger documentation for information on creating triggers. It sounds like you want a row level trigger.
"A row-level trigger fired before an operation ... can return NULL to skip the operation for the current row. This instructs the executor to not perform the row-level operation that invoked the trigger (the insertion or modification of a particular table row). "
So within the trigger test for your condition and return null to prevent the deletion, return the trigger row to allow the deletion to continue.

Related

SQL Trigger information

I have a general question about Triggers. Is there anyway to program the SQL database in a way that whenever a trigger is fired, SQL tells us what rows of data were removed or edited to meet one's criteria. In my case, I am using postgreSQL and I have one specific trigger which deletes certain rows being inserted in a table if a certain criteria is met. Is there anything I can add or change settings in postgreSQL that will indicate to me what rows and how many rows were deleted in this case:
CREATE TRIGGER unknowns
AFTER INSERT
ON employees
FOR EACH ROW
EXECUTE PROCEDURE delete_rows();
CREATE OR REPLACE FUNCTION delete_rows()
RETURNS trigger AS
$BODY$
BEGIN
DELETE FROM employees WHERE Customer = 'unknown';
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
You would not typically do this at the database level.
Normally, you would modify the trigger to store changed data in an archive/history table. Then you will have a record of the data that has changed, when it was changed, and who changed it.
If you specifically want to know about changes initiated in a trigger, I'm not sure if there is any mechanism to distinguish those changes from user-initiated changes.

Oracle Triggers, reliant on a key generated by a second trigger

I have question about triggers. For my database assignment, I have to create a trigger that automatically generates an ID number, which I have done.
The problem is a second trigger needs to be made that also acts on the same data. Both are inserts. Since they have to be separate, I am not sure how to make this work.
From what I have been taught the way to make a trigger act on the most recent addition is to use:
WHERE ID=:NEW.ID;
where ID is the primary key, but this does not work for me when the ID is being generated by a trigger. Is there a method of creating a trigger that acts on the most recent row added to the table that does not reference the primary key?
It is not clear why you need two triggers but let's assume it really makes sense (which I doubt).
Does the first trigger generates the ID from a sequence? In this case you can use value CURRVAL of the sequence. This pseudocolumn returns the current value of a sequence without increasing the value, see Sequence Pseudocolumns
If this does not fit your needs you can write a procedure which is then called be the trigger.
Would be like this:
create procedure PROC(aRow in ROWID) as
begin
...
end;
create first_trigger ....
begin
... whatever is needed at first trigger.
PROC(NEW.ROWID);
end;
create second_trigger ....
begin
... whatever is needed at second trigger.
PROC(NEW.ROWID);
end;
Both triggers would operate on the same row. You can also write current row values into a PL/SQL variable and process them by a Statement-Trigger (i.e. no row-level-trigger)

Trigger to ensure a value cannot happen

This is using Oracle SQL.
Apologies in advance as I am new to the SQL world.
I'm trying to create a simple trigger to ensure a sports event cannot happen in a certain month (we'll use December as the example). So if someone tries to insert a new row with a date in December, the trigger will prevent it.
The current table uses the DATE datatype, inserted as 'DD-MMM-YYYY' but when selected it's displayed as 'DD-MMM-YY' (I don't know why.)
Anyway, I've never made triggers before and I've tried it two ways but it bugs out because when I press ENTER on SQL Plus, it just keeps going as if I was missing a semi-colon. And I'm guessing the trigger itself is not working.
CREATE OR REPLACE TRIGGER event_test
BEFORE INSERT OR UPDATE
ON sports_event
BEGIN
IF DATE
IS 'DEC' THEN
'Sports cannot occur during December.';
END IF;
END;
I've also tried with a CASE and I could not get it to work.
I'm trying to create a simple trigger to ensure a sports event cannot happen in a certain month
[...]
The exercise that I'm trying to do this for specifically asks to create a trigger to ensure the event cannot happen in a certain month.
As this is for homework / educational purpose, here are some hints first:
First, as this was said by Mureink in his answer, remember that a CHECK CONSTRAINT is the preferred way to do data validation;
Then, as you are required to use a trigger, you will need both an INSERT trigger and an UPDATE trigger;
As you will do data validation, you need a BEFOREINSERT OR UPDATE trigger;
You will access to incoming data using the NEW. pseudo-record;
And you will reject DML statement by raising an exception.
You already have the (2) and (3) in your code. Starting from that, one complete solution might look like this:
CREATE OR REPLACE TRIGGER event_test
BEFORE INSERT OR UPDATE
ON sports_event
FOR EACH ROW WHEN (EXTRACT(MONTH FROM NEW.event_date) = 12)
BEGIN
RAISE_APPLICATION_ERROR (
num=> -20107,
msg=> 'Sports cannot occur during December.');
END;
Untested. Beware of typos !
Triggers aren't really meant for data validation. Why not use a check constraint instead?
ALTER TABLE sports_event
ADD CONSTRAINT not_in_december_ck
CHECK (TO_CHAR(event_date, 'MM') != '12')

mutating table with this trigger oracle database

Use Oracle database and need to store all rows in lower case. All INSERTand UPDATE should insert and update rows with lower case data. I wrote a trigger to do it. Read a lot of about table mutation with triggers.Very confused, will this code raise mutate error or not. Please, write here, if it will.
create or replace trigger employee_name
before update or insert on employee
for each row
begin
/* convert character values to lower case */
:new.lastname := lower( :new.lastname );
:new.firstname :=lower( :new.firstname );
end;
/
The trigger will not raise a mutating table error as you're not selecting from the table your trigger is on; you're only reassigning values, which is what triggers are designed to do.
It's worth noting that this can also be accomplished with a CHECK constraint, which will force everyone updating or inserting into the table to lower case everything:
alter table employee_name
add constraint chk_emp_name_lastname
check ( lastname = lower(lastname) )
Whilst this will increase update/insert times (not necessarily more than using a trigger) it doesn't silently change data being entered but instead complains that the data is incorrect. This is sometimes a better approach.
this code not raise exception
because you not try select from table which trigger executing
This should not raise any errors. Mutate errors occur when you read (or modify) some other rows from BEFORE trigger.
NOTE: there are situations where BEFORE trigger is fired more that once for the SAME row.
In order to guarantee consistency Oracle would have to justify, that your trigger body is "idempotent": e.i. each execution on the same input gives the same result.

creating a trigger to update a date in a table before a specific value is changed

I am tryign to create a trigger which will update a table so that when something is inserted into the table the given end date will change to set to the day before the start date of the new entry. i have created this trigger which complies but when checking it doesn't work. can anyone see the porblem with it?
CREATE TRIGGER
updatetrigger12345
BEFORE INSERT ON
shares_amount
FOR EACH ROW
BEGIN
UPDATE OF
shares_amount
SET
date_end = :NEW.date_start-1
WHERE
share_issue_id = :NEW.share_issue_id
AND
share_id= :NEW.share_id
;
END
;
/
Bad, bad, bad. You do not want to attempt to update the table on whose trigger is being executed. You just need to update the ":new" fields of interest. Maybe something like this:
CREATE TRIGGER
updatetrigger12345
BEFORE INSERT ON
shares_amount
FOR EACH ROW
BEGIN
:NEW.date_end := :NEW.date_start-1;
END
;
/
Here is a tutorial. In addition, I would try to really stay away from using trigger if not needed. There certainly are situations when they make sense; however, they can be abused and cause a lot of confusion and problems down the road. Please read this classic AskTom article The Trouble With Triggers.