【问题标题】:Trigger to Insert New Row in Records Table触发器在记录表中插入新行
【发布时间】:2014-06-14 19:39:25
【问题描述】:

我有两张桌子。

第一个表是带有模式(Train_Number、Route_Status、Time_Status、Route_TM、Departure、Destination)的 Routes,第二个表是带有模式(Train_Number、Time_Status、Route_TM、Departure、Destination)的 Records。

我需要一个触发器,当 Route Status 从“En-Route”变为“Arrived”时,它将(Train_Number、Time_Status、Date、Departure、Destination)从 Routes 记录到 Records。

还有另一个表配置与模式(名称,值)。特别是,在那个表中,我有 ('Enable_Logging', 'T')

这是我对这个触发器的尝试:

CREATE OR REPLACE TRIGGER Train_Logging
AFTER UPDATE OF Route_Status ON Routes

FOR EACH ROW
WHEN (new.route_status = 'Arrived' AND old.route_status = 'En-Route')

DECLARE
logging_flag Configuration.value%type;

BEGIN

SELECT Configuration.value INTO logging_flag FROM Configuration WHERE Configuration = 'Enable_Logging';

IF (logging_flag = 'T') THEN INSERT INTO Records (Train_Number, Time_Status, Date, Departure, Destination) VALUES (SELECT Train_Number, Time_Status, Date, Departure, Destination FROM Routes);
END IF;
END Train_Logging;

我的错误是 “忽略 PL/SQL 语句”(在 logging_flag Configuration.value%type; 处) 和“ORA-00936 缺少表达式”(在 BEGIN)

我的声明有什么问题?

【问题讨论】:

  • 您描述的表定义有一个名为date 的列,它是一个保留字,因此除非您创建引用它,否则它不会被称为;并且您没有一个名为 status 的列,正如您的触发器所期望的那样。显示您的实际表 DDL 可以更轻松地查看真正的问题。
  • 对不起,我重命名了所有内容,所以我不必发布很多不必要的脱离上下文的东西

标签: oracle plsql


【解决方案1】:

如上所述,忽略架构的问题 - 您似乎有一个名为 date 的字段是不允许的,除非它被引用,并且您正在检查一个不存在您显示的表描述的 status 字段,并且您在 select 中引用 configuration.configuration 而不是 configuration.name - 稍作调整,您会得到如下错误堆栈:

8/122          PL/SQL: ORA-00936: missing expression
8/30           PL/SQL: SQL Statement ignored

第 8 行似乎是 logging_flag 声明;但实际上错误中的行号指的是 PL/SQL 块,而不是之前的触发器定义,因此第 8 行实际上是您语句中的第 14 行。如果您将该行拆分,以便 insert 进入新行,values 也进入新行,则堆栈变为:

10/9           PL/SQL: ORA-00936: missing expression
9/1            PL/SQL: SQL Statement ignored

所以insert 被忽略,因为values 子句有问题。如果您确实想从选择中插入,则根本不会使用 values 关键字;你会这样做:

INSERT INTO Records (Train_Number, Time_Status, Train_Date,
  Departure, Destination)
SELECT Train_Number, Time_Status, Train_Date, Departure, Destination
FROM Routes;

(您可以在values 子句中使用select,但只能获取单个值,然后它会在另一组括号中,这不是您想要的。)

但您不希望或不需要从要插入的表中进行选择。您的select 没有where 子句,因此每次更新任何行时,您都会将整个routes 表复制到records 表中,这肯定不是您想要的。您只想复制受更新影响的特定行。但是你不需要select,你有newold伪行中的所有值:

INSERT INTO Records (Train_Number, Time_Status, Train_Date,
  Departure, Destination)
VALUES (:NEW.Train_Number, :NEW.Time_Status, :NEW.Train_Date,
  :NEW.Departure, :NEW.Destination);

所以假设您的date 列确实被称为train_date,并且您想检查route_status 是否已更改,它将如下所示:

CREATE OR REPLACE TRIGGER Train_Logging
AFTER UPDATE OF Route_Status ON Routes
FOR EACH ROW
WHEN (new.route_status = 'Arrived' AND old.route_status = 'En-Route')
DECLARE
  logging_flag Configuration.value%type;
BEGIN
  SELECT Configuration.value
  INTO logging_flag
  FROM Configuration
  WHERE Name = 'Enable_Logging';

  IF (logging_flag = 'T') THEN
    INSERT INTO Records (Train_Number, Time_Status, Train_Date,
      Departure, Destination)
    VALUES (:NEW.Train_Number, :NEW.Time_Status, :NEW.Train_Date,
      :NEW.Departure, :NEW.Destination);
  END IF;
END Train_Logging;
/

使用一些虚构的数据,并猜测您的一些数据类型:

insert into configuration values ('Enable_Logging', 'T');
insert into routes values (1, 'En-Route', 'On-time', trunc(sysdate),
  trunc(sysdate) + 10/24, 'New York');
update routes set route_status = 'Arrived' where train_number = 1;
select * from records;

TRAIN_NUMBER TIME_STATUS TRAIN_DATE DEPARTURE DESTINATION
------------ ----------- ---------- --------- -----------
           1 On-time     29-APR-14  29-APR-14 New York    

顺便说一句,将状态值保留为字符串会使它们更难管理,因为您必须始终以正确的大小写输入所有内容。通常,最好有一个带有数字主键的状态代码查找表,并让这些表带有该主状态表的外键。然后你插入键值而不是文本,所以你不必担心大小写等,如果措辞发生变化,你只需在一个地方更新它。当然,您必须加入该表才能检索文本值,但这并不是什么大问题。

【讨论】:

  • 这实际上教会了我两件事:错误代码没有给出实际的行,以及用于插入值的更清晰(和有效)的语法。为了提供更清晰的上下文,我确实更改了操作中的一些字段名称。真实的东西要混乱得多。
  • @KyleGrage - 错误代码确实给出了实际行,但相对于 PL/SQL 代码,这与触发器特别混淆,因为它们是 SQL 和 PL/SQL 的混合体。很高兴它有所帮助 *8-)
猜你喜欢
  • 2013-06-11
  • 2020-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多