|
13.01.2021, 13:48 | #1 |
Участник
|
А если просто в журнале базы данных SysDatabaseLog искать все записи за последнюю минуту, у которых нужный TableId ?
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 13:57 | #2 |
Участник
|
Я как-то делал отчет, который из журнала базы данных достает историю изменения выбранных полей. Вроде довольно быстро работает на базе в 500 Гб. Правда там журнал базы данных хранился только за последние 2 недели, а потом обрезался.
А вам всего-то надо узнать какие записи изменились из списка нужных вам таблиц.
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 14:29 | #3 |
Модератор
|
"Довольно быстро", или "достаточно быстро чтобы непрерывно делать это 10+ раз в минуту", учитывая других читателей-писателей в лог ?
__________________
-ТСЯ или -ТЬСЯ ? Последний раз редактировалось Vadik; 13.01.2021 в 14:53. |
|
13.01.2021, 15:37 | #4 |
Участник
|
Цитата:
А запрос к таблице SysDatabaseLog вроде быстро выполнялся. Прошло уже несколько лет с того времени. По-любому надо пробовать. По-моему это самый простой способ решить данную проблему. А начинать пробовать надо с простых способов. Хотя все-таки поймать изменение конкретного поля через таблицу SysDatabaseLog - задача не очень тривиальная, но пример как это сделать есть. Даже на этом форуме выкладывали.
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 15:39 | #5 |
Участник
|
Запустил у себя сейчас такой джоб.
Для 26 тысяч клиентов он отработал примерно за 2 минуты (если убрать вывод в инфолог) Это для Аксапты версии 3.0 X++: static void sysDatabaseLogFieldChangedCustAxForum(Args _args) { CustTable custTable; SysDatabaseLog SysDatabaseLog; int i; container tmp; SysOperationProgress sysOperationProgress = new SysOperationProgress(1); int stepCount; int total; UserInfo UserInfo; ; select count(RecId) from custTable; total = custTable.RecId; sysOperationProgress.setTotal(total); while select custTable { stepCount ++; sysOperationProgress.setCount(stepCount); sysOperationProgress.setText(strFmt("%1 запись из %2", stepCount, total)); while select SysDatabaseLog order by createdDate, createdTime where SysDatabaseLog.LogRecId == custTable.RecId && SysDatabaseLog.table == custTable.TableId { if (typeOf(conpeek(SysDatabaseLog.Data, 1)) == Types::Container) { for ( i = 1; i <= conlen(SysDatabaseLog.data); i ++) { tmp = conpeek(SysDatabaseLog.Data, i ); if (fieldExt2Id(conpeek(tmp, 1)) == fieldnum(custTable, zResponsibleDivision)) { if(conpeek(tmp, 3) != conpeek(tmp, 2)) { select firstonly UserInfo where UserInfo.Id == SysDatabaseLog.createdBy; info(strfmt("%1 ~ %2 ~ %3 ~ %4 ~ %5", custTable.AccountNum, UserInfo.name, SysDatabaseLog.createdDate, conpeek(tmp, 3),conpeek(tmp, 2) )); } } } } } } }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ Последний раз редактировалось Ace of Database; 13.01.2021 в 15:41. |
|
13.01.2021, 14:52 | #6 |
Участник
|
Цитата:
Цитата:
На худой конец можно создать свой лог-таблицу под эту задачу. У нас практикуется репликация о которой говорил axm2017. Цитата:
Если под эту задачу сделать свою таблицу, да с толковыми индексами, то будет очень быстро работать.
__________________
-Ты в гномиков веришь? -Нет. -А они в тебя верят, смотри, не подведи их. Последний раз редактировалось Pustik; 13.01.2021 в 15:00. |
|
13.01.2021, 16:29 | #7 |
Участник
|
Там главная проблема в том что связи сложны. Ну т.е. я там выше привел пример запроса который они сейчас используют. (Выгрузка измененных клиентов во внешнюю систему)
Т.е. понятно что для одной таблицы это все сделать просто. С outer join у вас будет 8 вариантов в различных комбинациях. Если делать с интервалом несколько часов(чтобы покрыть длинные транзакции) то данных будет много |
|
13.01.2021, 16:38 | #8 |
Участник
|
А если вот так сделать?
А потом уже сопоставлять отобранные записи вручную между собой через всякие Map'ы ? X++: while select SysDatabaseLog order by createdDate, createdTime where SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS) { }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 16:45 | #9 |
Участник
|
Вот так запихнуть всю историю в мапы, а потом мапы сопоставляеть между собой
X++: Map mapCustTable = new Map(Types::String, Types::Container), Map mapDIRPARTYLOCATION = new Map(Types::String, Types::Container), Map mapLOGISTICSELECTRONICADDRESS = new Map(Types::String, Types::Container), while select SysDatabaseLog order by createdDate, createdTime where SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS) { switch(SysDatabaseLog.table) { case tableNum(custTable): mapCustTable.insert(strfmt("%1_%2_%3", SysDatabaseLog.LogRecId, SysDatabaseLog.CreatedDate, SysDatabaseLog.CreatedTime), SysDatabaseLog.data); break; } }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 16:50 | #10 |
Участник
|
Т.е. у вас будет 8 мапов.
Например, если в мапе mapLOGISTICSELECTRONICADDRESS изменилось нужное вам поле, то вы уже можете обычным для Аксапты способом через таблицу LOGISTICSELECTRONICADDRESS выйти на того клиента, и узнать его код. И даже сопоставлять между собой эти 8 мапов не надо.
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
13.01.2021, 17:10 | #11 |
Участник
|
Вот как-то так:
X++: LogisticsElectronicAddress logisticsElectronicAddress; MapEnumerator me = new MapEnumerator(mapLogisticsElectronicAddress); CustTable custTable; RefRecId refRecId; while (me && me.moveNext()) { refRecId = str2Int64(conPeek(str2Con_RU(me.currentKey(), "_"), 1)); logisticsElectronicAddress = LogisticsElectronicAddress::findRecId(refRecId); custTable = CustTable::findByPartyRecId(logisticsElectronicAddress .PrivateForParty); }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ Последний раз редактировалось Ace of Database; 13.01.2021 в 17:13. |
|
13.01.2021, 18:53 | #12 |
Участник
|
что-то я потерял идею
X++: while select SysDatabaseLog order by createdDate, createdTime where SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS) |
|
13.01.2021, 20:45 | #13 |
Участник
|
Это пример был из АХ3. Там дата и время создания записи хранятся в отдельных полях
А для АХ 2012, чтобы вернуть все изменения за последнюю минуту, можно написать так: X++: while select SysDatabaseLog order by createdDateTime where SysDatabaseLog.createdDateTime >= DateTimeUtil::addMinutes(DateTimeUtil::utcNow(), -1) && (SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS)) { }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
14.01.2021, 01:53 | #14 |
Участник
|
Ну это будут изменения за последнюю минуту по которым завершилась транзакция в момент запуска данного запроса. Также могут быть изменения за конкретно эту же минуту по которым транзакция еще не завершилась, они появятся позже, как она завершится. Как предлагаете их искать?
|
|
14.01.2021, 08:18 | #15 |
Участник
|
Цитата:
1) запоминаем время запуска текущей репликации startDateTime = DateTimeUtil::utcNow() 2) достаем из таблички дату-время запуска последней репликации = lastDateTime 3) пошла репликация: X++: while select SysDatabaseLog order by createdDateTime where SysDatabaseLog.createdDateTime >= lastDateTime && (SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS)) { } PS: А меняя в этой табличке дату-время запуска последней репликации можно управлять интервалом минута, месяц, год и т.д.
__________________
-Ты в гномиков веришь? -Нет. -А они в тебя верят, смотри, не подведи их. Последний раз редактировалось Pustik; 14.01.2021 в 08:26. |
|
14.01.2021, 08:34 | #16 |
Участник
|
Цитата:
X++: while select SysDatabaseLog order by createdDateTime where SysDatabaseLog.RecId > lastRecId && (SysDatabaseLog.table == tableNum(custTable) || SysDatabaseLog.table == tableNum(DIRPARTYLOCATION) || SysDatabaseLog.table == tableNum(LOGISTICSELECTRONICADDRESS)) { }
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
14.01.2021, 11:02 | #17 |
Участник
|
RecId тоже непоследовательны. они выделяются пачками на клиента(по сто или около того) и потом расходуются по мере надобности
Последний раз редактировалось trud; 14.01.2021 в 11:39. |
|
14.01.2021, 11:48 | #18 |
Участник
|
Имеется в виду не RecId клиента, а RecId в таблице SysDatabaseLog
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ |
|
14.01.2021, 12:00 | #19 |
Участник
|
Прежде всего, согласен с Vadik, требование опрашивать 6M записей с частотой 1 раз в минуту - это какой-то перебор.
Но скорее всего, заказчик хотел иметь возможность опрашивать часто и опрашивать повторно некоторые разрезы (по группам, по условиям платежей, по наличию скидочной карточки и т.п.) поэтому простим такую формулировку. Критика предыдущих предложений 1. Change Tracking - хорош. Но слишком привязывает в MS SQL. В наше непростое время Great Again вендор-lock - это серьезный недостаток 2. ModifiedDateTime, RecID и прочие неубывающие последовательности - в топку, поскольку именно этот статус позволяет узнать что изменилось во всем справочнике, но не позволяет запросить измененные только в некоторых записях, и вынуждает делаеть полные запросы по всем 6M записей (а время еще и требует филигранной синхронизации времени на разных аосах/клиентах) 3. перехватывать событие/триггера изменений и записывать логи - чревато DDOS системы на расчетных полях типа себестоимости в складских проводках, алгоритм долбит обновлениями до посинения. (особенно категорически не надо использовать SysDatabaseLog) Что есть в стандартной Аксапте? 4. в аксапте уже есть поле recVersion. Ядро Аксапты использует это поле в рамках механизма оптимистической конкуренции в формах для того, чтобы узнать изменилась ли запись в других Аксаптах. Это то, что нам нужно! подход с recVersion придумал не майкрософт, но почитать об этом подходе можно здесь https://docs.microsoft.com/ru-ru/sql...n-transact-sql Кратко как поле RecVersion работает в аксапте: 1. RecVersion = 0 при создани записи 2. RecVersion = random(int) при любом обновлении записи (повторю: это делает ядро Аксапты, программировать это не надо) Инвариант: С огромной вероятностью recVersion после обновления будет отличаться от recVersion до обновления (ах это "почти"... длинные рассуждения о почти уникальных GUID и о инженерном подходе "на практике работоспособно") Что предлагается 5. 5.1. у вас есть справочник, состоящий из нескольких связанных таблиц (tab1, tab2 ... tabN) 5.2. у каждого справочника аксапта обслуживает служебное поле recVersion 5.3. Создайте shadow таблицу для вашего справочника tabShadow, в которой храните: 5.3.1. refRecVersion1, ... refRecVersionN - recVersion таблиц, которые вы выгрузили во внешнюю систему 5.3.2. [опционально] сериализованные значений полей, которые вы выгружаете. Важно, чтобы: 5.3.2.1. сериализованные значения можно было удобно сравнивать на равенство (изменились ли?) 5.3.2.2. сериализовать можно в аксаптовский container - тогда не надо будет программировать парсинг. Но конейнеры в MS SQL хранятся в (Memo)-полях. Со всеми вытекающими для производительности 5.3.3. FK к вашему справочнику, которые позволят однозначно связать shadow-запись и запись в справочнике (связь 0,1..0,1) 5.4. при каждой выгрузке обновляйте shadow-таблицу 5.5. shadow таблица позволит вам четко определить запросами 4 состояния: 5.5.1. есть запись в tab, нет записи в shadow - запись в справочнике создана, ни разу не выгружалась 5.5.2. есть запись в tab, есть запись в shadow, recVersion не совпадают - запись в справочнике изменилась, надо выгрузить 5.5.3. есть запись в tab, есть запись в shadow, recVersion совпадают - запись в справочнике не изменалась 5.5.4. ест записи в tab, есть запись в shadow - запись удалена, надо дать команду на удаление внешней системе 5.6. c shadow таблицей вы можете накладывать любые фильтры на справочник и получать измененные записи только среди отфильтрованных 5.7. shadow таблица не будет нагружать вашу систему паразитной нагрузкой в insert/update/delete и логах. будет работать только по запросу там еще куча соображений. но пока хватит. Последний раз редактировалось mazzy; 14.01.2021 в 12:07. |
|
|
За это сообщение автора поблагодарили: sukhanchik (10), vmoskalenko (4). |
14.01.2021, 13:00 | #20 |
Боец
|
Цитата:
Сообщение от mazzy
Прежде всего, согласен с Vadik, требование опрашивать 6M записей с частотой 1 раз в минуту - это какой-то перебор.
Но скорее всего, заказчик хотел иметь возможность опрашивать часто и опрашивать повторно некоторые разрезы (по группам, по условиям платежей, по наличию скидочной карточки и т.п.) поэтому простим такую формулировку. Критика предыдущих предложений 1. Change Tracking - хорош. Но слишком привязывает в MS SQL. В наше непростое время Great Again вендор-lock - это серьезный недостаток 2. ModifiedDateTime, RecID и прочие неубывающие последовательности - в топку, поскольку именно этот статус позволяет узнать что изменилось во всем справочнике, но не позволяет запросить измененные только в некоторых записях, и вынуждает делаеть полные запросы по всем 6M записей (а время еще и требует филигранной синхронизации времени на разных аосах/клиентах) 3. перехватывать событие/триггера изменений и записывать логи - чревато DDOS системы на расчетных полях типа себестоимости в складских проводках, алгоритм долбит обновлениями до посинения. (особенно категорически не надо использовать SysDatabaseLog) Что есть в стандартной Аксапте? 4. в аксапте уже есть поле recVersion. Ядро Аксапты использует это поле в рамках механизма оптимистической конкуренции в формах для того, чтобы узнать изменилась ли запись в других Аксаптах. Это то, что нам нужно! подход с recVersion придумал не майкрософт, но почитать об этом подходе можно здесь https://docs.microsoft.com/ru-ru/sql...n-transact-sql Кратко как поле RecVersion работает в аксапте: 1. RecVersion = 0 при создани записи 2. RecVersion = random(int) при любом обновлении записи (повторю: это делает ядро Аксапты, программировать это не надо) Инвариант: С огромной вероятностью recVersion после обновления будет отличаться от recVersion до обновления (ах это "почти"... длинные рассуждения о почти уникальных GUID и о инженерном подходе "на практике работоспособно") Что предлагается 5. 5.1. у вас есть справочник, состоящий из нескольких связанных таблиц (tab1, tab2 ... tabN) 5.2. у каждого справочника аксапта обслуживает служебное поле recVersion 5.3. Создайте shadow таблицу для вашего справочника tabShadow, в которой храните: 5.3.1. refRecVersion1, ... refRecVersionN - recVersion таблиц, которые вы выгрузили во внешнюю систему 5.3.2. [опционально] сериализованные значений полей, которые вы выгружаете. Важно, чтобы: 5.3.2.1. сериализованные значения можно было удобно сравнивать на равенство (изменились ли?) 5.3.2.2. сериализовать можно в аксаптовский container - тогда не надо будет программировать парсинг. Но конейнеры в MS SQL хранятся в (Memo)-полях. Со всеми вытекающими для производительности 5.3.3. FK к вашему справочнику, которые позволят однозначно связать shadow-запись и запись в справочнике (связь 0,1..0,1) 5.4. при каждой выгрузке обновляйте shadow-таблицу 5.5. shadow таблица позволит вам четко определить запросами 4 состояния: 5.5.1. есть запись в tab, нет записи в shadow - запись в справочнике создана, ни разу не выгружалась 5.5.2. есть запись в tab, есть запись в shadow, recVersion не совпадают - запись в справочнике изменилась, надо выгрузить 5.5.3. есть запись в tab, есть запись в shadow, recVersion совпадают - запись в справочнике не изменалась 5.5.4. ест записи в tab, есть запись в shadow - запись удалена, надо дать команду на удаление внешней системе 5.6. c shadow таблицей вы можете накладывать любые фильтры на справочник и получать измененные записи только среди отфильтрованных 5.7. shadow таблица не будет нагружать вашу систему паразитной нагрузкой в insert/update/delete и логах. будет работать только по запросу там еще куча соображений. но пока хватит. |
|
Теги |
aif, ax2012, change tracking, интеграция, как правильно |
|
|