2015-04-10 4 views
0

Я создаю триггер A1, так что статья с определенным типом, то есть «Bert», не может быть добавлена ​​несколько раз, и она может иметь только 1 на складе. Однако, хотя я создаю триггер, я все же могу добавить статью с типом «Берт». Так или иначе, count возвращает «0», но когда я запускаю тот же оператор sql, он возвращает правильное число. Он также начинает правильно подсчитываться, если я удаляю триггер и снова добавляю его. Любые идеи, что может пойти не так?Count (*) не работает должным образом

TRIGGER A1 BEFORE INSERT ON mytable 
FOR EACH ROW 
DECLARE 
l_count NUMBER; 
PRAGMA AUTONOMOUS_TRANSACTION; 
BEGIN 
    SELECT COUNT(*) INTO l_count FROM mytable WHERE article = :new.article; 

    dbms_output.put_line('Count: ' || l_count); 
    IF l_count >0 THEN 
    IF(:new.TYPEB = 'Bert') THEN 
     dbms_output.put_line('article already exists!'); 
     ROLLBACK; 
    END IF; 
    ELSIF (:new.TYPEB = 'Bert' AND :new.stock_count>1) THEN 
    dbms_output.put_line('stock cannot have more than 1 of this article with type Bert'); 
    ROLLBACK; 
    END IF; 
END; 

Это заявление вставки я использую:

INSERT INTO mytable VALUES('Chip',1,9,1,'Bert'); 
+0

Count возвращает 0, потому что, когда вы вставляете его, в моей таблице нет строки, где статья такая же, как та, которую вы пытаетесь вставить. Во второй раз, когда вы запускаете свой SQL-запрос и запускаете триггер, он возвращает 1. Единственное объяснение этого поведения. Когда вы бросаете триггер, вы также удаляете записи, которые были добавлены ранее? –

+0

output.putline продолжает причислять «Count: 0» каждый раз, когда я вставляю. Не только в первый раз. – Sammy

+7

Почему вы не используете для этого уникальное ограничение? –

ответ

1

Несколько пунктов. Во-первых, вы злоупотребляете автономной транзакционной прагмой. Он предназначен для отдельных транзакций, которые необходимо совершать или откатываться независимо от основной транзакции. Вы используете его для отката основной транзакции - и никогда не выполняете, если нет ошибки.

И что такое «непредвиденные последствия»? Один из них состоит в том, что ваш счет всегда возвращает 0. Так что удалите прагму как из-за неправильного использования, так и для подсчета верните правильное значение.

Другое дело, что в триггерах нет обвинений или откатов. Возьмите ошибку и позвольте управляющему коду делать то, что нужно. Я знаю, что откаты были из-за прагмы. Просто не забудьте удалить их, когда вы удалите прагму.

Следующий триггер работает для меня:

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; 

Однако, это не очень хорошая идея для триггера на стол, чтобы отдельно получить доступ к этой таблице. Обычно система даже не позволяет это - этот триггер не будет выполняться вообще, если его изменить на «после». Если это разрешено исполнять, вы никогда не можете быть уверены в полученных результатах - как вы уже выяснили. На самом деле, я немного удивлен, что триггер над работами. Я бы чувствовал себя неловко, используя его в реальной базе данных.

Наилучший вариант, когда спусковой крючок должен доступ к целевой таблице - это скрыть таблицу за представлением и написать триггер «вместо» на представлении. То, что триггер может получить доступ к таблице всего, что она хочет.

+0

Просто следующий вопрос: зачем нам использовать триггер вместо «вместо»? – Sammy

+0

Поскольку нормальный до/после триггера является частью выполняемого оператора DML. Вот почему они не могут или не должны обращаться к базовой таблице - она ​​находится в середине инструкции Insert, Update или Delete, которая вызвала запуск триггера. Триггер вместо «триггера» выполняет «этот код вместо операции DML». Таким образом, основная таблица не участвует во вставке, обновлении или удалении. Сам код запуска должен инициировать DML, и он не должен быть одним и тем же DML. Вместо триггера delete может выполняться «набор таблиц обновлений is_delete = 'Y» для мягкого удаления. – TommCatt

0

Вам нужно сделать ПОСЛЕ запуска, а не перед срабатыванием. Выполняя подсчет (*) «ДО», вставка происходит с нулевыми строками, потому что данные еще не были вставлены.

Смежные вопросы