【问题标题】:Using Auto-Increment value in MYSQL Before Insert Trigger?在插入触发器之前在 MYSQL 中使用自动增量值?
【发布时间】:2023-04-11 10:35:01
【问题描述】:

用户表:

CREATE TABLE `users` (
  `id` int(8) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(45) DEFAULT NULL,
  `username` varchar(16) DEFAULT NULL,
  `salt` varchar(16) DEFAULT NULL,
  `password` varchar(128) DEFAULT NULL,
  `lastlogin` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `joined` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `loggedin` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `sessionkey` varchar(60) DEFAULT NULL,
  `verifycode` varchar(16) DEFAULT NULL,
  `verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `banned` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `locked` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `ip_address` varchar(45) DEFAULT NULL,
  `failedattempts` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `unlocktime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;

user_records 表:

CREATE TABLE `user_records` (
  `id` int(8) unsigned NOT NULL AUTO_INCREMENT,
  `userid` int(8) unsigned DEFAULT NULL,
  `action` varchar(100) DEFAULT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;

用户表上的插入前触发器:

USE `gknet`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` TRIGGER `before_create_user` BEFORE INSERT ON `users` FOR EACH ROW BEGIN
INSERT INTO user_records (action, userid, timestamp)
  VALUES ('CREATED', ID, NOW() );
END

基本上,我的问题是,在触发器上,当我尝试输入 MySQL 自动分配的用户 ID(PK、NN、自动增量)时,它只是在 user_records 表上为用户 ID 输入 0 .我该怎么做才能选择用户由 SQL 分配的 ID,并将其作为用户 ID 放入记录条目(ID 就在“创建”之后)?

另外,如果您发现可以对表格进行的任何其他优化,请随时告诉我:D

【问题讨论】:

    标签: mysql triggers


    【解决方案1】:

    将触发器更改为after insert而不是before insert,并使用NEW获取最后插入的id

    USE `gknet`;
    DELIMITER $$
    CREATE DEFINER=`root`@`localhost` 
    TRIGGER `after_create_user` AFTER INSERT ON `users` 
    FOR EACH ROW 
    BEGIN
    INSERT INTO user_records (action, userid, timestamp)
      VALUES ('CREATED', NEW.ID, NOW() );
    END; $$
    

    【讨论】:

    • 你之前我会怎么做?
    • 在插入之前没有意义,一旦插入发生,那么只有你会从用户表中获取 iduser。如果您在插入之前使用,那么您需要从用户表中选择最后一个 id 将其递增一,然后在表上插入,这不是正确的方法。插入前最适合检查传入的数据,然后在插入之前根据条件更改一些值。
    • @AbhikChakraborty:不,不需要手动增加值。您可以通过读取下一个要分配的可用 auto_increment 值来实现此目的。检查我的答案。
    • @RavinderReddy 在高负载系统和批量插入中,这会准确吗?如果我正在运行批量插入(比如 INSERT INTO ... SELECT FROM 之类的东西),或者我正在运行高并发多线程应用程序。
    • @RichardAQuadling:在插入下一条记录之前,似乎对使用 auto_increment 捕获进行了几项测试。建议在批处理过程中不要依赖。请阅读我在stackoverflow.com/a/22343265/767881 的答案之一下的 cmets。
    【解决方案2】:

    OP 的评论:
    你之前我会怎么做?

    您可以找到要分配给新记录的当前auto_increment 值。
    并在before 触发器中将其用作user_records 表的父用户ID。
    您必须查询information_schema.tables 表才能找到该值。

    示例

    use `gknet`;
    
    delimiter $$
    
    drop trigger if exists before_create_user; $$
    
    create definer=`root`@`localhost` trigger `before_create_user` 
           before insert on `users` 
    for each row begin
      declare fk_parent_user_id int default 0;
    
      select auto_increment into fk_parent_user_id
        from information_schema.tables
       where table_name = 'users'
         and table_schema = database();
    
      insert into user_records ( action, userid, timestamp )
             values ( 'created', fk_parent_user_id, now() );
    end;
    
    $$
    
    delimiter ;
    

    观察
    根据mysql documentation on last_insert_id()

    "如果您使用单个 INSERT 语句插入多行, LAST_INSERT_ID() 返回为第一个插入生成的值 仅行。"

    因此,取决于批量插入中的last_insert_id()auto_increment 字段值似乎不可靠。

    【讨论】:

    • 我不认为这个值是“事务性的”,这意味着在触发器触发时它可能不是最新的......
    • @LukasEder:请评论您的观察并建议应采用何种替代方案
    • 我已经陈述了我的观察,我不知道有什么更好的方法
    • @LukasEder:好的。我的回答中的最后一句话也是一个警示。
    【解决方案3】:

    请在插入和更新后使用

    不要让 auto_increment 任何你想显式操作的列。这可能会混淆发动机并导致严重问题。如果没有用于主键的列是 auto_increment,则可以通过触发器对它们执行任何操作。如果生成的值违反了主键的强制唯一性,那么它们将被拒绝。

    【讨论】:

      【解决方案4】:

      也许这个解决方案可以

        BEGIN 
        DECLARE id int;
        SELECT MAX(table_id) 
               FROM table 
               INTO id;
      
           IF id IS NULL THEN 
              SET NEW.column=(CONCAT('KTG',1));
           ELSE
           SET NEW.column=(CONCAT('KTG',id+1));
          END IF;
      
         END
      

      【讨论】:

      • 在回答一个老问题时,如果您包含一些上下文来解释您的答案如何提供帮助,那么您的答案将对其他 StackOverflow 用户更有用,特别是对于已经有一个已接受答案的问题。请参阅:How do I write a good answer
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 1970-01-01
      • 1970-01-01
      • 2013-05-12
      • 2021-05-14
      • 1970-01-01
      • 2017-08-15
      相关资源
      最近更新 更多