代码之家  ›  专栏  ›  技术社区  ›  MagicMatt

由于值(…)中的“SELECT*”,MySQL触发器的插入导致基数冲突

  •  0
  • MagicMatt  · 技术社区  · 2 年前

    我遇到了一个问题,由于触发器的编写方式,无法在具有触发器的MySQL表上插入新记录。我得到错误:

    Cardinality violation: 1241 Operand should contain 1 column(s)

    它本质上是一个审计触发器,将新记录复制到包含额外数据的审计表中。我已经成功地证明了这个问题是特定于嵌套的“SELECT a.*…”的在触发器的Insert的VALUES()部分,因为当我用“NEW”的逐项列表替换该SELECT时。“xyx”列它工作正常。示例如下:

    表A:

    CREATE TABLE `users` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(20) NOT NULL,
      `email` varchar(50) NOT NULL,
      `admin` tinyint(1) NOT NULL DEFAULT 0,
      `date_updated` datetime NOT NULL DEFAULT current_timestamp()
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    ALTER TABLE `users`
      ADD PRIMARY KEY (`id`);
    

    表B:

    CREATE TABLE `users_audit` (
      `action` varchar(8) NOT NULL,
      `revision` int(11) NOT NULL,
      `id` int(11) NOT NULL,
      `username` varchar(20) NOT NULL,
      `email` varchar(50) NOT NULL,
      `admin` tinyint(1) NOT NULL,
      `date_updated` datetime NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    

    触发器(在插入到表A时导致基数冲突):

    CREATE TRIGGER users_ins AFTER INSERT ON users
    FOR EACH ROW BEGIN
        INSERT INTO users_audit
        VALUES ('insert', 
                (SELECT IFNULL(MAX(a.revision), 0) + 1 FROM users_audit a where a.id = NEW.id), 
                (SELECT b.* FROM users b WHERE b.id = NEW.id LIMIT 1)
               );    
    END
    

    问题在于线路 (SELECT b.* FROM user_accounts b WHERE b.id = NEW.id LIMIT 1) . 此上方的行没有问题(第一个“select”语句)。我甚至添加了“LIMIT 1”以确保只返回了1行(应该这样做,因为“id”是“users”上的PK)。我认为发生错误是因为第二条SELECT语句返回多个列(由于“SELECT b.*…”),并且它们不能以逗号分隔,以便“VALUES”中的项数与“users\u audit”中的列数匹配。

    CREATE TRIGGER users_ins AFTER INSERT ON users
    FOR EACH ROW BEGIN
        INSERT INTO users_audit
        VALUES ('insert', 
                (SELECT IFNULL(MAX(a.revision), 0) + 1 FROM users_audit a where a.id = NEW.id), 
                SELECT b.* FROM users b WHERE b.id = NEW.id LIMIT 1
               );    
    END
    

    但这在myPhpAdmin中甚至没有运行(我得到了一个语法错误)。

    真的没有办法做到这一点,而不必手动写出每一列吗?作为记录,我已经完成了以下工作,但对于我要添加审计的所有表来说,这将需要很多额外的时间(必须手动写出它们的每一列)。

    工作触发器,但过于冗长,必须单独列出新列:

    CREATE TRIGGER users_ins AFTER INSERT ON users
    FOR EACH ROW BEGIN
        INSERT INTO users_audit 
        VALUES ('insert', 
                (SELECT IFNULL(MAX(a.revision), 0) + 1 FROM users_audit a where a.id = NEW.id),
                NEW.id, NEW.username, NEW.email, NEW.admin, NEW.date_updated
               );    
    END 
    

    我真的不想这样写专栏。当然,一定有什么方法可以实现我想要的?谢谢你的帮助!

    1 回复  |  直到 2 年前
        1
  •  0
  •   ysth    2 年前

    一般来说,使用select*根据列顺序进行插入而不使用列列表是一个非常糟糕的主意,因此我建议只单独列出新列。在本例中,我看到有两个密切相关的表 users users_audit

    INSERT INTO users_audit
    SELECT
        'insert',
        (SELECT IFNULL(MAX(a.revision), 0) + 1 FROM users_audit a where a.id = NEW.id),
        b.*
        FROM users b WHERE b.id = NEW.id
    
    推荐文章