简体   繁体   English

多行插入时触发器不工作/触发

[英]Trigger isn't working / fired while multi-row inserting

I've two tables in the DB called KistStatus and LastKistStatus . 我在DB中有两个名为KistStatusLastKistStatus The latter will hold all the 'newest' values of KistStatus . 后者将拥有KistStatus所有“最新”值。

KistStatus has around 174.000 records, LastKistStatus should have around 9.500 records. KistStatus有大约174.000条记录, LastKistStatus应该有大约9.500条记录。

If I insert the records by hand (execute the insert one by one by hand), the LastKistStatus will be filled properly. 如果我手动插入记录(手动逐个执行插入), LastKistStatus将被正确填充。 But If I execute the sql export file, the trigger seems to be broken. 但是,如果我执行sql导出文件,触发器似乎被破坏了。

The trigger: 触发:

DROP TRIGGER IF EXISTS `KistStatusTrigger`;
DELIMITER //
CREATE TRIGGER `KistStatusTrigger` AFTER INSERT ON `KistStatus`
 FOR EACH ROW begin
       DECLARE id_exists Boolean;
       DECLARE time_exists Timestamp;

       -- Check LastKistStatus table
       SELECT 1, `Timestamp`
         INTO @id_exists, @time_exists
       FROM LastKistStatus
       WHERE LastKistStatus.idKist = NEW.idKist;

       IF @id_exists > 0
       THEN
            IF @time_exists <= NEW.Timestamp
            THEN
                UPDATE LastKistStatus SET `Timestamp` = NEW.Timestamp, `idPartij` = NEW.idPartij, `Actie` = NEW.Actie, `idTaak` = NEW.idTaak, `idBewaarplaats` = NEW.idBewaarplaats, `Rij` = NEW.Rij, `Stapel` = NEW.Stapel, `Hoogte`= NEW.Hoogte, `KnolAantal` = NEW.KnolAantal, `Gewicht` = NEW.Gewicht, `idGebruiker` = NEW.idGebruiker, `Laatste` = NEW.Laatste WHERE `idKist` = NEW.idKist;
            END IF;
        ELSE
            INSERT INTO LastKistStatus (`idKistStatus`, `idKist`, `Timestamp`, `idPartij`, `Actie`, `idTaak`, `idBewaarplaats`, `Rij`, `Stapel`, `Hoogte`, `KnolAantal`, `Gewicht`, `idGebruiker`, `Laatste` ) VALUES (NEW.idKistStatus, NEW.idKist, NEW.Timestamp, NEW.idPartij, NEW.Actie, NEW.idTaak, NEW.idBewaarplaats, NEW.Rij, NEW.Stapel, NEW.Hoogte, NEW.KnolAantal, NEW.Gewicht, NEW.idGebruiker, NEW.Laatste);
        END IF;
END
//
DELIMITER ;

Insert command (done from command line) mysql -u *** -p TTTDB < KistStatus.sql 插入命令(从命令行完成) mysql -u *** -p TTTDB < KistStatus.sql

And the KistStatus.sql have a lot of lines like this: 并且KistStatus.sql有很多这样的行:

INSERT INTO `KistStatus` (`idKistStatus`, `idKist`, `Timestamp`, `idPartij`, `Actie`, `idTaak`, `idBewaarplaats`, `Rij`, `Stapel`, `Hoogte`, `KnolAantal`, `Gewicht`, `idGebruiker`, `Laatste`) VALUES
(1, 227862, '2015-09-04 14:15:29', 40, '3', 0, 260, 2060, 33980, 1, NULL, 0, 40, 1),
(21, 229522, '2015-09-04 14:15:29', 40, '3', 0, 260, 2060, 33980, 2, NULL, 0, 40, 1),
(1083, 51102, '2015-09-05 07:33:37', 80, '3', 0, 40, 440, 5060, 1, NULL, 0, 100, 1),
(1103, 51102, '2015-09-05 07:33:44', 80, '3', 0, 40, 440, 5060, 1, NULL, 0, 100, 2),
(1123, 51102, '2015-09-05 07:33:44', 80, '3', 0, 40, 440, 5060, 1, NULL, 0, 100, 1),
(1143, 22202, '2015-09-05 07:37:33', 80, '3', 0, 40, 420, 4820, 1, NULL, 0, 100, 1);

I also have tested it with and insert per record, but that didn't changed anything (other than taking forever to insert...) 我也测试了它并插入每条记录,但这没有改变任何东西(除了永远插入...)

Sow what am I doing wrong? 播种我做错了什么? Is there a problem in my trigger? 我的触发器有问题吗? Wouldn't it be triggered properly while inserting it from the command line? 从命令行插入时不会被正确触发吗? I'm out of ideas at the moment 我现在没有想法

Your Problem 你的问题

MySQL, somewhat confusingly, has two different types of variable: MySQL有点混乱,有两种不同类型的变量:

  • local variables ( not prefixed by @ ), which are strongly typed and scoped to the stored program block in which they have been declared with a DECLARE statement; 局部变量不以 @ 为前缀 ),它们是强类型的,并且作用于使用DECLARE语句声明它们的存储程序块; and

  • user variables ( prefixed by @ ), which are loosely typed and scoped to the session. 用户变量 @ 为前缀 ),它们被松散地键入并限定在会话中。 Note that they neither need nor can be declared—they are just used directly. 请注意,它们既不需要也不能声明 - 它们只是直接使用。

You declare local variables named id_exists and time_exists , but thereafter use user variables named @id_exists and @time_exists . 您声明了名为id_existstime_exists 局部变量 ,但之后使用名为@id_exists@time_exists 用户变量 Since the latter are scoped to the session, they retain their values between subsequent invocations of the trigger within the same session—and, consequently, once @id_exists has been set to 1 no further records will be inserted. 由于后者的范围限定为会话,因此它们在同一会话中的触发器的后续调用之间保留其值 - 因此,一旦将@id_exists设置为1将不再插入其他记录。

The obvious solution is not to use user variables in this case, but instead to stick with your local variables: ie remove the @ prefixes throughout your trigger. 显而易见的解决方案是在这种情况下不使用用户变量,而是坚持使用本地变量:即在整个触发器中删除@前缀。

Better Approaches 更好的方法

  1. Reduce data duplication 减少数据重复

    Unless you really need to, don't store a copy of every column from KistStatus in LastKistStatus —you can simply store (idKist, idKistStatus) and fetch the underlying record as/when required. 除非您确实需要,否则不要在LastKistStatus存储KistStatus中每一列的副本 - 您只需存储(idKist, idKistStatus)并在需要时获取基础记录。

     ALTER TABLE LastKistStatus DROP COLUMN Timestamp, DROP COLUMN idPartij, DROP COLUMN Actie, DROP COLUMN idTaak, DROP COLUMN idBewaarplaats, DROP COLUMN Rij, DROP COLUMN Stapel, DROP COLUMN Hoogte, DROP COLUMN KnolAantal, DROP COLUMN Gewicht, DROP COLUMN idGebruiker, DROP COLUMN Laatste; 

    Then, whenever you referenced LastKistStatus before, you can now simply reference LastKistStatus NATURAL JOIN KistStatus in its place and get the same results. 然后,每当您之前引用LastKistStatus ,您现在可以简单地在其位置引用LastKistStatus NATURAL JOIN KistStatus并获得相同的结果。

  2. Learn about INSERT ... ON DUPLICATE KEY UPDATE . 了解INSERT ... ON DUPLICATE KEY UPDATE

    Given the logic in your trigger, it would appear that LastKistStatus will only ever contain a single record with any given idKist . 给定触发器中的逻辑,看起来LastKistStatus将只包含任何给定idKist的单个记录。 By enforcing this constraint in the database, you can do some clever stuff: 通过在数据库中强制执行此约束,您可以执行一些聪明的操作:

     ALTER TABLE LastKistStatus ADD UNIQUE KEY (idKist); 

    Here I'm adding a UNIQUE KEY to the table. 在这里,我正在为表添加一个UNIQUE KEY However, it's quite probable that this could (and should) be the table's PRIMARY KEY instead, which has the same behaviour (but is a bit faster, given its special status). 但是,很可能这(而且应该)是表的PRIMARY KEY ,它具有相同的行为(但考虑到它的特殊状态,它会更快一些)。

    Now, instead of testing whether a record already exists and then inserting or updating accordingly, you can combine the operations into one: 现在,您可以将操作合并为一个,而不是测试记录是否已存在然后相应地插入或更新:

     INSERT INTO LastKistStatus (idKist, idKistStatus) VALUES (NEW.idKist, NEW.idKistStatus) ON DUPLICATE KEY UPDATE idKistStatus = NEW.idKistStatus; 
  3. Observe that LastKistStatus is really just a property of Kist 观察到LastKistStatus实际上只是Kist一个属性

    I presume you have a table Kist . 我猜你有一张桌子Kist You could simply move idKistStatus from the LastKistStatus table into that one, and do away with the LastKistStatus table altogether. 您可以简单地将idKistStatusLastKistStatus表移动到该表中,并完全取消LastKistStatus表。

     ALTER TABLE Kist ADD COLUMN idKistStatus BIGINT UNSIGNED NULL DEFAULT NULL, ADD FOREIGN KEY (idKistStatus) REFERENCES KistStatus (idKistStatus); UPDATE Kist JOIN LastKistStatus USING (idKist) SET Kist.idKistStatus = LastKistStatus.idKistStatus; 
  4. Do away with the trigger and cache altogether 完全取消触发器和缓存

    By finding the groupwise maximum from the KistStatus table, you can locate the most recent status without maintaining LastKistStatus at all: 通过从KistStatus表中查找KistStatus 最大值 ,您可以在不保留LastKistStatus情况下找到最新状态:

     SELECT * FROM KistStatus NATURAL JOIN ( SELECT idKist, MAX(Timestamp) AS Timestamp FROM KistStatus GROUP BY idKist ) t; 

    This would be sargable with an index over (idKist, Timestamp) : 索引超过(idKist, Timestamp)可能会出现这种情况:

     ALTER TABLE KistStatus ADD INDEX (idKist, Timestamp); 

    However, notice that it will return all records from each group that have the maximal timestamp—should you have a preference for one in particular, you will need to define the logic for identifying which. 但是,请注意它将返回具有最大时间戳的每个组中的所有记录 - 如果您特别优先选择一个,则需要定义用于标识哪个的逻辑。

    You can even create a view to yield the same results as you used to obtain from LastKistStatus : 您甚至可以创建一个视图,以产生与从LastKistStatus获取的结果相同的结果:

     CREATE VIEW LastKistStatus AS SELECT * FROM KistStatus NATURAL JOIN ( SELECT idKist, MAX(Timestamp) AS Timestamp FROM KistStatus GROUP BY idKist ) t; 

    Then use it as before: 然后像以前一样使用它:

     SELECT * FROM LastKistStatus WHERE ... 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM