【问题标题】:Creating a trigger in SQL that reads data from one table, and inserts new data to another在 SQL 中创建一个触发器,该触发器从一个表中读取数据,并将新数据插入到另一个表中
【发布时间】:2015-07-29 10:56:59
【问题描述】:

我在介绍数据库类时遇到了一些麻烦,无法理解我在 SQL 中关于触发器可以做什么和不可以做什么。尤其是一个问题令人困惑。

我得到了这个架构(可以查看here):

CREATE TABLE Sensor(
    sid int NOT NULL AUTO_INCREMENT,
    Name varchar(100),
    PRIMARY KEY(sid)
);

CREATE TABLE TemperatureSensor(
    sid int NOT NULL,
    metricSystem ENUM(‘Celsius’,’Kelvin’),
    PRIMARY KEY(sid),
    FOREIGN KEY(sid) REFERENCES Sensor(sid)
);

CREATE TABLE Observation(
    oid int NOT NULL,
    sid int NOT NULL,
    PRIMARY KEY(sid,oid),
    FOREIGN KEY(sid) REFERENCES Sensor(sid)
);

CREATE TABLE RawTemperature(
    oid int NOT NULL,
    sid int NOT NULL,
    temperature float,
    timestamp timestamp NOT NULL,
    PRIMARY KEY(sid,oid),
    FOREIGN KEY(sid,oid) REFERENCES Observation(sid,oid),
    FOREIGN KEY(sid) REFERENCES TemperatureSensor(sid)
);

CREATE TABLE Event(
    eid int NOT NULL AUTO_INCREMENT,
    activity ENUM(‘running’,’walking’,’entering’,’Too High Temperature’),
    confidence int unsigned,
    PRIMARY KEY(eid)
);

CREATE TABLE DerivedFrom(
    eid int NOT NULL,
    sid int NOT NULL,
    oid int NOT NULL,
    PRIMARY KEY(eid,oid,sid),
    FOREIGN KEY(eid) REFERENCES Event(eid),
    FOREIGN KEY(sid) REFERENCES Sensor(sid)
);  

有了这个,我应该创建一个“事件检测机制”,当来自RawTemperature 的温度太高时会发出警报。触发器检查温度是否大于 65 摄氏度,如果是,将适当更新 EventDerivedFrom 表。它将创建一个'Too High Temperature' 类型的Eventconfidence 为1。它还表明我们可以假设没有并发问题。

为了解决这个问题,我尝试了这个想法(有点伪代码):

CREATE TRIGGER TemperatureHigh
AFTER INSERT 
ON RawTemperature FOR EACH ROW
BEGIN
    UPDATE Event
    SET activity = ‘Too High Temperature’
INSERT INTO DerivedFrom(eid) VALUES (LAST_INSERT_ID())
    WHERE temperature > 65;
END;

但这给了我错误并且根本不起作用。那么如何正确使用触发器来完成此任务呢?

【问题讨论】:

    标签: mysql sql triggers


    【解决方案1】:

    我相信这是你想要的:

    delimiter //
    
    create trigger temperaturehigh after insert on rawtemperature
        for each row begin
            insert into event (activity, confidence) values ('Too High Temperature',1);
            insert into derivedfrom select max(eid), new.oid, new.sid from event;
    end
    //
    
    delimiter ;
    

    小提琴: http://sqlfiddle.com/#!9/65a01/1/0

    您会注意到,除了插入 rawtemperature 之外,我还插入了一些示例数据,因为否则您的外键将失败(假设您的小提琴不包含示例数据):

    insert into sensor values (1,'Test Sensor');
    insert into temperaturesensor values (1,'Celsius');
    insert into observation values (1,1);
    insert into rawtemperature values (1,1,66,'2015-01-01 12:00:00');
    

    带有您发布的 DDL 和上述示例数据的触发器似乎可以按您的预期工作。

    例如,当你运行时,插入 66 度(高于 65):

    select * from event;
    

    (如在小提琴中),你得到:

    | eid |             activity | confidence |
    |-----|----------------------|------------|
    |   1 | Too High Temperature |          1 |
    

    触发器正确地将关联行插入到event 表中。如果插入的角度为 65 度或更小,则不会。

    附带说明,您发布的 DDL 与您放入小提琴中的 DDL 不同(在一些地方)。我假设您在帖子正文中发布的 DDL 是您的实际 DDL。

    此外,由于触发器可能涉及 2+ 个语句(而您的语句),您必须暂时将分隔符更改为分号以外的其他内容(以便您的触发器可以被解释为一个整体。我使用了双斜杠 (@987654328 @)

    【讨论】:

      【解决方案2】:

      您并不遥远 - 需要注意的一点是,在触发器中,您可以访问伪行 newold,它们代表正在插入的每个新行或正在更新/删除的旧行(在此大小写到TemperatureHigh 表中)。

      您可以使用 new 上的列来检索观察结果和 sensorid 以插入到 Event + DerivedFrom 表中。

      另外,请注意,要求温度必须大于 65 Celsius,您需要重新加入 TemperatureSensor 定义,以便传感器查看它正在测量的内容。

      CREATE TRIGGER TemperatureHigh
      AFTER INSERT 
      ON RawTemperature FOR EACH ROW
      BEGIN
            IF (new.temperature > 65 AND EXISTS
               (SELECT 1 FROM TemperatureSensor WHERE sid = new.sid AND metricSystem = 'Celsius')) 
            THEN
              INSERT INTO `Event`(activity, confidence)
              VALUES('Too High Temperature', 1);
      
              INSERT INTO DerivedFrom(eid, sid, oid) 
              VALUES (LAST_INSERT_ID(), new.sid, new.oid);
            END IF;
      END
      

      SqlFiddle here with various test cases

      根据事件表的布局(AUTO_INCREMENT 和名称),在我看来,此表需要插入新事件而不是更新旧事件。

      【讨论】:

      • 这太棒了,感谢您的帮助和解释!我对SELECT 1 FROM.. 的声明有点困惑。为什么我们使用它而不是 SELECT *?
      • 在选择中放入什么实际上并不重要(我们正在检查是否存在一行而不是选择任何内容)。 select * 也会做exactly the same thing。我使用1,因为它清楚地表明没有使用列,希望它更易于阅读。
      • (我的意思当然是,当包裹在EXISTS 中时,您在select 中放入的内容并不重要)!此外,LAST_INSERT_ID() 将提供前面 insert into Event 行的 ID。
      • 啊,这很有意义。感谢您的所有帮助!你为我解决了很多困惑,我很感激。
      • 我想需要的一个强制性警告是要注意,尽管触发器非常强大,但它们通常被认为是不好的做法,因为它们发生在“幕后”并且具有许多触发器的系统可能难以调试 -在可能的情况下,倾向于将业务逻辑放入存储过程或应用程序代码中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多