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

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.

Related

Can you easily and efficiently copy or edit the INSERTED table in a trigger?

I'm writing a trigger in which I need to check the incoming data and potentially change it. Then later in the trigger I need to use that new data for further processing. A highly simplified version looks something like this:
ALTER TRIGGER [db].[trig_update]
ON [db].[table]
AFTER UPDATE
AS
BEGIN
DECLARE #thisprofileID int
IF (Inserted.profileID IS NULL)
BEGIN
SELECT #thisprofileID=profileID
FROM db.otherTable
WHERE userid = #thisuserID;
UPDATE db.table
SET profileID = #thisprofileID
WHERE userid = #thisuserID;
-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
END
IF ({conditional})
BEGIN
UPDATE db.thirdTable
SET [profileID] = Inserted.profileID
...{20+ other fields}
FROM Inserted ...{a few joins}
WHERE {various criteria}
END
END
The problem that we're running into is that the update statement fails because Inserted.profileID is null, and thirdTable.profileID is set to not allow nulls. table.profileID will never stay null; if it is created as null then this trigger should catch it and set it to a value. But even though we're updated 'table', Inserted still has the null value. So far it makes sense to me why this is happening.
I'm unsure how to correct the problem. In the area with commented Xs I tried running an update query against the Inserted table to update profileID, but this resulted in an error because the pseudo-table apparently can't be updated. Am I incorrect in this presumption? That would be an easy solution.
The next most logical solution to me would be to INSERT INTO a table variable to make a copy of Inserted and then use that in the rest of the trigger, but that fails because the table variable is not defined. Defining that table variable would require more fields than I care to count, and will present a major maintenance nightmare any time that we need to make changes to the structure of 'table'. So assuming this is the best approach, is there an easy way to copy the data and structure of Inserted into a table variable without explicitly defining the structure?
I don't think that a temp table (which I could otherwise easily insert into) would be a good solution because my limited understanding is that they are far slower than a table variable that lives only inside the trigger. I assume that temp table also must be public, and cause problems if our trigger fires twice and both instances need the temp table.

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')

SQL Server: DonĀ“t execute a Trigger when the updater is another trigger

Here is the scenario:
There is Trigger A and Trigger B, both in the Person table. I can't trigger the Trigger A when the update on table Person comes from Trigger B.
Is there something, such as an IF that I could use to solve this situation?
Thanks in advance for any help!
Right at the start, I will warn you that having multiple triggers on one table is not a good idea. Try and merge the actions of the two triggers into one if possible. However, if that is not a solution for you, then read on for my version. (I am not certain if it is practically valid, but go ahead and give it a shot anyway)
CREATE TRIGGER triggerB
ON yourtable
FOR UPDATE
AS
BEGIN
ALTER TABLE table_name DISABLE TRIGGER triggerA
--your processing
ALTER TABLE table_name ENABLE TRIGGER triggerA
END
This question deals with disabling then enabling triggers inside a stored procedure. This is an application of the same in a trigger.
Disclaimer: I am counting on this to fail because altering a table while in a trigger defined on that same table seems like an impossible task. But I have no resources at hand to test my wacky theory, so please test it and let me know if I'm thinking too far outside the box.
I got it!
IF TRIGGER_NESTLEVEL(OBJECT_ID('TRIGGER_NAME')) = 0
BEGIN
-- Your Trigger STuff
END
Thanks for answers and comments.

SQL trigger help on UPDATE of row and column

I am having problems getting a SQL trigger to do what I want. I need to populate a field in a table when another field is updated. More specifically, when an employee ID is entered I need to generate Unicode to make a barcode work. I have written code to generate the unicode, I just need the trigger to function.
This is the skeleton of what I need:
CREATE TRIGGER Unicode
AFTER UPDATE of unique.id
ON table [dbo].table
FOR EACH ROW???
AS
BEGIN
SELECT (unique.id2
now I need to set unique.id2 to the unicode function that I wrote
I feel like I am on the wrong track with this.
Here is an image of the database I am working with (I did not create it) http://i.imgur.com/4wkfY.png. Running SQL server 2008
When PersonalDataID[2] is updated (employee ID). I need to generate code for PersonalDataID[32].
Thank you for any help you can give!
To become proficient in SQL Server, the first thing you need to do is become comfortable with the fact that most operations work best when applied as a set. Thinking in terms of "for each row" will almost always lead you down the wrong path.
In this case, since an update can affect multiple rows, you need to deal with all of those rows at once. You can do this by using the inserted pseudo-table. I looked at your picture but your sample code is quite a mess and I have no idea how to correlate the two. So here is a quick example that you will have to tailor to your actual schema.
CREATE TRIGGER dbo.UpdateUnicodeColumn
ON dbo.table
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET BarcodeColumn = (your calculation, a function, maybe?)
FROM dbo.table AS t
INNER JOIN inserted AS i
ON t.key = i.key;
END
GO
Here's the syntax. Just replace somefield with whatever field you need entered and DestinationTable with the table you want to add the Unicode record to.
CREATE TRIGGER Unicode ON SomeTable
AFTER UPDATE
AS
INSERT INTO DestinationTable
(somefield)
SELECT somefield FROM INSERTED
GO
I don't think you need 'FOR EACH ROW???' :P
CREATE TRIGGER Unicode
ON table [dbo].table
AFTER UPDATE of unique.id
AS
Begin
(SELECT unique.id2...)
Overall reference the answer above.

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

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.