2016-04-24 50 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检测第一白色空间,然后用形成的子字符串找到正确的条目。