MySQL 8 - триггер на INSERT - дубликат идентификатора AUTO_INCREMENT для VCS - PullRequest
1 голос
/ 26 сентября 2019

Попытка
создать триггер, который вызывается при вставке и устанавливает originId = id (AUTO_INCREMENT),
Я использовал предложенный SQL здесь, в 1-м блоке :

  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 файле.Теперь информация обновляется практически мгновенно по каждой вставке, как я заметил ..

Но, выполняя "прямые" вставки через консоль с интервалом ~ 2 минуты, я продолжаю получать не обновленные значения AUTO_INCREMENT в originId.
table example after couple INSERTs (они должны быть равны id полям)
При явных запросах, извлекающих AUTO_), обновляются правильные значения.

Таким образом я подозреваю, что результат подзапроса SELECT AUTO_INCREMENT... получает как-то .. что?кешируется?
Как можно обойти это?

Спасибо.


Edit 1

Я намеревался реализовать сортировку VCS следующим образом:

  1. Пользователь создает новое примечание, приложение помечает его как «новое» и выполняет INSERT в таблице MySQL.Это примечание "origin".
  2. Тогда пользователь может редактировать это примечание (полностью) в пользовательском интерфейсе, приложение пометит его как «update» и вставит его в таблицу MySQL как новую строку, очередной раз.Но на этот раз originId должен быть заполнен id примечанием «origin» (по логике приложения).И т. Д.
  3. Это позволяет РАЗДЕЛИТЬ с помощью originId в SELECT, выбирая только последние версии в пользовательский интерфейс.

исходная проблема:
Если originId из "origin" Note имеет значение NULL, оконная функция (-и) MySQL 8 по умолчанию (и только?) Выполняет режим RESPECT_NULL(s) кадрирование не так, как ожидалось («ну, да, это все о ваших NULL в столбце группировки по группам»).

Предполагаемое решение:
Установите originId для "оригинальных" нот на id на их начальных и только INSERT, ожидая 2 преимущества:

  • Легко извлекайте «исходные» ноты через originId = id,
  • , выполняйте правильное РАЗДЕЛЕНИЕ с помощью originId.

.:
id - это AUTO_INCREMENT, поэтому нет способа (мне известно) получить его новое значение (для новой строки) в INSERT через бэкэнд (а именно, PHP).

предполагаемое решение:
Итак, я надеялся найти какой-нибудь механизм MySQL для решения этой проблемы (избегая манипуляций с полем id), и TRIGGER казались правильным способом ...


Редактировать 2

Я считал, что автоматическое дублирование поля id AUTO_INCREMENT (или любого поля) в MySQL очень быстрое и супер простое, но сейчас это не так..

Таким образом, возможно, лучше использовать поле vcsGroupId UNSIGNED INT, отвечающее за «связывание» версий Заметки:

  • Вкл. create и «origin»INSERT - заполнить его MAX(vcsGroupId) + 1,
  • On edit и "version" INSERT - заполнить его значением "sibling" / "origin" vcsGroupId (извлечено с помощью CTE),
  • On view и «обычный» SELECT - выполнить кадрирование с помощью оконной функции с помощью PARTITION BY vcsGroupId, ORDER BY id или отметки времени DESC, затем просто используя 1-й (или в порядке возрастания, используя & last) row,
  • On view и «origin» SELECT - почти одинаковые, но обратные ..

It sэто проще, не правда ли?

1 Ответ

1 голос
/ 26 сентября 2019

То, что вы делаете, играет с огнем.Я не знаю точно, что может пойти не так с вашим триггером (кроме того, что он уже не работает для вас), но у меня есть сильное чувство, что многие вещи могут и будут работать неправильно.Например: что если вы вставите несколько строк в один оператор?Я не думаю, что движок будет обновлять information_schema для каждой строки.И будет еще хуже, если вы запустите оператор INSERT ... SELECT.Поэтому использование information_schema для этой задачи - очень плохая идея.

Однако - первый вопрос: зачем он вообще нужен?Если вам нужно сохранить «идентификатор источника», то вы, вероятно, планируете обновить столбец id.Это уже плохая идея.И при условии, что вы найдете способ решить вашу проблему - что гарантирует, что originId не будет изменен вне триггера?

Однако - альтернатива - оставить пустым столбец originId при вставке.и обновите его в триггере UPDATE.

Предполагая, что это ваша таблица:

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

Используйте триггер UPDATE, чтобы сохранить идентификатор источника, когда он изменяется в первый раз:

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;

См. Демонстрация на db-fiddle

Вы даже можете сохранитьполная история идентификаторов со следующей схемой:

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           |

Просмотр на БД Fiddle

...