Invoking procedure from a trigger to validate birthdate - sql

I have created a trigger below (Trig 1)to fire before insert on employee table. Then this trigger will invoke the procedure (Proc 1) which will validate the birthdate being not earlier the the current date. if not proceed with the insert, but if date is earlier than current date, it will show a message like "invalid birthdate".
(Trig 1)
create or replace trigger VALIDATE_BDAY_TRIG
before insert on employee
for each row
declare
birth_date date;
employee_birthdate date;
begin
birth_date := employee_birthdate;
val_bday_proc birth_date;
end VALIDATE_BDAY_TRIG;
(Proc 1)
create or replace procedure val_bday_proc(
date_of_birth in date)
as
begin
if date_of_birth > current_date()
then raise_application_error(-20000, 'Employee birth date should not be earlier than the current date');
end;

When calling a stored procedure, you should pass variables between parentheses:
val_bday_proc(birth_date)
Also, you need to get the actual value that is to be inserted, because now employee_birthdate is just a variable, and will be null. You can use :new.fieldname to get the value of the field 'fieldname' of the new record. No need to declare variables for that at all, so your trigger could look like this, assuming the field is called employee_birthdate:
Create or Replace trigger VALIDATE_BDAY_TRIG
before insert on employee
for each row
begin
val_bday_proc(:new.employee_birthdate);
end VALIDATE_BDAY_TRIG;
The stored procedure seems to be okay, except it's missing the end if; to close the if-statement.
Some side notes:
You seem to be confusing earlier and later. The code in the proc is okay, but in the error message and in your question text you got it the other way around.
You could (maybe should?) also check this on update, otherwise you can insert an earlier date and then update it to some date in the future. You could make a separate trigger for this, or modify the current one to also trigger on update: (before insert or update).
It may help to have a naming convention for triggers that show their context (whether they are insert and/or update, row level or statement level). That helps you find the right one if you have multiple triggers.
It's a good idea to at least consider not putting this in a trigger at all. I learned the hard way that having a lot of business logic in triggers will eventually affect performance, is hard to debug, and hard to change. These kind of checks could be made in an application layer that stores the employee-data.

Related

What can I use instead of an update trigger?

I have an update trigger in SQL Server and I want to remove this trigger and make update operation with a stored procedure instead of the trigger. But I have UPDATE(end_date) control in update trigger.
What can I use instead of below UPDATE(end_date) control? How can I compare old and new end_dates in stored procedure efficiently?
Update trigger
ALTER TRIGGER [dbo].[trig_tbl_personnel_car_update]
ON [dbo].[tbl_personnel_cars]
FOR UPDATE
AS
IF (UPDATE(end_date))
UPDATE pc
SET pc.owner_changed = 1
FROM tbl_personnel_cars pc, inserted i
WHERE pc.pk_id = i.pk_id
Sample updated script in stored procedure
ALTER PROCEDURE [dbo].[personnel_car_update]
(#PkId INT)
UPDATE tbl_personnel_cars
SET end_date = GETDATE()
WHERE pk_id = #PkId
I update tbl_personnel_cars table inside many stored procedures like this. How can I update this table like trigger does instead of update trigger?
I tried below codes to get old and new end_dates but I can't.
Sample updated script in stored procedure:
ALTER PROCEDURE [dbo].[personnel_car_update]
(#PkId INT)
UPDATE tbl_personnel_cars
SET end_date = GETDATE()
WHERE pk_id = #PkId
EXEC update_operation_sp_instead_trigger #PkId
ALTER PROCEDURE [dbo].[update_operation_sp_instead_trigger]
(#PkId INT)
UPDATE pc
SET pc.owner_changed = 1
FROM tbl_personnel_cars pc
JOIN tbl_personnel_cars pc2 ON pc.pk_id = pc2.pk_id
WHERE pc.end_date <> pc2.end_date
And last question. Is it a correct choice to use stored procedure instead of trigger where the table is updated?
Firstly, I want to clarify a misunderstanding you appear to have about the UPDATE function in Triggers. UPDATE returns a boolean result based on if the column inside the function was assigned a value in the SET clause of the UPDATE statement. It does not check if that value changed. This is both documented feature, and is stated to be "by-design".
This means that if you had a TRIGGER with UPDATE(SomeColumn) the function would return TRUE for both of these statements, even though no data was changed:
UPDATE dbo.SomeTable
SET SomeColumn = SomeColumn;
UPDATE ST
SET SomeColumn = NULL
FROM dbo.SomeTable ST
WHERE SomeColumn IS NULL;
If, within a TRIGGER, you need to check if a value has changed you need to reference the inserted and deleted pseudo-tables. For non-NULLable columns equality (=) can be checked, however, for NULLable columns you'll also need to check if the column changed from/to NULL. In the latest version of the data engine (at time of writing) IS DISTINCT FROM makes this far easier.
Now onto the problem you are actually trying to solve. It looks like you are, in truth, overly complicated this. Firstly, you are setting the value to GETDATE so it is almost certainly impossible that the column will be set to be the same value it already set to; you have a 1/300 second window to do the same UPDATE twice, and if you add IO operations, connection timing, etc, that basically makes hitting that window twice impossible.
For what you want, just UPDATE both columns in your procedure's definition:
ALTER PROCEDURE [dbo].[personnel_car_update] #PkId int AS --This has a trailing comma, which is invalid syntax. The parathesis are also not needed; SP's aren't functions. You were also missing the AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.tbl_personnel_cars --Always schema qualify
SET end_date = GETDATE(),
owner_changed = 1
WHERE pk_id = #PkId;
END;
Larnu gave you a great answer about the stored procedure logic, so I want to answer your question about "Is it a correct choice to use stored procedure instead of trigger where the table is updated?"
The upsides of DML triggers are following in my opinion:
When you have a lot of places that manipulate a table, and there need to be some common logic performed together with this manipulation like audit / logging, trigger can solve it nicely because you don't have to repeat your code in a lot of places
Triggers can prevent "stupid" actions like: DELETEs / UPDATEs without WHERE by performing some specific validation etc. They can also make sure you are setting all mandatory fields (for example change date) when performing adhoc updates
They can simplify quick and dirty patches when you can concentrate your logic to one place instead of X number of procedures / compiled code.
Downside of triggers is performance in some cases, as well as some more specific problems like output clause not working on the triggered tables and more complicated maintenance.
It's seldom you can't solve these issues with other non-trigger solutions, so i'd say if your shop already uses triggers, fine, but if they don't, then there's seldom a really good reason to start either

Stored procedure to run a query based off timestamp in table

I'm trying to write a procedure where the query will only run if the table has been updated at a certain time.
There is actually a timestamp in the table so I just need to check something like
IF table.update = GETDATE() THEN …
I'm not even sure where to start here.
Can anyone point me in the direction of a place where I can learn about stuff like this, or show me which functions I need to use?
You need to start by looking at how to use Database JOBs.
Since no stored procedure can start by itself.
Inside the Job, you can define how often and when stored procedure run.
The code inside the job can be simple SQL.
OR
You can create an update Trigger on the table. And inside the trigger base on the time call the stored procedure.
Update:
Just saw your latest comment. I have a similar app in my company. We use a Database Job to send a batch of auotmated emails on specific time everyday.
You need a trigger, in the UPDATE method, and you can have it run when it is saved in a separate table.
The new table must have the primary key and the time stamp with the GETDATE () method.
To Implement the Trigger:
create trigger trTriggerTest on tableName after update
As
Begin
set nocount on;
INSERT INTO TimeRegister(Id, date, ...) VALUES (#Id, GetDate(), ...);
end
go
To register en new table:
INSERT INTO TimeRegister(Id, date, ...) VALUES (#Id, GetDate(), ...);
Documentation: Trigger

Oracle Sql trigger not working

I have a very simple trigger, that prints out the username and date when a new row is inserted into the users table. But after it successfully compiled, the trigger didn't get triggered (there was no output in the dbms window). So, I simplified my code until I got this:
CREATE OR REPLACE TRIGGER logger
AFTER INSERT ON USERS
BEGIN
DBMS_OUTPUT.PUT_LINE('User added with name:');
END;
If I run the code in the SQL worksheet (from BEGIN to END), I can see the output, but not when I try to use the trigger. Where is the problem?
There are two options, one is that the trigger is not firing, the other is that it is, but you're not seeing the output.
Assuming you're working in a console program like sqlplus, make sure to do SET SERVEROUTPUT ON before inserting your row.
If that doesn't fix it, then make sure the trigger is firing. Try creating a simple table:
CREATE TABLE logtable ( msg VARCHAR2(30 CHAR));
Next, add this to your trigger,
INSERT INTO logtable( msg ) VALUES ( 'trigger fired' );
Then, try your insert again.

Encountered the symbol "end-of-file" when expecting one of the following in simple trigger

create or replace TRIGGER PR_POZ_ZAM_CENA_TRG AFTER UPDATE ON PR_POZYCJA_ZAMOWIENIA
FOR EACH ROW BEGIN
BEGIN
UPDATE PR_POZYCJA_ZAMOWIENIA
SET PR_POZYCJA_ZAMOWIENIA.cena_za_sztuke = 1.1*PR_PRODUKT.cena
WHERE PR_PRODUKT.id_produktu=:new.id_produktu;
END PR_POZYCJA_ZAMOWIENIA;
heres my code, i have no idea why its wrong, I think I miss : somewhere or the query is bad, but idk how to fix it, tried using : and changing PR_POZYCJA_ZAMOWIENIA.cena_za_sztuke to new.cena_za_sztuke but it didnt work
You have BEGIN twice, and you only END once. The final END needs to atch the name of the trigger, not the table. (Giving the name is optional, but useful.)
You are also updating the same trigger the update is against; which will cause the trigger to fire again; which will cause a further update; which will fire the trigger again...
You can assign a column value in the row being updated, in this case with a select ... into:
create or replace TRIGGER PR_POZ_ZAM_CENA_TRG
AFTER UPDATE ON PR_POZYCJA_ZAMOWIENIA
FOR EACH ROW
BEGIN
SELECT 1.1*PR_PRODUKT.cena
IMTO :new.cena_za_sztuke
FROM PR_PRODUKT
WHERE PR_PRODUKT.id_produktu=:new.id_produktu;
END PR_POZ_ZAM_CENA_TRG:
/
If that calculation is constant, it might be simpler to calculate it when querying the table, or via a view, rather than storing and maintaining that derived value.

Trigger After Update In SQL Compiles But Does Not Seem To Fire

I have a trigger that should update a certain column, Points, after an update is made to a record in that same row. To do this, I have created a series of functions calc_extrapoint and calc_fieldgoalpoint to facilitate the process. Both of these functions work and return the correct values when run manually.
However, when I try to have the trigger fire after an update to the table, which most likely will happen with the columns ExtraPoint or FieldGoal the Points column is not updated as I'd expect.
For example iniitally in my table all values of each record starts as 0, 0, 0 for points, extra point, and field goal columns respectively. When I update the extra point column though to say 2, I expect the Points column to be 2. If I update again this time with field goal points to 2, I expect my Points column to be set to 8.
create or replace function calc_points() returns trigger as $$
begin
NEW."Points" := calc_extrapoint(NEW."ExtraPoint") + calc_fieldgoalpoint(NEW."FieldGoal");
return NEW;
end
$$ LANGUAGE plpgsql;
DROP TRIGGER calc_points on playerdata;
CREATE TRIGGER calc_points AFTER UPDATE ON playerdata
FOR EACH ROW EXECUTE PROCEDURE calc_points();
The trigger and functions all compile appropriately, but are not firing as expected. Is there something I'm missing to complete the action?
Modifying NEW in an AFTER trigger won't do anything. The row has already been written. Put a RAISE NOTICE 'Trigger fired'; in there; you'll get the notice. It's just that the trigger fires, but does nothing.
That's why we have BEFORE triggers. If you want to modify the row to be written to the table, you must use a BEFORE trigger.