【问题标题】:MySQL trigger to update a field to the value of idMySQL 触发器将字段更新为 id 的值
【发布时间】:2010-11-15 18:09:07
【问题描述】:

我想要一个触发器来对插入的记录执行以下操作:

 # pseudocode
 if new.group_id is null
    set new.group_id = new.id
 else
    # don't touch it
 end

更清楚地说:假设我有一个包含三列的表:id 主键,group_id int,value varchar。

当我像这样用group_id 插入时:

INSERT INTO table(value, group_id) VALUES ('a', 10)

我想要:

id | group_id | value
---+----------+------
 1 |       10 | a

但是当我省略group_id:

INSERT INTO table(value) VALUES ('b')

应该自动设置为这条记录的id

id | group_id | value
---+----------+------
 2 |        2 | b

可以用触发器吗? (我知道我可以在插入后更新记录,但有触发器会更好。)

【问题讨论】:

    标签: mysql insert triggers


    【解决方案1】:

    我不知道有什么方法可以在一个语句中执行此操作,即使使用触发器也是如此。

    @Lucky 建议的触发器解决方案在 MySQL 中如下所示:

    CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable
    FOR EACH ROW BEGIN
      SET NEW.group_id = COALESCE(NEW.group_id, NEW.id);
    END
    

    但是,有一个问题。在BEFORE INSERT 阶段,尚未生成自动生成的id 值。因此,如果group_id 为空,则默认为NEW.id,始终为0。

    但如果您将此触发器更改为在AFTER INSERT 阶段触发,因此您可以访问NEW.id 的生成值,则无法修改列值。

    MySQL 不支持列的DEFAULT 表达式,因此您也不能在表定义中声明此行为。 *更新:MySQL 8.0.13 支持DEFAULT (<expression>),但表达式仍然不能依赖自动增量值(这是documented)。

    唯一的解决方案是执行INSERT,然后立即执行UPDATE 以更改group_id(如果未设置)。

    INSERT INTO MyTable (group_id, value) VALUES (NULL, 'a');
    UPDATE MyTable SET group_id = COALESCE(group_id, id) WHERE id = LAST_INSERT_ID();
    

    【讨论】:

    • 这太糟糕了。即使在 2016 年
    • @AA_PV,这可以通过一个独立的序列对象来解决,就像他们在 Oracle 中所做的那样。自 2003 年以来,这一直是 MySQL 中的一个开放功能请求:bugs.mysql.com/bug.php?id=1625 但在 Oracle 中,要使用该序列,您必须在应用程序代码中调用它,这意味着为每个 INSERT 到一个你想拥有一个 auto-inc 主键的表。或者为 every 这样的表编写一个触发器。 MySQL 用户喜欢能够跳过编写这么多触发器,而只需将 AUTO_INCREMENT 声明为列选项。
    • 是的,但是 MySQL 中不允许有两个 AUTO_INCREMENT 列,对吧?虽然我认为可以做类似于stackoverflow.com/a/7345183/4411645 的事情。为了避免嵌套查询,也许它也可以在应用程序代码中维护,尽管这可能更不安全,不确定。
    • @AA_PV,正确,不支持两个 AUTO_INCREMENT 列。但大多数用户认为这不是问题。
    • 在 INSERT 触发器中,只能使用 NEW.col_name;没有旧行。在 DELETE 触发器中,只能使用 OLD.col_name;没有新行。在 UPDATE 触发器中,您可以使用 OLD.col_name 来引用行更新前的列,使用 NEW.col_name 来引用行更新后的列。 Syntax 所以你可以使用OLD.id 访问ID。
    【解决方案2】:

    这对我有用

    DELIMITER $$
    CREATE TRIGGER `myTriggerNameHere`
    BEFORE INSERT ON `table` FOR EACH ROW
    BEGIN
        SET NEW.group_id = IF(NEW.group_id IS NULL, LAST_INSERT_ID()+1, NEW.group_id);
    END;
    $$
    DELIMITER ;
    

    【讨论】:

    • 不适用于多行插入,因为在批处理完成之前不会更新 LAST_INSERT_ID。
    【解决方案3】:

    我在这里回答,正如接受的答案Bill Karwin 所说:

    在 BEFORE INSERT 阶段,自动生成的 id 值还没有被 尚未生成。因此,如果 group_id 为 null,则默认为 NEW.id,即 总是 0。


    我有一个答案 - 对于 OP(以及即将到来的访客),这里有几点: 您无法更新调用触发器的表,因为您将收到 Error 1442

    Error Code: 1442
    Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
    

    1.更新新行 使用BEFORE INSERT ON触发器,这样你就可以更新新行的所有字段,可以通过NEW操作符访问,即

    set NEW.group_id = NEW.id
    

    2.插入前获取auto_increment值

    SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='MyTable'
    

    总而言之 - ' 的触发 SQL 如下所示:

    DELIMITER //
    DROP TRIGGER IF EXISTS MyTrigger//
    CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable
    FOR EACH ROW BEGIN
        IF new.group_id IS NULL
            set @auto_id := (SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES
                             WHERE TABLE_NAME='MyTable' AND TABLE_SCHEMA=DATABASE() ); 
            set NEW.group_id = @auto_id;
        ENF IF;
    END;
    //
    DELIMITER ;
    

    【讨论】:

    • 从表中选择 auto_increment 值会引入竞争条件。
    【解决方案4】:

    在这种情况下,触发器似乎有点矫枉过正。只需应用默认值。

    CREATE TABLE `test` (
        `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
        `value` varchar(100) NOT NULL,
        `group_id` TINYINT(3) UNSIGNED NOT NULL DEFAULT '2'
    )
    

    【讨论】:

    • 很抱歉,您错过了重点。这与将文字值 2 插入为 group_id 无关。我希望这条记录的idgroup_id,除非我明确指定group_id
    【解决方案5】:

    这个触发器应该按照你的要求做。

       CREATE TRIGGER mytrigger BEFORE INSERT ON mytable
              IF new.group_id IS NULL
                SET new.group_id = new.id
              END IF
          END;
    

    它是从MYSQL documentation 页面中一个非常相似的示例复制而来的。

    【讨论】:

    • 不适用于 MySQL:您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“WHERE new.group_id IS NULL END”附近使用正确的语法
    • 您应该能够从dev.mysql.com/doc/refman/5.1/en/create-trigger.html 的示例中获得确切的语法 - 我没有测试。我会尝试编辑
    • 未设置 NEW.id,如果 group_id 是唯一的,则插入第二行时将失败。
    【解决方案6】:

    我相信这对你有用

    我有两张桌子

    test_b: a_id, value
    test_c: a_id, value
    

    这是插入测试 b 的触发器。它检查 a_id 是否为空,如果是则插入 0

    CREATE TRIGGER test_b AFTER INSERT ON test_b
      FOR EACH ROW 
        INSERT INTO test_c (a_id, value) VALUES (IFNULL(NEW.a_id, 0),NEW.value)
    

    【讨论】:

    • 如果你告诉我更多关于你的表的结构,我可以修改这个查询来为你工作。
    猜你喜欢
    • 1970-01-01
    • 2015-02-11
    • 2013-04-14
    • 2017-08-03
    • 2013-01-02
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多