【问题标题】:MySQL BEFORE INSERT trigger to turn duplicate primary keys inserts into updatesMySQL BEFORE INSERT 触发器将重复的主键插入转换为更新
【发布时间】:2018-02-22 17:06:40
【问题描述】:

我正在尝试通过 phpmyadmin

在数据库中执行此查询
create trigger avoid_duplicated_sharing
before insert on sharingevents
for each row
begin
  if ( select count(*) from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to  > 0 ) then
      delete from sharingevents where shared_note_id = NEW.shared_note AND shared_to = NEW.shared_to
  END IF;
END

但是 phpmyadmin 给了我以下错误:

MySQL said: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'END IF' at line 7

两个问题:

  • 我的脚本有什么问题?
  • BEFORE INSERT 触发后,会执行INSERT 操作吗?如果不是,我将不得不删除INSERT INTO SharingEvents (SELECT * FROM NEW);

【问题讨论】:

  • 你不是把插入变成更新,而是把它变成同一个插入。
  • 你说得对,我把查询改成:delete from sharingevents where (shared_note = NEW.shared_note_id AND shared_to = NEW.shared_to);
  • INSERT INTO ... ON DUPLICATE KEY UPDATEINSERT IGNORE 查询有什么问题?它们不适合您的用例吗?
  • @Mjh 正如我所说我不能修改应用程序,所以我不能让它执行与 INSERT INTO 不同的查询
  • 这整个方法注定要失败,因为不允许触发器修改触发它们的同一个表。

标签: mysql sql triggers phpmyadmin sql-insert


【解决方案1】:

我用下面的代码解决了:

delimiter $$

create trigger avoid_duplicated_sharing
before insert on sharingevents
for each row
begin
    if ( select count(*) from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to  > 0 ) then
        delete from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to;
    end if;
END$$

问题在于分隔符。

即便如此,我的触发器还是不起作用。当应用程序插入重复的主键时,MySQL 会抛出以下错误:

#1442 - Can't update table 'sharingevents' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

【讨论】:

  • 触发器不允许修改它们触发的同一个表。
【解决方案2】:

使用exists:

if (exists (select 1 from sharingevents where shared_note_id = new.shared_note_id AND shared_to = new.shared_to) > 0)  then
    insert into sharingevents (shared_note_id,shared_to,permission_level) 
        values (NEW.shared_note_id,NEW.shared_to,NEW.permission_level);
end if;

或者,更好的是,在sharingevents(shared_note-id, shared_to) 上添加一个唯一索引,然后使用:

    insert into sharingevents (shared_note_id, shared_to, permission_level) 
        values (NEW.shared_note_id, NEW.shared_to, NEW.permission_level)
        on duplicate key update shared_note_id = values(shared_note_id);

这将忽略表中已存在对的任何更新。不需要if

【讨论】:

  • 同样的错误:/ #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'then insert into sharingevents (shared_note_id,shared_to,permission_level) ' at line 5
  • 括号不平衡,不用> 0来测试exists()的结果。
  • 您的第二个建议很好,但可能需要更改他不想接触的应用程序代码,因此他使用触发器自动执行。
  • @Barmar 。 . .但是,第一个有竞争条件。无论如何,该表都应具有唯一索引/约束。
【解决方案3】:

count(shared_note_id, shared_to) 是无效语法。当您使用count(DISTINCT ...) 时,您只能在COUNT() 中放置多个列名。在你的情况下,你根本不需要输入列名,只需使用COUNT(*) 来计算符合条件的行数。

请参阅count(*) and count(column_name), what's the diff?,了解有关何时应将列名放入COUNT() 的更多信息

很遗憾,修复语法错误并不能真正解决您的问题,因为您不能使用触发器来更改同一张表。来自FAQ

触发器可以访问表吗?
触发器可以访问自己表中的旧数据和新数据。触发器也可以影响其他表,但不允许修改已被调用函数或触发器的语句使用(用于读取或写入)的表。

您需要重新编码调用者以使用 INSERT ... ON DUPLICATE KEY UPDATE 或等效的东西来完成此操作。

【讨论】:

  • 我已经完成了该更改,是的,这是一个错误!谢谢。但即使这样也行不通。现在它在 END IF 处给了我一个错误
猜你喜欢
  • 2011-12-04
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
相关资源
最近更新 更多