Nik Kashi
Nik Kashi

Reputation: 4596

Check Condition with SELECT in oracle trigger

What is problem in my trigger? I use toad for oracle
After executing this sql toad show this message and no more!:

Warning: compiled but with compilation errors

and trigger does not work

my trigger is here:

CREATE OR REPLACE TRIGGER trg_delete_mmtp_user_feature
   BEFORE DELETE
   ON mmtp_user
   FOR EACH ROW
BEGIN
-- for these cases delete not allowed
   IF ((SELECT COUNT (*)
          FROM payment p INNER JOIN mmtp_user mt
               ON mt.customer_id = p.requester_id
         WHERE p.rp_reason_id = 20 AND mt.mmtp_user_id = :OLD.mmtp_user_id) >
                                                                             0
      )
   THEN
      raise_application_error
             (-20654,
              'Query has attempted to delete MMTP_USER with existing payment'
             );

   ELSE                            --befor deleting features should be deleted
      DELETE FROM mmtp_user_feature
            WHERE mmtp_user_id = :OLD.mmtp_user_id;
   END IF;
END;
/

and for deleting i receive this message:

ORA-04098: trigger 'DEVX2.TRG_DELETE_MMTP_USER_FEATURE' is invalid and failed re-validation

Upvotes: 0

Views: 6072

Answers (2)

Florin Ghita
Florin Ghita

Reputation: 17643

The problem is

   SELECT COUNT (*)
              FROM payment p INNER JOIN mmtp_user mt
                   ON mt.customer_id = p.requester_id
             WHERE p.rp_reason_id = 20 AND mt.mmtp_user_id = :OLD.mmtp_user_id

You're reading the table being updated/deleted, so you get mutating table error.

Maybe if you just verify that

SELECT COUNT (*)
          FROM payment p 
          WHERE :OLD:customer_id = p.requester_id
            and p.rp_reason_id = 20  

is greater than zero, is enough?

Upvotes: 2

APC
APC

Reputation: 146219

The reason you get a compilation error is that we cannot use SELECT statements in IF clauses. So you need to break it out like this:

....
DECLARE
    n pls_integer;
BEGIN
    SELECT COUNT (*)
    into n
    FROM payment p INNER JOIN mmtp_user mt
               ON mt.customer_id = p.requester_id
    WHERE p.rp_reason_id = 20 AND mt.mmtp_user_id = :OLD.mmtp_user_id
-- for these cases delete not allowed
   IF n > 0
   THEN
....

Having solved that you will find, as Florin points, that you will get a runtime mutating table error. Oracle throws this error when we put a query in a trigger which accesses the table that owns the trigger. It happens because the table is in an unstable state and the outcome of the query is unpredictable.

Mutating table errors are almost always an indicator of a bad data model, with the culprit being insufficient normalization. That means fixing them requires a knowledge of the business rules. In this case the obvious solution would be:

SELECT COUNT (*)
into n
FROM payment p 
WHERE p.requester_id = :OLD.customer_id
AND p.rp_reason_id = 20 

This would work if there is a one-to-one relationship between user_id and customer_id. It there is not such a relationship you'll have to figure out a solution using your knowledge of your business rules.

Incidentally, using COUNT(*) to test for existence is not a solution which scales. It will take a long time for a customer with many payments. That's a lot of wasted effort when all you require is that they have at least one such record. Adding and rownum = 1 into the query will reduce the needless work.

Upvotes: 5

Related Questions