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.
Related
I'm having a little trouble with understanding functions and triggers in sql. I didn't post the code of procedure chkInsertAritcle but let's say it returns NEW if it managed to make change and NULL if it didn't.
So my question is about the trigger. If I put AFTER INSERT does that means that it will complete INSERT without depending on the return value? And what
happens with the rest of the rows?
Next question is if I put BEFORE INSERT, in what order does code runs?
Thanks!
CREATE TRIGGER ArticleIns
AFTER INSERT ON ListOfArticles
FOR EACH ROW
EXECUTE PROCEDURE chkInsertArticle();
First all BEFORE triggers run in alphabetical order, then the operation is performed, then all AFTER triggers run in alphabetical order.
Each operation sees the result of the previous one as input, and if any trigger returns NULL, processing for that row stops. So if a BEFORE trigger returns NULL, the DML operation won't take place.
This happens independently for each row affected by the triggering DML statement.
So if the trigger runs before insert, then the code runs before the data is inserted into the row and constraints are checked. So for example you might want to add a timestamp before the data is committed to the database,
If it runs after then the data is already present in the table and all constraints have been checked. This is usually where you want to trigger another process based on the row data, maybe update another table, send an e-mail etc.
In your example, the data will be in the database before your procedure runs. So if your procedure modifies the row data, it needs to be in the database.
I have a query that I run to pull data from multiple tables which are part of the Questionnaire plugin Moodle.
This returns a view. (Joining this view to another for php search)
What I would like is for a column app_ref which has a ref number inserted into it to copy to another column ref_number in the same view.
ref_number|app_ref
1234
2345
I have googled and I understand I need to use a trigger but I have never used a trigger/constructed one (learning PostgreSQL) so I'm not sure as having read on the PostgreSQL site, would the trigger run BEFORE or AFTER?
If I understand you correctly, you want to update a view which is not updatable because it selects from more than one table.
First, you can write a procedure that executes the update you want:
CREATE OR REPLACE FUNCTION my_view_update()
RETURNS trigger AS
$BODY$
BEGIN
-- Here comes your SQL to handle your ref_number and app_ref issue
-- use NEW.app_ref or OLD.app_ref to access values
RETURN NEW;
END
$BODY$
You can then tell the view to execute your function instead of the "update" like so:
CREATE TRIGGER my_update
INSTEAD OF UPDATE
ON my_view
FOR EACH ROW
EXECUTE PROCEDURE my_view_update;
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)
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.
If I have a statement
that updates multiple rows, only the trigger will fire only on the first or last row that is being updated (not sure which one). I need to make a trigger that fires for ALL the records that are being updated into a particular table
Assuming SQL Server, A trigger only fires once per update, regardless of the number of rows that are updated. If you need to carry out some additional logic based on updates to multiple rows you can access the changed records by looking at the INSERTED and DELETED logical tables that are accessible in the context of a trigger.
You have not specified the database .....
In Oracle a trigger can be defined to fire for individual rows and based on the type of transaction:
CREATE OR REPLACE TRIGGER BIUDR_MY_TABLE
BEFORE INSERT OR UPDATE OR DELETE
ON MY_TABLE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
pk PLS_INTEGER;
BEGIN
etc ....
You just need to indicate if your trigger needs to be executed "FOR EACH ROW" or "FOR EACH STATEMENT". Adding one of these two clauses in the trigger definition will tell the DBMS when to execute the trigger (most, but not all, DBMSs support it). If you don't indicate this clause then the DBMS uses the default option which in your case seems to be the FOR EACH STATEMENT option, and that's why your trigger only fires one for each update sentence, regardless of how many rows you are updating