2014-11-26 2 views
2

Я довольно новый (как для чтения, о-он-5-часовой давности новый) в триггерах, поэтому мне нужна помощь по этому вопросу:DB2 SQL Триггеры

Создать таблицу TelefonnummerAenderung и удалить все данные в нем, если он уже существует. Создание триггера, отвечающие следующим условиям: (я надеюсь, что это нормально для вас, ребята, если я не переводить все имена таблиц и атрибутов на английский язык.)

-a изменение атрибута Telefonnummer в KundenKontaktDaten разрешено только 15 сек после последнего изменения -otherwise в SIGNAL SQLSTATE '70001' выброшен

-когда изменения Telefonnummer в KundenKontaktDaten новая запись в TelefonnummerAenderungen создается содержащий старый Telefonnummer и время было изменено

-если новый и й е старые Telefonnummer то же самое, не провоцирующее действие не происходит

Эти две таблицы являются следующие:

KundenKontaktDaten: (редактирование: Я забыл, Kunden_Nr также ссылки Kunde(Kunden_Nr). Это не имеет значения для выполнения этой задачи, просто говоря)

create table KundenKontaktDaten 
(Kunden_Nr int not null primary key, 
    Twitter_Id Varchar(40), 
    Google_Id bigint, 
    Facebook_Id bigint, 
    Skype_Id Varchar(64), 
    Telefonnummer Varchar(50) 
); 

TelefonnummerAenderungen:

create table TelefonnummerAenderungen 
(GEAENDERT_AM TIMESTAMP NOT NULL, 
    KUNDEN_NR INTEGER NOT NULL, 
    ALTE_NUMMER VARCHAR(50), 
    FOREIGN KEY(KUNDEN_NR) 
    REFERENCES KUNDE(KUNDEN_NR) 
    ON DELETE CASCADE 
    ON UPDATE RESTRICT, 
    PRIMARY KEY(GEAENDERT_AM, KUNDEN_NR) 
); 

мое решение (Java-код, не имеет значения в отношении вопроса):

static public void triggerAnlegen(Connection con) throws SQLException { 
    Statement stmt = con.createStatement(); 
    try { 
     stmt.execute("create table TelefonnummerAenderungen (GEAENDERT_AM TIMESTAMP NOT NULL, KUNDEN_NR INTEGER NOT NULL, ALTE_NUMMER VARCHAR(50), FOREIGN KEY(KUNDEN_NR) REFERENCES KUNDE(KUNDEN_NR) ON DELETE CASCADE ON UPDATE RESTRICT, PRIMARY KEY(GEAENDERT_AM, KUNDEN_NR))"); 
    } catch (SQLException e) { 
     if(e.getErrorCode()==-601){ 
      stmt.execute("DELETE FROM TelefonnummerAenderungen"); 
     } 
    } 
    try{ 
     stmt.execute("CREATE TRIGGER haTrigger AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten REFERENCING NEW as n_row OLD as o_row FOR EACH ROW WHEN (o_row.Telefonnummer<>n_row.Telefonnummer) "+ 
         "BEGIN " + 
          "IF(NOT EXISTS(SELECT * FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr)) " + 
          "THEN " + 
           "INSERT INTO TelefonnummerAenderungen VALUES (CURRENT TIMESTAMP,n_row.Kunden_Nr,o_row.Telefonnummer); " + 
          "ELSEIF(CURRENT TIMESTAMP<((SELECT GEAENDERT_AM FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds)) " + 
          "THEN " + 
           "UPDATE KundenKontaktDaten SET Kunden_Nr=o_row.Kunden_Nr,Twitter_Id=o_row.Twitter_Id,Google_Id=o_row.Google_Id,Facebook_Id=o_row.Facebook_Id,Skype_Id=o_row.Skype_Id,Telefonnummer=o_row.Telefonnummer;" + 
           "SIGNAL SQLSTATE '70001'; " + 
          "ELSE " + 
           "UPDATE TelefonnummerAenderungen SET GEAENDERT_AM=CURRENT TIMESTAMP,ALTE_NUMMER=o_row.Telefonnummer WHERE Kunden_Nr=n_row.Kunden_Nr; " + 
          "END IF;" + 
         "END"); 
    } catch (SQLException e) { 
     throw e; 
    } 
} 

SQL- только:

CREATE TRIGGER haTrigger 
    AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten 
    REFERENCING 
    NEW as n_row 
    OLD as o_row 
    FOR EACH ROW WHEN (o_row.Telefonnummer <> n_row.Telefonnummer) 
    BEGIN 
    IF (NOT EXISTS (SELECT * 
        FROM TelefonnummerAenderungen 
        WHERE Kunden_Nr=n_row.Kunden_Nr) 
     ) 
    THEN 
     INSERT INTO TelefonnummerAenderungen 
     VALUES 
     (CURRENT TIMESTAMP, n_row.Kunden_Nr, o_row.Telefonnummer); 
    ELSEIF (CURRENT TIMESTAMP < ((SELECT GEAENDERT_AM 
            FROM TelefonnummerAenderungen 
            WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds) 
      ) 
    THEN 
     UPDATE KundenKontaktDaten 
     SET Kunden_Nr = o_row.Kunden_Nr, 
      Twitter_Id = o_row.Twitter_Id, 
      Google_Id = o_row.Google_Id, 
      Facebook_Id = o_row.Facebook_Id, 
      Skype_Id = o_row.Skype_Id, 
      Telefonnummer = o_row.Telefonnummer; 
     SIGNAL SQLSTATE '70001'; 
    ELSE 
     UPDATE TelefonnummerAenderungen 
     SET GEAENDERT_AM = CURRENT TIMESTAMP, 
      ALTE_NUMMER = o_row.Telefonnummer 
     WHERE Kunden_Nr = n_row.Kunden_Nr; 
    END IF; 
    END 

Я использовал AFTER UPDATE OF [...], потому что не был уверен, что (при использовании BEFORE UPDATE OF [...]) запрос, вызвавший запуск триггера, все еще выполняется после обработки триггера.

[редактировать: изменил его BEFORE и выронил

UPDATE KundenKontaktDaten 
SET Kunden_Nr = o_row.Kunden_Nr, 
    Twitter_Id = o_row.Twitter_Id, 
    Google_Id = o_row.Google_Id, 
    Facebook_Id = o_row.Facebook_Id, 
    Skype_Id = o_row.Skype_Id, 
    Telefonnummer = o_row.Telefonnummer; 

, потому что я получаю каскадные ошибки запуска в противном случае; не изменит код здесь, хотя для вопроса прозрачности]

Это (очевидно) моя домашняя работа (не проблема реального мира, надеюсь, что все в порядке, и мы получаем градуировку автоматически с помощью некоторой тестовой функции, которую мы не знаем - таким образом, я знаю, что мои результаты неверны Но я не знаю, какая часть этого является неправильным, так что мне нужна помощь по этому


Edit 2:
Мой пост становится.. я думаю, что создание ответа было бы более ясным, чем дальнейшее редактирование моего сообщения:

После дополнительных испытаний я выяснил проблему с первой попытки:

При использовании 'ПОСЛЕ ОБНОВЛЕНИЯ ...'в моем триггере, слишком поздно, и я не могу отменить изменения, внесенные в «KundenKontaktDaten», хотя 15 секунд не прошло, так как это приведет к каскадным вызовам триггера.

При использовании «ПЕРЕД ОБНОВЛЕНИЕМ ...» DB2 ограничивает меня использованием любых INSERT или UPDATE.

Так я понял, что я должен использовать INSTEAD OF:

CREATE TRIGGER haTrigger INSTEAD OF UPDATE ON KundenKontaktDaten REFERENCING NEW as n_row OLD as o_row FOR EACH ROW 
     BEGIN 
    IF(o_row.Telefonnummer<>n_row.Telefonnummer) THEN 
        IF(NOT EXISTS(SELECT * FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr)) THEN 
      UPDATE KundenKontaktDaten SET Kunden_Nr=n_row.Kunden_Nr,Twitter_Id=n_row.Twitter_Id,Google_Id=n_row.Google_Id,Facebook_Id=n_row.Facebook_Id, 
      Skype_Id=n_row.Skype_Id,Telefonnummer=n_row.Telefonnummer WHERE Kunden_Nr=o_row.Kunden_Nr; 
         INSERT INTO TelefonnummerAenderungen VALUES (CURRENT TIMESTAMP,n_row.Kunden_Nr,o_row.Telefonnummer); 
        ELSEIF(CURRENT TIMESTAMP<((SELECT GEAENDERT_AM FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds)) THEN 
         SIGNAL SQLSTATE '70001'; 
        ELSE 
      UPDATE KundenKontaktDaten SET Kunden_Nr=n_row.Kunden_Nr,Twitter_Id=n_row.Twitter_Id,Google_Id=n_row.Google_Id,Facebook_Id=n_row.Facebook_Id, 
      Skype_Id=n_row.Skype_Id,Telefonnummer=n_row.Telefonnummer WHERE Kunden_Nr=o_row.Kunden_Nr; 
         UPDATE TelefonnummerAenderungen SET GEAENDERT_AM=CURRENT TIMESTAMP,ALTE_NUMMER=o_row.Telefonnummer WHERE Kunden_Nr=n_row.Kunden_Nr; 
        END IF; 
      END IF; 
     END 

в результате еще одной ошибки:

DB2 SQL Error: SQLCODE=-159, SQLSTATE=42809, SQLERRMC=GRP13.KUNDENKONTAKTDATEN;TABLE;UNTYPED VIEW

Поскольку я понятия не имею, кто-нибудь знает, что проблема с этой? Если ни Before, ни After, ни Instead of не работают, то у меня немного вариантов.

+0

Я предлагаю вам извлечь Java из картинки, пока вы не выясните триггер. Покажите только код SQL и объясните, как результаты выполнения триггера отличаются от ожидаемого. – mustaccio

+0

Я просто редактирую сообщение и добавляю только запрос sql. Кроме того, я провел некоторое дополнительное тестирование, и если я изменил атрибут «Telefonnummer» слишком быстро (<15 секунд друг от друга), теперь я получаю: Произошла ошибка в вызванном SQL-заявлении в триггере «GRP13.HATRIGGER». Информация, возвращаемая для ошибки, включает SQLCODE «-724», SQLSTATE «54038» <- не должен ли я получить sqlstate = 70001, так как это то, что бросает? –

+0

nvm, изменил часть с «ПОСЛЕ ОБНОВЛЕНИЯ ...» до «ПЕРЕД ОБНОВЛЕНИЕМ» -> теперь я получаю правильный SQLSTATE; все еще не работает, хотя, и у меня нет идеи, почему (как я уже сказал, его автоматическое тестирование этого, и у меня нет исходного кода для теста). Он работает, проверяя вручную. –

ответ

1

Я не думаю, что вам нужно включить UPDATE из KundenKontaktDaten в триггер, потому что таблица уже модифицирована оператором, вызывающим триггер. Вероятно, это должно выглядеть примерно так:

CREATE TRIGGER haTrigger 
AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten 
REFERENCING NEW as n_row OLD as o_row 
FOR EACH ROW 
WHEN (o_row.Telefonnummer<>n_row.Telefonnummer) 
BEGIN 
    IF EXISTS ( 
    SELECT 1 FROM TelefonnummerAenderungen a 
    WHERE a.Kunden_Nr = o_row.Kunden_Nr 
    AND a.GEAENDERT_AM > CURRENT TIMESTAMP - 15 seconds 
) 
    THEN -- within the 15 sec. window, record old telephone number 
    INSERT INTO TelefonnummerAenderungen 
    VALUES (CURRENT TIMESTAMP, o_row.Kunden_Nr,o_row.Telefonnummer); 
    ELSE -- too late 
    SIGNAL SQLSTATE '70001'; 
    END IF; 
END 

PS. Не испытано.

PPS. Вероятно, вы должны сохранить GEAENDERT_AM также в KundenKontaktDaten, чтобы избежать дополнительного запроса.

+0

Это работает, у меня явно было неправильное представление о триггерах: я думал, что это все равно изменит запись в KundenKontaktDaten, потому что это срабатывает после того, как это было сделано (ну, но это не так). Спасибо! –