【问题标题】:MySQL 8 - Trigger on INSERT - duplicate AUTO_INCREMENT id for VCSMySQL 8 - 触发 INSERT - VCS 的重复 AUTO_INCREMENT id
【发布时间】:2019-09-26 17:45:52
【问题描述】:

尝试
创建在 INSERT 上调用的触发器并设置 originId = id (AUTO_INCREMENT)
我用过SQL建议here in 1st block

  CREATE TRIGGER insert_example
  BEFORE INSERT ON notes
  FOR EACH ROW 
  SET NEW.originId = (
        SELECT AUTO_INCREMENT 
        FROM information_schema.TABLES 
        WHERE TABLE_SCHEMA = DATABASE() 
        AND TABLE_NAME = 'notes'
  );  


由于information_schema缓存我也设置了

information_schema_stats_expiry = 0

my.cnf 文件中。正如我所注意到的,现在信息几乎在每次 INSERT 时都会立即更新..

但是,通过控制台以大约 2 分钟的间隔执行“直接”插入,我一直没有更新 originId 中的 AUTO_INCREMENT 值。
(它们应该等于id 字段)
在显式查询时,获取 AUTO_) 会导致更新的正确值。

因此我怀疑SELECT AUTO_INCREMENT... 子查询的结果不知何故......什么?缓存?
如何解决这个问题?

谢谢。


编辑 1

我打算以这种方式实现某种 VCS:

  1. 用户创建新注释,应用程序将其标记为“新”并在 MySQL 表中执行 INSERT。这是“原点”注释。
  2. 然后用户可以在 UI 中编辑此注释(完全),应用程序会将其标记为“更新”,并在 MySQL 表中再次将其插入为新行。但是这一次originId 应该用“原点”注释的id 填充(通过应用程序逻辑)。等等。
  3. 这允许通过originId 对SELECT 进行分区,仅获取最新版本的UI。

最初的问题:
如果 originId of "origin" Note 为 NULL,默认情况下 MySQL 8 窗口函数(并且仅?) RESPECT_NULL 模式执行的框架不符合预期(“嗯,duh,这都是关于分组中的 NULL -按列”)。

假设的解决方案:
在他们的initial and only INSERT 上将originId of "origin" Notes 设置为id,期望有两个好处:

  • 通过originId = id 轻松获取“来源”便笺,
  • 通过originId 执行正确的分区。

导致的问题:
id 是 AUTO_INCREMENT,因此(我知道)无法通过后端(即 PHP)在 INSERT 上获取其新值(对于新行) )。

假设的解决方案:
所以,我希望找到一些 MySQL 机制来解决这个问题(避免使用 id 字段进行操作),TRIGGER 似乎是正确的方法......


编辑 2

我相信在 MySQL 中自动复制 id AUTO_INCREMENT 字段(或任何字段)会非常快速且超级简单,但现在看来完全不是这样......

因此,可能更好的方法是拥有vcsGroupId UNSIGNED INT 字段,负责“关联” Note 的版本:

  • 创建和“原点”插入 - 用MAX(vcsGroupId) + 1填充它,
  • 编辑和“版本”插入 - 用“兄弟”/“原点”vcsGroupId 值填充它(使用 CTE 获取),
  • view 和“正常”SELECT 中 - 通过 PARTITION BY vcsGroupIdORDER BY id 或时间戳 DESC 使用窗口函数执行框架,然后只使用第一个(或使用最后一个升序)行,
  • view 和“origin” SELECT - 几乎相同,但相反..

这似乎更容易,不是吗?

【问题讨论】:

  • 有什么好的理由,你为什么要尝试“复制” id?
  • @PaulSpiegel,尝试实现 VCS 排序,我需要复制 id 以获取“原点”注释..
  • 如果版本控制是由应用程序管理的,那为什么不在初始 INSERT 之后的 UPDATE 语句中复制 id 呢?
  • @PaulSpiegel,我不确定用链式查询来实现这个我必须执行(INSERT - SELECT id - UPDATE),就像我最近想的那样。就性能而言,三倍查询似乎非常无效。让我感到羞耻,因为我忘记了 PHP 允许 getting id on every INSERT using Mysqli class,而不是 Mysqli_stmt 作为准备好的查询.. 尽管如此,仍然存在加倍查询.. 另一方面,通过WHERE id = originId 获取“原始”行似乎更容易和更快,而不是分区、排序和挑选……不是吗?
  • 您的设计(每个版本一行)对性能不太友好。所以我不太关心初始插入的两个查询。

标签: mysql sql-insert database-trigger mysql-8.0


【解决方案1】:

你在玩火。我不确切知道您的触发器会出现什么问题(除此之外,它已经不适合您了),但我有一种强烈的感觉,很多事情都可能而且将会出错。例如:如果您在单个语句中插入多行怎么办?我不认为引擎会为每一行更新information_schema。如果您运行INSERT ... SELECT 语句,情况会更糟。所以在这个任务中使用information_schema 是一个非常糟糕的主意。

但是 - 第一个问题是:您为什么需要它?如果您需要保存“来源 ID”,那么您可能计划更新 id 列。这已经是个坏主意了。并假设您会找到解决问题的方法 - 什么保证您,originId 不会在触发器之外更改?

但是 - 另一种方法是在插入时将 originId 列保留为空白,并在 UPDATE 触发器中对其进行更新。

假设这是你的桌子:

create table vcs_test(
  id int auto_increment,
  origin_id int null default null,
  primary key (id)
);

第一次更改时,使用 UPDATE 触发器保存源 ID:

delimiter //
create trigger vcs_test_before_update before update on vcs_test for each row begin
  if new.id <> old.id then
    set new.origin_id = coalesce(old.origin_id, old.id);
  end if;
end;
delimiter //

您的 SELECT 查询将是这样的:

select *, coalesce(origin_id, id) as origin_id from vcs_test;

demo on db-fiddle

您甚至可以使用以下架构保存完整的 id 历史记录:

create table vcs_test(
  id int auto_increment,
  id_history text null default null,
  primary key (id)
);


delimiter //
create trigger vcs_test_before_update before update on vcs_test for each row begin
  if new.id <> old.id then
    set new.id_history = concat_ws(',', old.id_history, old.id);
  end if;
end;
delimiter //

下面的测试

insert into vcs_test (id) values (null), (null), (null);

update vcs_test set id = 5 where id = 2;
update vcs_test set id = 4 where id = 5;

select *, concat_ws(',', id_history, id) as full_id_history
from vcs_test;

会回来

| id  | id_history | full_id_history |
| --- | ---------- | --------------- |
| 1   |            | 1               |
| 3   |            | 3               |
| 4   | 2,5        | 2,5,4           |

View on DB Fiddle

【讨论】:

  • 感谢您的详尽回答,但是“...那么您可能计划更新id 列...” - 实际上不是我计划的。我的意图是用 INSERTS “替换” UPDATE 操作。请查看有问题的编辑。
猜你喜欢
  • 2015-04-15
  • 2012-01-03
  • 2011-04-12
  • 1970-01-01
  • 2015-03-08
  • 2015-11-06
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
相关资源
最近更新 更多