Regarding trigger sequence - sql

Have two triggers on a table. One trigger is executed when there is a insert or update for each row in the table. Second trigger is executed when there is a update for each row in the table. Which trigger gets executed first in ORACLE 10G when there is a update statement on a row in the table. Is there any order of execution for triggers in oracle? If so how can i set it?

The order in which the triggers will fire is arbitrary and not something that you can control in 10g. I believe, technically, it goes in the order that the triggers happened to be created but that's certainly not something that you'd want to count on.
In 11g, you can control the firing order of triggers. However you are almost always better off replacing the two triggers with one trigger that calls two stored procedures. So rather than
CREATE TRIGGER trg_1
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
<<do thing 1>>
END;
CREATE TRIGGER trg_2
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
<<do thing 2>>
END;
you would be much better served with something like
CREATE PROCEDURE p1( <<arguments>> )
AS
BEGIN
<<do thing 1>>
END;
CREATE PROCEDURE p2( <<arguments>> )
AS
BEGIN
<<do thing 2>>
END;
CREATE TRIGGER trg
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
p1( <<list of arguments>> );
p2( <<list of arguments>> );
END;

For versions before 11g, no, the order is unspecified. From 10g Release 2 docs:
For enabled triggers, Oracle automatically performs the following actions:
Oracle runs triggers of each type in a planned firing sequence when more than one trigger is fired by a single SQL statement. First, statement level triggers are fired, and then row level triggers are fired.
Oracle performs integrity constraint checking at a set point in time with respect to the different types of triggers and guarantees that triggers cannot compromise integrity constraints.
Oracle provides read-consistent views for queries and constraints.
Oracle manages the dependencies among triggers and schema objects referenced in the code of the trigger action
Oracle uses two-phase commit if a trigger updates remote tables in a distributed database.
Oracle fires multiple triggers in an unspecified, random order, if more than one trigger of the same type exists for a given statement; that is, triggers of the same type for the same statement are not guaranteed to fire in any specific order.

No order to trigger firing can be relied upon in 10g beyond the normal before statement, before row, after row, after statement order. In 11g a new FOLLOWS clause was added to the CREATE TRIGGER statement.

In Oracle 10g we do not control the triggers that are created on same timing. It is executed randomly. So we cannot say which trigger is fired first. To overcome this problem, Oracle 11g introduced FOLLOWS CLAUSE. Using this we can control the execution order.

Related

Looping through a CURSOR in PL/PGSQL without locking the table

I have a simple PL/PGSQL block Postgres 9.5 that loops over records in a table and conditionally updates some of the records.
Here's a simplified example:
DO $$
DECLARE
-- Define a cursor to loop through records
my_cool_cursor CURSOR FOR
SELECT
u.id AS user_id,
u.name AS user_name,
u.email AS user_email
FROM users u
;
BEGIN
FOR record IN my_cool_cursor LOOP
-- Simplified example:
-- If user's first name is 'Anjali', set email to NULL
IF record.user_name = 'Anjali' THEN
BEGIN
UPDATE users SET email = NULL WHERE id = record.user_id;
END;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
I'd like to execute this block directly against my database (from my app, via the console, etc...). I do not want to create a FUNCTION() or stored procedure to do this operation.
The Issue
The issue is that the CURSOR and LOOP create a table-level lock on my users table, since everything between the outer BEGIN...END runs in a transaction. This blocks any other pending queries against it. If users is sufficiently large, this locks it up for several seconds or even minutes.
What I tried
I tried to COMMIT after each UPDATE so that it clears the transaction and the lock periodically. I was surprised to see this error message:
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
I'm not quite sure how this is done. Is it asking me to raise an EXCEPTION to force a COMMIT? I tried reading the documentation on Trapping Errors but it only mentions ROLLBACK, so I don't see any way to COMMIT.
How do I periodically COMMIT a transaction inside the LOOP above?
More generally, is my approach even correct? Is there a better way to loop through records without locking up the table?
1.
You cannot COMMIT within a PostgreSQL function or DO command at all (plpgsql or any other PL). The error message you reported is to the point (as far as Postgres 9.5 is concerned):
ERROR: cannot begin/end transactions in PL/pgSQL
A procedure could do that in Postgres 11 or later. See:
PostgreSQL cannot begin/end transactions in PL/pgSQL
In PostgreSQL, what is the difference between a “Stored Procedure” and other types of functions?
There are limited workarounds to achieve "autonomous transactions" in older versions:
How do I do large non-blocking updates in PostgreSQL?
Does Postgres support nested or autonomous transactions?
Do stored procedures run in database transaction in Postgres?
But you do not need any of this for the presented case.
2.
Use a simple UPDATE instead:
UPDATE users
SET email = NULL
WHERE user_name = 'Anjali'
AND email IS DISTINCT FROM NULL; -- optional improvement
Only locks the rows that are actually updated (with corner case exceptions). And since this is much faster than a CURSOR over the whole table, the lock is also very brief.
The added AND email IS DISTINCT FROM NULL avoids empty updates. Related:
Update a column of a table with a column of another table in PostgreSQL
How do I (or can I) SELECT DISTINCT on multiple columns?
It's rare that explicit cursors are useful in plpgsql functions.
If you want to avoid locking rows for a long time, you could also define a cursor WITH HOLD, for example using the DECLARE SQL statement.
Such cursors can be used across transaction boundaries, so you could COMMIT after a certain number of updates. The price you are paying is that the cursor has to be materialized on the database server.
Since you cannot use transaction statements in functions, you will either have to use a procedure or commit in your application code.

Type of update on postgresql

New to Postgres and PL/pgSQL here.
How do I go about writing a PL/pgSQL function to perform different actions based on the type of update (insert,delete,etc) made to the table/record in a postgres database.
You seem to be looking for a trigger.
In SQL, triggers are procedures that are called (fired) when a specific event happens on an object, for example when a table is updated, deleted from or insterted into. Triggers can respond to many use cases such as implementing business integrity rules, cleaning data, auditing, security, ...
In Postgres, you should first define a PL/pgSQL function, and then reference it in the trigger declaration.
CREATE OR REPLACE FUNCTION my_table_function() RETURNS TRIGGER AS $my_table_trigger$
BEGIN
...
END
$my_table_trigger$ LANGUAGE plpgsql;
CREATE TRIGGER my_table_trigger
AFTER INSERT OR UPDATE OR DELETE ON mytable
FOR EACH ROW EXECUTE PROCEDURE my_table_function();
From within the trigger code, you have access a set of special variables such as :
NEW, OLD : pseudo records that contain new/old database records affected by the query
TG_OP : operation that fired the trigger (INSERT, UPDATE, DELETE, ...)
Using these variables and other triggers mechanisms, you can analyze or alter the on-going operation, or even abort it by raising an exception.
I would recommend reading Postgres documentation for the CREATE TRIGGER statement and Trigger Procedure (the latest gives lots of examples).

How to disable nesting of triggers at table or trigger level in SQLServer?

Using SQL Server 2012, is it possible to have nested triggers enabled at the server/database level but to disable nesting on one specific table/trigger?
I think that you should be able to achieve your purpose by using the TRIGGER_NESTLEVEL() function, which is available since SQL Server 2008:
Returns the number of triggers executed for the statement that fired the trigger. TRIGGER_NESTLEVEL is used in DML and DDL triggers to determine the current level of nesting.
You could alter the code of the relevant trigger to add the following statement just after the BEGIN keyword. The trick is to dynamically compute the object_id of the current trigger, which TRIGGER_NESTLEVEL() expects as first argument:
IF TRIGGER_NESTLEVEL(
( SELECT object_id FROM sys.triggers WHERE name = 'MyTrigger' )
) > 1 RETURN
This will actually prevent the given trigger to execute recursively.
Another option is to use TRIGGER_NESTLEVEL() without arguments. This returns the number of times all triggers have been executed for the statement. My understanding of your use case is that the first solution should be what you need.

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