views:

23

answers:

2

For a certain assignment, I'm tasked with creating a very simple Q/A site, complete with tagged questions and commented answers.

One of the requirements is to have a trigger to detect the insertion of duplicate tags and, if such is the case, increment the already-existing tag's usage counter.
Problem is, I can't cancel the trigger with rolling back the entire transaction, including the UPDATE, which defeats the purpose of the trigger.

Any ideas on how to pull it off?

UPDATE

The requirement's phrased like follows:

"Create the trigger that checks if any tag to be added doesn't previously exist in the database. In case it exists, the use column in the corresponding row must be incremented by 1"
(Original language: "Crear el trigger tg_insertar_tag que revise que cualquier nuevo tag que se agregue no exista antes en la base de datos; en caso de existir, se debe incrementar en 1 la columna “usos” de la tabla tag del registro que corresponda")

This requirement can't be changed or avoided, although loopholes would be welcome.


For reference, my current trigger code:

CREATE OR REPLACE TRIGGER tg_insertar_tag BEFORE INSERT ON Tag
FOR EACH ROW
DECLARE
    tagCount integer;
    v_usos integer;
BEGIN
    SELECT COUNT(*) INTO tagCount FROM Tag WHERE nombre = :new.nombre;
    SELECT Usos INTO v_usos FROM Tag WHERE nombre = :new.nombre;
    IF tagCount > 0 THEN
        UPDATE Tag SET usos = v_usos + 1 WHERE nombre = :new.nombre;
    ELSE
        :new.usos := 1;
    END IF;
END;
+1  A: 

That's not what the triggers on a table are for.

You should use a MERGE statement from a stored procedure, an INSTEAD OF trigger or just from the client:

MERGE
INTO    tag tc
USING   (
        SELECT  :NEW.nombre
        FROM    dual
        ) t
ON      (tc.nombre = t.nombre)
WHEN MATCHED THEN
UPDATE
SET     usos = usos + 1
WHEN NOT MATCHED THEN
INSERT  (nombre, usos)
VALUES  (nombre, 1)

Performance-wise, it would be best to pass all tags in a collection from the client and perform this query:

MERGE
INTO    tag tc
USING   (
        SELECT  tag, COUNT(*) AS cnt
        FROM    TABLE(:mycollection)
        GROUP BY
                nombre
        ) t
ON      (tc.nombre = t.nombre)
WHEN MATCHED THEN
UPDATE
SET     usos = usos + cnt
WHEN NOT MATCHED THEN
INSERT  (nombre, usos)
VALUES  (nombre, cnt)

in the stored procedure which would accept the collection as a parameter.

Quassnoi
That's be smart. And not allowed. The trigger has to exist, no matter what.
Kyte
Talking with a couple classmates, I realized the Instead Of trigger is actually what I was looking for. Thanks.
Kyte
A: 

or...

place this kind of logic on another table with the same or similar structure.

then when the insert passes, you use a post trigger logic to also insert it into the primary table.

Randy