如果og_id指的是取消的奥运会游戏,则触发提升引发有意义消息的应用程序错误

时间:2018-01-21 07:06:40

标签: oracle plsql database-trigger

以下是我创建的表格:

 CREATE TABLE Country 
(
 country_id NUMBER(3) PRIMARY KEY,
 country_name VARCHAR(3) UNIQUE NOT NULL,
 CONSTRAINT check_country_id CHECK (country_id > 0)
);

CREATE TABLE OG_Type 
(
 og_type_id NUMBER(3) PRIMARY KEY,
 og_type_title VARCHAR(20) UNIQUE NOT NULL
);

CREATE TABLE Olympic_Game 
(
 og_id NUMBER(3) PRIMARY KEY,
 og_type_id NUMBER(3) NOT NULL,
 og_year NUMBER(4) NOT NULL,
 og_website VARCHAR(150),
 og_cancel VARCHAR(1) NOT NULL,
 country_id NUMBER(3) NOT NULL,
 CONSTRAINT check_og_id CHECK (og_id > 0),
 CONSTRAINT check_og_year_og_type UNIQUE (og_type_id, og_year),
 CONSTRAINT fk_og_type_id FOREIGN KEY(og_type_id) REFERENCES OG_Type(og_type_id),
 CONSTRAINT fk_country_id FOREIGN KEY(country_id) REFERENCES Country(country_id)
);

CREATE TABLE Sport 
(
 sport_id NUMBER(3) PRIMARY KEY,
 sport_title VARCHAR(100) UNIQUE NOT NULL,
 CONSTRAINT check_sport_id CHECK (sport_id > 0)
);

CREATE TABLE Event 
(
 event_id NUMBER(6) PRIMARY KEY,
 sport_id NUMBER(3) NOT NULL,
 og_id NUMBER(3) NOT NULL,
 event_title VARCHAR(100) NOT NULL,
 event_team  VARCHAR(1) NOT NULL,
 no_per_team  NUMBER(2) NOT NULL,
 event_gender VARCHAR(1) NOT NULL,
 CONSTRAINT check_event_id CHECK (event_id > 0),
 CONSTRAINT check_event_title_sport_id_og_id_event_team_event_gender UNIQUE (event_title, sport_id, og_id, event_team, event_gender),
 CONSTRAINT check_event_team CHECK (event_team IN ('Y','N')),
 CONSTRAINT check_event_team_no_per_team CHECK ((event_team='N' AND no_per_team=1) OR (event_team='Y' AND no_per_team>1)),
 CONSTRAINT check_event_gender CHECK (event_gender IN ('M','F')),
 CONSTRAINT fk_sport_id FOREIGN KEY(sport_id) REFERENCES Sport(sport_id),
 CONSTRAINT fk_og_id FOREIGN KEY(og_id) REFERENCES Olympic_Game(og_id)
);

创建这4个表后,我插入了这4个表的值:

insert into country(country_id,country_name) values (country_seq.nextval,'FRA');
insert into country(country_id,country_name) values (country_seq.nextval,'GBR');
insert into country(country_id,country_name) values (country_seq.nextval,'GRE');
insert into country(country_id,country_name) values (country_seq.nextval,'USA');

insert into og_type(og_type_id, og_type_title) values(og_type_seq.nextval,'Summer');
insert into og_type(og_type_id, og_type_title) values(og_type_seq.nextval,'Winter');
insert into og_type(og_type_id, og_type_title) values(og_type_seq.nextval,'Special');
insert into og_type(og_type_id, og_type_title) values(og_type_seq.nextval,'Youth');
insert into og_type(og_type_id, og_type_title) values(og_type_seq.nextval,'Senior');

insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,1896,null,'N',3);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,1900,null,'N',1);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,1904,null,'N',4);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,1908,'op1908.org','N',2);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,2,1924,null,'N',1);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,1944,null,'Y',2);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,3,1944,null,'Y',2);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,4,1944,null,'Y',2);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,5,1944,null,'Y',2);
insert into olympic_game(og_id,og_type_id,og_year,og_website,og_cancel,country_id) values(og_seq.nextval,1,2012,'https://www.olympic.org/london-2012','N',2);

insert into sport(sport_id,sport_title) values(sport_seq.nextval, 'Track and Field');
insert into sport(sport_id,sport_title) values(sport_seq.nextval, 'Tennis');
insert into sport(sport_id,sport_title) values(sport_seq.nextval, 'Speed Skating');

insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,1,'100m','N',1,'M');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,2,1,'Double','Y',2,'M');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,2,'200m','N',1,'M');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,2,2,'Single','N',1,'M');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,3,'400m','N',1,'F');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,3,'100m','N',1,'F');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,4,'1500m','N',1,'F');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,3,5,'800m','N',1,'F');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,10,'100m','N',1,'M');
insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,10,'100m','N',1,'F');

现在这是个问题: 创建一个名为TR_event_on_cancelled_og的触发器。在插入或更新Event表中的行之前触发此触发器。如果og_id引用取消的奥运会,触发器应该使用有意义的消息引发应用程序错误。

现在这是我提出的解决方案:

CREATE OR REPLACE TRIGGER TR_event_on_cancelled  
BEFORE INSERT OR UPDATE
ON Event
FOR EACH ROW
BEGIN
DECLARE 
    v_og_cancel Olympic_Game.og_cancel%TYPE;
BEGIN
    SELECT og_cancel INTO V_og_cancel 
    FROM Olympic_Game, Event
    WHERE Olympic_Game.og_id = Event.og_id
    AND Olympic_Game.og_id = :NEW.og_id;
    IF (v_og_cancel = 'N')
    THEN
        RAISE_APPLICATION_ERROR(-20001, 'This Olympic Game is cancelled already');
    END IF;
END;
END;

Trigger TR_EVENT_ON_CANCELLED compiled

触发器编译成功但问题是当我尝试测试触发器时收到以下错误消息:

insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,8,'400m','N',1,'F');

Error report -
SQL Error: ORA-01403: no data found
ORA-06512: at "OG_JC480454.TR_EVENT_ON_CANCELLED", line 5
ORA-04088: error during execution of trigger 'OG_JC480454.TR_EVENT_ON_CANCELLED'
01403. 00000 -  "no data found"
*Cause:    No data was found from the objects.
*Action:   There was no data from the objects which may be due to end of fetch.

insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) values(event_seq.nextval,1,2,'400m','N',1,'F');

Error report -
SQL Error: ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "OG_JC480454.TR_EVENT_ON_CANCELLED", line 5
ORA-04088: error during execution of trigger 'OG_JC480454.TR_EVENT_ON_CANCELLED'
01422. 00000 -  "exact fetch returns more than requested number of rows"
*Cause:    The number specified in exact fetch is less than the rows returned.
*Action:   Rewrite the query or change number of rows requested

我哪里出错了? ... 如果提供了触发器解决方案代码,那将非常有用。

1 个答案:

答案 0 :(得分:0)

我认为您的问题是您的触发器代码是从EVENT中选择的。它不需要这样做,因为该规则仅适用于您正在插入或更新的当前事件。

所以:

SELECT og_cancel INTO V_og_cancel 
FROM Olympic_Game
WHERE Olympic_Game.og_id = :NEW.og_id;

顺便说一下,下一个测试看起来不对:

IF (v_og_cancel = 'N')

除非您以奇怪的方式使用Y / N标志,否则将测试游戏是否已取消 。当然应该是= 'Y'

  

“我想收到此错误消息”

我已经用您的数据和我的触发器版本创建了一个Oracle LiveSQL。 Find it here (but you will need an OTN account to look at it)

当我尝试这个INSERT ...

insert into event(event_id,sport_id,og_id,event_title,event_team,no_per_team,event_gender) 
values(event_seq.nextval,2,9,'Double','Y',2,'M'); 

它失败了

ORA-20001: This Olympic Game is cancelled already

非常正确,因为og_id = 9og_cancel='Y'的游戏。

现在你说你得到了ORA-20977: the game was cancelled。这是一个用户定义的错误(在-20999到-20000的范围内。所以它是你的代码库的一部分,它正在抛出它。

如果您不知道它是什么部分,您需要深入研究数据字典。我无法为你解决这个问题。但这是一个想法:你有不止一个触发器在桌子上?你的作业说创建一个名为TR_event_on_cancelled_og的触发器,但你发布的代码是CREATE OR REPLACE TRIGGER TR_event_on_cancelled。也许您之前使用其他名称创建了一个触发器,这就是提升ORA-20977的原因。