2016-04-24 51 views
1

因此,我正在嘗試編寫一個Oracle觸發器,它將插入到一個表中,其中給定的值是另一個表中外鍵的錯誤名稱。因此,觸發器需要將該值更改爲正確的名稱,而不是給定的名稱。Oracle PL/SQL觸發器 - 用錯誤名稱捕獲插入

我的數據庫表是建立像這樣:

SIGHTINGS (NAME, PERSON, LOCATION, SIGHTED) 
FEATURES (LOCATION, CLASS, LATITUDE, LONGITUDE, MAP, ELEV) 
FLOWERS (GENUS, SPECIES, COMNAME) 

該表具有以下語義:

  • 目擊給出描述每一次信息,該俱樂部的成員觀察野花之一在表格中描述。 NAME告訴觀察到的花的名字,PERSON描述誰看到了這朵花,LOCATION告訴附近地理特徵的名字,花的位置被看到,SIGHTED告訴花被看到的那一天。
  • FLOWERS列出了俱樂部成員試圖找到的所有鮮花。屬和種給學名花,和COMNAME給出了非學名(SIGHTING.NAME是一個外鍵FLOWER.COMNAME)

我試圖建立映入插入到觸發SIGHTINGS表格,當用戶不小心插入花朵的科學名稱而不是通用名稱時。例如,他們可以使用:

INSERT INTO SIGHTINGS VALUES 
    ('Chaenactis douglasii', 'Person A', 'Shirley Peak', TO_DATE('18-Aug-06', 'DD-MON-YY')); 

相反的:

INSERT INTO SIGHTINGS VALUES 
    ('Douglas dustymaiden', 'Person A', 'Shirley Peak', TO_DATE('18-Aug-06', 'DD-MON-YY')); 

我想觸發捕獲這個問題,並顯示一條警告消息到屏幕上,然後插入通用名稱到數據庫,而不是拉丁名字。

我的代碼看起來像這樣:

CREATE OR REPLACE TRIGGER Used_Latin_Name 
BEFORE INSERT ON SIGHTINGS 
FOR EACH ROW 
WHEN (NEW.NAME IS NOT NULL) 
DECLARE 
    -- local variables 
    inserted_name VARCHAR2(30); 
    comm_name  VARCHAR2(30); 
    Invalid_name  EXCEPTION; 
    Valid_name  EXCEPTION; 
    -- local cursor 
    CURSOR c (name_in VARCHAR2) IS 
     SELECT COMNAME 
     FROM FLOWERS 
     WHERE COMNAME = name_in; 

BEGIN 
    -- open cursor and fetch a match 
    OPEN c(:NEW.NAME); 
    FETCH c INTO inserted_name; 
    CLOSE c; 

    -- Raise an exception when foreign key is invalid 
    IF inserted_name IS NULL THEN 
     RAISE Invalid_name; 
    ELSE 
     RAISE Valid_name; 
    END IF; 
EXCEPTION 
    WHEN Invalid_name THEN 
     CREATE INDEX genusindex on FLOWERS(GENUS) 
      indextype is ctxsys.ctxrule; 
     SELECT COMNAME FROM FLOWERS 
     INTO comm_name 
     WHERE matches(GENUS, :NEW.NAME) > 0; 
     DROP INDEX genusindex; 
     DBMS_OUTPUT.PUT_LINE ('Warning: Your insert into the SIGHTINGS table seemed to use the Latin name "' || :NEW.NAME || '" for the flower "' || comm_name '". I used the common name instead.'); 
    WHEN Valid_name 
     NULL; 
END; 
/

我得到一個錯誤,雖然你不能創造一個我創建的索引的索引,所以我想知道是否有另一種辦法,而不是做其他我正在做的方式,或者如果有其他地方我想插入索引。我到處尋找幫助,並沒有運氣找出答案。

+0

請僅用您實際使用的數據庫標記您的問題。 –

+3

首先,您不能在觸發器中創建索引。其次,你不會想。創建索引,特別是Oracle文本索引是一個緩慢的操作。創建索引只是爲了稍後放下索引是沒有意義的。第三,你是否真的需要一個Oracle Text索引來做你想要的查詢,而不僅僅是擁有一個標準索引並進行直接的相等檢查?第四,如果你真的有外鍵約束,那麼在觸發器觸發前可能會違反這個約束。第五,這可能不是應該在觸發器中完成的事情,它屬於應用程序。 –

+0

你的解決方案中有更多的問題 - 如果'從'FLOWERS INTO comm_name WHERE匹配(GENUS,:NEW。NAME)> 0;'返回多行,你會得到一個錯誤。另一個問題 - 「DBMS_OUTPUT」通常用於調試,而不是將消息打印給用戶。如果你在生產代碼中使用它,你遲早會得到'ORA-20000 DBMS buffer overlow'。 – krokodilko

回答

0

UPDATE

我想出了一個解決方案,我試圖做的,代碼看起來像這樣:

CREATE OR REPLACE TRIGGER Used_Latin_Name 
BEFORE INSERT ON SIGHTINGS 
FOR EACH ROW 
WHEN (NEW.NAME IS NOT NULL) 
DECLARE 
    -- local variables 
    inserted_name VARCHAR2(30); 
    comm_name  VARCHAR2(30); 
    Invalid_name EXCEPTION; 
    Valid_name  EXCEPTION; 
    -- local cursor 
    CURSOR c (name_in VARCHAR2) IS 
     SELECT COMNAME 
     FROM FLOWERS 
     WHERE COMNAME = name_in; 
BEGIN 
    -- open cursor and fetch a match 
    OPEN c(:NEW.NAME); 
    FETCH c INTO inserted_name; 
    CLOSE c; 


    -- Raise an exception when foreign key is invalid 
    IF inserted_name IS NULL THEN 
     RAISE Invalid_name; 
    ELSE 
     RAISE Valid_name; 
    END IF; 
EXCEPTION 
    WHEN Invalid_name THEN 
     SELECT COMNAME 
     INTO comm_name 
     FROM FLOWERS 
     WHERE GENUS = SUBSTR (:NEW.NAME, 1, INSTR(:NEW.NAME, ' ') - 1); 
     DBMS_OUTPUT.PUT_LINE ('Warning: Your insert into the SIGHTINGS table  seemed to use the Latin name ''' || :NEW.NAME || ''' for the flower ''' || comm_name || '''. I used the common name instead.'); 
     :NEW.NAME := comm_name; 
    WHEN Valid_name THEN 
     NULL; 
END; 
/

我用SUBSTR和INSTR檢測第一白色空間,然後用形成的子字符串找到正確的條目。