Un par de puntos. Primero, está haciendo un mal uso del pragma de transacción autónomo. Está destinado a transacciones separadas que necesita confirmar o revertir independientemente de la transacción principal. Lo está utilizando para deshacer la transacción principal, y nunca se compromete si no hay ningún error.
¿Y esas "consecuencias imprevistas" que alguien mencionó? Una de ellas es que su conteo siempre devuelve 0. Así que elimine el pragma porque se está utilizando incorrectamente y para que el conteo devuelva un valor adecuado.
Otra cosa es no tener compromisos o reversiones dentro de los disparadores. Genere un error y deje que el código de control haga lo que debe hacer. Sé que los retrocesos se debieron al pragma. Simplemente no olvide eliminarlos cuando elimine el pragma.
El siguiente disparador funciona para mí:
CREATE OR REPLACE TRIGGER trg_mytable_biu
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
WHEN (NEW.TYPEB = 'Bert') -- Don't even execute unless this is Bert
DECLARE
L_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO L_COUNT
FROM MYTABLE
WHERE ARTICLE = :NEW.ARTICLE
AND TYPEB = :NEW.TYPEB;
IF L_COUNT > 0 THEN
RAISE_APPLICATION_ERROR( -20001, 'Bert already exists!' );
ELSIF :NEW.STOCK_COUNT > 1 THEN
RAISE_APPLICATION_ERROR( -20001, 'Can''t insert more than one Bert!' );
END IF;
END;
Sin embargo, no es una buena idea que un disparador en una tabla acceda por separado a esa tabla. Por lo general, el sistema ni siquiera lo permitirá; este activador no se ejecutará en absoluto si se cambia a "después". Si se permite su ejecución, uno nunca puede estar seguro de los resultados obtenidos, como ya descubrió. En realidad, estoy un poco sorprendido de que el disparador anterior funcione. Me sentiría incómodo usándolo en una base de datos real.
La mejor opción cuando un disparador debe acceder a la tabla de destino es ocultar la tabla detrás de una vista y escribir un disparador "en lugar de" en la vista. Eso trigger puede acceder a la tabla todo lo que quiera.