Opened 3 months ago

Last modified 3 months ago

#1257 new задача

БД: Обсуждение. добавить таблицу с логом действия пользователя.

Reported by: san Owned by: Denis_N
Priority: major Component: БД изделий АДС
Keywords: Cc: alx

Description

  1. Пользователи просят добавить возможность дополнять или редактировать некоторые записи в истории (например Несоответствия). Например если при ошибке или опечатке пользователь записал в базу не то что нужно, он хотел бы иметь возможность это исправить.
  1. В обсуждении с Денисом п.1 мы пришли к тому, что если разрешать редактирование Истории, то информацию об этом действии пользователя нужно где-то сохранять. Один из вариантов - отдельная табличка, назовём её userlog. Т.е. в таблице history мы храним историю действий над изделием, а в табличке userlog историю действий пользователя.

Этот тикет я создал для обсуждения предлагаемых изменений.
Алексей, я хотел бы получить твою оценку предложений 1 и 2 и может быть, какие-то свои предложения по обозначенным темам.

Change History (11)

in reply to:  description comment:1 by alx, 3 months ago

Replying to san:

  1. Пользователи просят добавить возможность дополнять или редактировать некоторые записи в истории (например Несоответствия). Например если при ошибке или опечатке пользователь записал в базу не то что нужно, он хотел бы иметь возможность это исправить.

Полезная функция. Как человек, сам делающий мныго ачипяток, поддерживаю! :) Сейчас, если опечатка допущена в существенном месте (например номере, адресе и т.п.), и на нее нельзя махнуть рукой, приходится добавлять в историю новый комментарий, в котором писать что-то типа "в предыдущей записи допущена ошибка, вместо 123 должно быть 132!". Это неудобно. Хороший пример того, как удобно - trac.

  1. В обсуждении с Денисом п.1 мы пришли к тому, что если разрешать редактирование Истории, то информацию об этом действии пользователя нужно где-то сохранять.

Эту мысль я не понял...

Один из вариантов - отдельная табличка, назовём её userlog. Т.е. в таблице history мы храним историю действий над изделием, а в табличке userlog историю действий пользователя.

Нельзя ли чуть более подробно описать этот вариант? То, что написано выше - слишком уж кратко чтобы понять идею. Какая информация будет храниться в этих таблицах? Как она будет между собой связана? Как будет отображаться пользователю? Чем отличается "действие над изделием" от "действия пользователя", если действия над изделиями и так выполняют пользователи? :)

Алексей, я хотел бы получить твою оценку предложений 1 и 2

Дал оценку выше. Краткое резюме: п. 1 поддерживаю, п. 2 непонятен.

и может быть, какие-то свои предложения по обозначенным темам.

По моему видению, надо добавить в history дополнительный столбец (next_rev), содержащий ссылку на обновленную запись или NULL если такой нет.

Когда в историю заносится первая ревизия записи, в этом поле пишется NULL.

Когда пользователь, например, обнаружив опечатку в комментарии, записывает исправленную версию комментария, в поле предыдущей (первой) записи записывается ID новой (исправленной) записи. Таким образом, формируется список истории ревизий этой записи. Строго говоря, этого было бы достаточно для реализации поставленной в п. 1 задачи.

Вероятно, было бы удобно сделать этот список двунаправленным, а не однонаправленным - то есть иметь не один, а два дополнительных столбца со ссылками: один (prev_rev) ссылается на предыдущую ревизию записи, другой (next_rev) - на следующую. Например получить последние ревизии всех записей можно по условию IS_NULL(next_rev). Условием того, что запись была изменена, будет непустое поле prev_rev - по этому условию можно в веб-интерфейсе делать пометку "изменена Васей 3 дня назад". Ну и ссылки на просмотр предыдущей и следующей ревизий комментария, я думаю, понятно как в этом варианте сделать.

Дополнительно может быть полезно хранить ссылку на самую первую ревизию (first_rev) - она может быть полезна (сократит время поиска самой первой ревизии записи) для вывода истории в веб-интерфейсе - чтобы брать из нее значения полей worker, date, type_write и т.п. В самой первой ревизии туда можно тоже заносить NULL.

Также рекомендую посмотреть, как это организовано в trac (я сам не смотрел).

Last edited 3 months ago by alx (previous) (diff)

comment:2 by san, 3 months ago

Нельзя ли чуть более подробно описать этот вариант? То, что написано выше - слишком уж кратко чтобы понять идею. Какая информация будет храниться в этих таблицах? Как она будет между собой связана? Как будет отображаться пользователю? Чем отличается "действие над изделием" от "действия пользователя", если действия над изделиями и так выполняют пользователи? :)

Ну то есть "История" - это история действий изменяющих свойства изделия. Передали в другой отдел - проверили - обнаружили дефект. А редактирование комментария(для простоты сузим предложение до редактирование комментария) это принципиально другое действие. Предлагается таблицу История "оставить как есть", чтобы не переделывать интерфейсы связанные с ней, и хранить в ней только последнюю(актуальную) ревизию комментария. Предыдущие версии же хранить в отдельной таблице. В ней, как минимум, должен быть столбец с ссылками на записи в истории(N), тогда для любой записи в историю можно будет из новой таблицы загрузить все предыдущие ревизии коментария.

in reply to:  2 ; comment:3 by alx, 3 months ago

Replying to san:

Ну то есть "История" - это история действий изменяющих свойства изделия. Передали в другой отдел - проверили - обнаружили дефект. А редактирование комментария(для простоты сузим предложение до редактирование комментария) это принципиально другое действие. Предлагается таблицу История "оставить как есть", чтобы не переделывать интерфейсы связанные с ней, и хранить в ней только последнюю(актуальную) ревизию комментария.

Не переделывать интерфейсы не получится.

Во-первых, в интерфейсе должен отображаться факт изменения записи, плюс должна быть возможность просмотреть, что изменилось, и все предыдущие ревизии записи (иначе какой смысл в их хранении?). Во-вторых - см. чуть ниже...

Предыдущие версии же хранить в отдельной таблице.

При изменении записи предыдущая ревизия перемещается в другую таблицу (из history в userlog), а в history записывается новая редакция записи. Я правильно понял твою идею?

Если да, то это противоречит написанному выше (что в одной таблице - действия над изделием, в другой - действия пользователя): получается, что в таблице history записаны вперемежку как действия над изделиями (если запись не редактировалась), так и действия пользователя по редактированию записей (для тех, которые хоть раз редактировались). То же самое будет в userlog, ведь туда перемещаются записи из history. Семантически эти две таблицы не различаются. Я не вижу смысла в наличии двух разных таблиц.

Теперь во-вторых, которые обещал выше. Давай представим, что фронтэнд не менялся. Что получится в случае отредактированных записей? Получится, что в истории изделия будет записано, что, например, обнаружено несоответствие, но вместо времени обнаружения несоответствия будет отображаться время последней правки комментария. Вместо пользователя, обнаружившего несоответствие, будет записан пользователь, последним отредактировавший запись... То есть будет отображена неверная информация. Попросту говоря, старый фронтэнд не будет работать. Это вторая причина, по которой фронтэнд тоже придется менять.

В ней, как минимум, должен быть столбец с ссылками на записи в истории(N), тогда для любой записи в историю можно будет из новой таблицы загрузить все предыдущие ревизии комментария.

Допустим, у нас в таблице history есть некая запись. Как узнать, была ли эта запись отредактирована? Выполнять поиск ID этой записи по таблице userlog? И такой поиск придется выполнять для каждой записи таблицы history! Кошмар... :)

Идем дальше. Допустим, запись была отредактирована несколько раз. Как определить, какая из записей в таблице userlog, ссылающихся на данную запись в history, является предыдущей ревизией? Предполагаю, что для этого надо найти запись с максимальным ID. То есть надо сначала найти все ID, вычислить максимум, и получить запись с этим ID. Аналогично придется искать самую первую ревизию (только искать не максимальный, а минимальный ID). Так не лучше ли сразу при редактировании добавить в запись в history ID предыдущей и первой записей, чтобы не надо было потом при каждом выводе истории делать их поиск? Это был риторический вопрос. :) Запрос одной записи по уже известному ID будет выполняться за минимальное и практически константное время.

in reply to:  3 ; comment:4 by Denis_N, 3 months ago

Привет, Алексей

Replying to alx:

Семантически эти две таблицы не различаются. Я не вижу смысла в наличии двух разных таблиц.*

Если я правильно понимаю, что "семантически" - значит по значениям, то в них всё же будут различия если их разделить. В таблице userlog будет столбец с номерами ревизий =)

Пусть это не так важно, допустим семантически они не отличаются. Но они отличаются с точки зрения, наверное, какой-то нормализации или предназначения

В модели безнес-процессов (если их можно так назвать) изделия, хранимые в таблице products, проходят этапы в предприятии. Прием -> Тестирование -> ОТК -> Отгрузка. Каждый этап вносит данные в Историю

Также у нас есть "Несоответствия", который не является - этапом, а является своеобразным "критерием", позволяющим пройти изделию дальше, на следующий этап, или не проходить. Этот "критерий" оказывает влияние на этапы, он тоже вносится в Историю

История хранит данные передвижения по этапам предприятия и ошибки, с которыми сталкивается изделие на своем пути до Отгрузки

Редактирование записей, а если быть точнее "Комментариев"(по крайней мере пока), не оказывает влияние на передвижение этапов. Запись с типом "Несоответствие" уже будет создана, а информация хранимая в комментариях не влияет на движение изделия по этапам. Поэтому я считаю, что нужно делать отдельной таблицей историю изменения данных в Истории

Теперь во-вторых, которые обещал выше. Давай представим, что фронтэнд не менялся. Что получится в случае отредактированных записей? Получится, что в истории изделия будет записано, что, например, обнаружено несоответствие, но вместо времени обнаружения несоответствия будет отображаться время последней правки комментария. Вместо пользователя, обнаружившего несоответствие, будет записан пользователь, последним отредактировавший запись...

Если пользователь редактировал запись, то в основной строке, где изменился комментарий будет записано время создания записи, а не редактирования. Можно будет добавить какой-то флаг, по которому будет понятно, что запись была отредактирована. И по клику на этот флаг раскроется история редактирования, например

То же самое и с пользователем. Будет записан пользователь создавший несоответствие. И будет, например как в соц.сетях надпись рядом с текстом "ред. Казанцев А.".

Допустим, у нас в таблице history есть некая запись. Как узнать, была ли эта запись отредактирована? Выполнять поиск ID этой записи по таблице userlog? И такой поиск придется выполнять для каждой записи таблицы history! Кошмар... :)

Мы можем через Otter Join вывести со всей историей столбец с флагом из user_log, это будет работать быстро

На подобии того, как сейчас заменили на главноq столбец "Несоответствия". Можно посмотреть в строке 576 https://trac.adc-line.ru/mc-04/browser/base/main.php?rev=345

А вывод самого user_log можно сделать по клику, с помощью JS и AJAX

Идем дальше. Допустим, запись была отредактирована несколько раз. Как определить, какая из записей в таблице userlog, ссылающихся на данную запись в history, является предыдущей ревизией? Предполагаю, что для этого надо найти запись с максимальным ID. То есть надо сначала найти все ID, вычислить максимум, и получить запись с этим ID. Аналогично придется искать самую первую ревизию (только искать не максимальный, а минимальный ID)

При подходе с динамической подгрузкой данных лога по клику, такие сложности отпадают. Достаточно будет простого "select *" с "order by revision desc (или asc)"

И таблицу user_log я предлагаю формировать через триггеры на уровне базы. По триггеру перед событием update в Истории создавать строку в user_log с данными из строки в Истории. Также сделать в user_log первичный клич на два столбца, сделать так auto_increment приводил к следующему виду:
N revision
1 1
1 2
2 1
3 1

Я смотрел пример со стак overflow, код пока не весь мне понятен, но подход ясен. Буду рад, если тоже его как-то прокомментируешь: https://stackoverflow.com/questions/12563706/is-there-a-mysql-option-feature-to-track-history-of-changes-to-records

in reply to:  4 ; comment:5 by alx, 3 months ago

Replying to Denis_N:

Если я правильно понимаю, что "семантически" - значит по значениям, то в них всё же будут различия если их разделить. В таблице userlog будет столбец с номерами ревизий =)

Насколько я могу видеть, san ничего не писал о таком столбце.

Пусть это не так важно, допустим семантически они не отличаются. Но они отличаются с точки зрения, наверное, какой-то нормализации или предназначения

Почему они будут отличаться, если в них содержатся одни и те же записи (так как записи из history будут переноситься в userlog), просто сделанные в разное время?

В модели безнес-процессов...

...

Поэтому я считаю, что нужно делать отдельной таблицей историю изменения данных в Истории

С поскипанными утверждениями я согласен, все так. Только я не вижу логической связи между ними и наличием второй таблицы. Твое "поэтому" мне непонятно...

Если пользователь редактировал запись, то в основной строке, где изменился комментарий будет записано время создания записи, а не редактирования.

Это только твое предположение. К сожалению, san не стал раскрывать подробности предложенного им варианта, и мы можем об этом только гадать...

Можно будет добавить какой-то флаг, по которому будет понятно, что запись была отредактирована. И по клику на этот флаг раскроется история редактирования...

Не можно, а нужно - иначе как пользователь узнает, что комментарий был кем-то отредактирован? Это и означает, что фронтэнд изменится.

Мы можем через Otter Join

??? А кто это? Это имя какой-то знаменитой выдры? :)

вывести со всей историей столбец с флагом из user_log, это будет работать быстро

"Быстро" - понятие субъективное. Извлечение одной строки по primary key всегда будет быстрее, чем поиск строк по неуникальному значению.

При подходе с динамической подгрузкой данных лога по клику, такие сложности отпадают.

Нет, не отпадают. При выводе истории время создания и автор оригинальной (самой первой) записи требуется отобразить сразу, а не по клику. Кто и когда последним изменил запись тоже требуется отобразить сразу. А эта информация находится в таблице userlog.

Достаточно будет простого "select *" с "order by revision desc (или asc)"

Не понимаю, что выполняется в данном примере. В предложении san ничего не говорится о столбце revision.

Я смотрел пример со стак overflow, код пока не весь мне понятен, но подход ясен. Буду рад, если тоже его как-то прокомментируешь: https://stackoverflow.com/questions/12563706/is-there-a-mysql-option-feature-to-track-history-of-changes-to-records

Насколько я вижу, на этой странице 9 ответов. Который из них ты хотел бы чтобы я прокомментировал? Или все девять? :)

Last edited 3 months ago by alx (previous) (diff)

comment:6 by san, 3 months ago

К сожалению, san не стал раскрывать подробности предложенного им варианта

Это не мой вариант, это я описал предложение Дениса, которое мы обсуждали устно, подробностей на тот момент не было.

comment:7 by san, 3 months ago

Нет, не отпадают. При выводе истории время создания и автор оригинальной (самой первой) записи требуется отобразить сразу, а не по клику. Кто и когда последним изменил запись тоже требуется отобразить сразу. А эта информация находится в таблице userlog.

Насколько я понял, Денис предлагает необходимую для вывода информацию хранить в history, а в userlog обращаться только за полной историей ревизий.

in reply to:  7 comment:8 by alx, 3 months ago

Replying to san:

Насколько я понял, Денис предлагает необходимую для вывода информацию хранить в history, а в userlog обращаться только за полной историей ревизий.

Коллеги, невозможно предметно обсуждать предложение, которое не было толком описано!

Свой вариант я описал в comment:1. Если хочется узнать мое мнение о каком-то еще варианте, опишите его в деталях, чтобы не надо было гадать.

in reply to:  5 ; comment:9 by Denis_N, 3 months ago

Replying to alx:

??? А кто это? Это имя какой-то знаменитой выдры? :)

Да =) Именно так

Насколько я вижу, на этой странице 9 ответов. Который из них ты хотел бы чтобы я прокомментировал? Или все девять? :)

Первый пожалуйста

in reply to:  9 ; comment:10 by alx, 3 months ago

Replying to Denis_N:

Первый пожалуйста

Нормальный, вроде бы, пример, поставленную задачу решает. Я только не понял, зачем там добавляется столбец revision. По-моему он лишний - и без него поставленная задача решается.

Из потенциальных недостатков данного примера могу отметить такое:

  • Если в строке таблицы хранится большой объем данных (например гигабайт), и какое-то поле (например время последнего доступа) обновляется множество раз, при каждом таком обновлении сохраняется полная копия (весь гигабайт) строки, что может оказаться накладно и малоэффективно. Более эффективным в подобном сценарии могло бы оказаться сохранение изменений значений столбцов, но это несколько усложнит реализацию. Это ак бы мое дополнение к комментарию 3.
  • При каждом изменении схемы мастер-таблицы необходимо синхронно менять соответствующим образом и соответствующую таблицу истории.

in reply to:  10 comment:11 by Denis_N, 3 months ago

Спасибо, Алексей, что прокомментировал
Тщательно обдумав свою реализацию, я понял, что это приведет к определенным сложностям. И сама реализация более сложная. Также согласен с недостатками данного примера, описанные тобой в предыдущем комментарии
Конечно хотелось бы разграничить эти две таблицы, но я выберу простой подход

Note: See TracTickets for help on using tickets.