A Trigger which will implement update cascade to PK - sql

I need to write a trigger which will implement update cascade for (gameno) code for SummerGames but I keep encountering `Error(11,1): PLS-00103: Encountered the symbol "CREATE
I'm using Oracle SQL Developer.
My current code:
create or replace TRIGGER SG_GAMENO_UPDATE
BEFORE UPDATE OF SG_GAMENO ON SUMMERGAMES
FOR EACH ROW
BEGIN
UPDATE SUMMERGAMES
SET SG_GAMENO = :new.SG_GAMENO
WHERE SG_GAMENO = :old.SG_GAMENO;
END;

You need to end the trigger statement, which is partly PL/SQL, with a / on a line on its own. That will end the statement and cause it to be run.
create or replace TRIGGER SG_GAMENO_UPDATE
BEFORE UPDATE OF SG_GAMENO ON SUMMERGAMES
FOR EACH ROW
BEGIN
UPDATE SUMMERGAMES
SET SG_GAMENO = :new.SG_GAMENO
WHERE SG_GAMENO = :old.SG_GAMENO;
END;
/
At the moment whatever is folowing this in your script - which seems to be another create statement - is being seen as part of the same trigger creation, and that keyword isn't valid within a block.
Your trigger doesn't seem to make any sense, and at best will lead to infinite recursion when you attempt an update (which will be detected and killed), but that's a separate issue. Perhaps you meant to update a child table, rather than the same table the trigger is against. But you shouldn't really be updating a PK at all; that's why synthetic keys are generally preferred over natural ones. gameno sounds synthetic anyway.

it will something about the separation of your codes, i mean when you executed the code snippet above, you marked off some part from another code, that's why you get syntactic error. try to execute just the code above, delete anyting else from the sql editor

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

need help writing this compound trigger

i have a column in my table that i need to update on insert or update, i can't do it with a simple trigger because the new value has to be SELECTED from the same table and other tables (which would generate trigger is mutating error).
my solution was to use a compound trigger, after a little bit of documentation i wrote this code:
CREATE OR REPLACE TRIGGER update_contract_n FOR
INSERT OR UPDATE ON contract
COMPOUND TRIGGER
v_n VARCHAR(70);
AFTER EACH ROW IS BEGIN
v_n := object_contract(:new.id_contract);
END AFTER EACH ROW;
AFTER STATEMENT IS BEGIN
UPDATE contract
SET
n = v_n
WHERE
id_contract = :new.id_contract;
END AFTER STATEMENT;
END update_contract_n;
the function object_contract() joins 4 tables one of which is the table contract and returns a varchar.
when i compile the trigger code i get this error :
pls-00679 trigger binds not allowed in before/after statement section
but i don't know what i'm doing wrong since the variable binding was done in AFTER EACH ROW section not in AFTER STATEMENT.
any help would be appreciated (fixing or rewriting the code), a simple guide on how to use a compound trigger would also be appreciated.
EDIT:
i found the problem, i was referencing :new.id_contract in AFTER STATEMENT and i fixed it, but now i am getting table is mutating trigger/function may not see it
how can i fix that?

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.

Simple IF EXISTS in TRIGGER fails

I've created a simple TRIGGER:
ALTER TRIGGER [dbo].[SprawdzZajetosc] ON [dbo].[Wypozyczenia]
FOR insert, update
AS
BEGIN
IF EXISTS (SELECT * FROM Wypozyczenia)
BEGIN
RAISERROR('Wybrany pojazd został już wypożyczony w wybranym przedziale czasu.', 16, 1)
ROLLBACK TRANSACTION
END
END
I can't understand why 'if' is returning me TRUE even if table 'Wypozyczenia' is empty? It doesn't matter what 'Wypozyczenia' contains - it always returns me TRUE.
I tried with count(*) it always returns me a value > 0.
Why is that?
I am not 100% sure of this, but it sounds logical to me - The trigger is an insert/update trigger. As soon as something is being inserted, the trigger is triggered and the condition is TRUE. Since there is a ROLLBACK TRANSACTION fired, the inserted row is then rolled back and hence you get an empty table. What are you actually trying to achieve here ?
Apart from the reasons why you're doing this, the cause for IF EXISTS() to be always TRUE in your case is very simple it's because you're using an AFTER or FOR trigger.
CREATE TRIGGER
AFTER specifies that the DML trigger is fired only when all operations
specified in the triggering SQL statement have executed successfully.
All referential cascade actions and constraint checks also must
succeed before this trigger fires.
Meaning the row(s) you're trying to insert are already in the table. It's just a transaction has not been committed yet.
Here is SQLFiddle demo
Your IF EXISTS() check might've worked only in INSTEAD OF INSERT trigger but then you should've take into consideration that triggers in SQL Server are statement based. Meaning it fires once per statement and you can insert more than one row in one statement.
Here is SQLFiddle demo
As far as FOR UPDATE clause goes in your trigger definition it doesn't make any sense at all. If you're updating something it should be in the table. Thus table is not empty.

Solving the mutating table problem in Oracle SQL produces a deadlock

Hey, I'm trying to create a trigger in my Oracle database that changes all other records except the one that has just been changed and launched the trigger to 0. Because I am updating records in the same table as the one that launched the trigger I got the mutating table error. To solve this, I put the code as an anonymous transaction, however this causes a deadlock.
Trigger code:
CREATE OR REPLACE TRIGGER check_thumbnail AFTER INSERT OR UPDATE OF thumbnail ON photograph
FOR EACH ROW
BEGIN
IF :new.thumbnail = 1 THEN
check_thumbnail_set_others(:new.url);
END IF;
END;
Procedure code:
CREATE OR REPLACE PROCEDURE check_thumbnail_set_others(p_url IN VARCHAR2)
IS PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE photograph SET thumbnail = 0 WHERE url <> p_url;
COMMIT;
END;
I assume I'm causing a deadlock because the trigger is launching itself within itself. Any ideas?
Using an autonomous transaction for this sort of thing is almost certainly a mistake. What happens if the transaction that inserted the new thumbnail needs to rollback? You've already committed the change to the other rows in the table.
If you want the data to be transactionally consistent, you would need multiple triggers and some way of storing state. The simplest option would be to create a package with a collection of thumbnail.url%type then create three triggers on the table. A before statement trigger would clear out the collection. A row-level trigger would insert the :new.url value into the collection. An after statement trigger would then read the values from the collection and call the check_thumbnail_set_others procedure (which would not be an autonomous transaction).