AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
DAX
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 12.01.2021, 14:33   #1  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Выгрузка измененных клиентов во внешнюю систему
Стоит такая задача - есть внешняя система, ей как-то надо передавать клиентов из АХ которые изменились за интервал времени.
Клиентов много(около 6млн)
Интервал хотят около минуты, т.е. раз в минуту идет запрос к АХ - дай мне клиентов(из определенной группы), которые изменились за последнюю минуту.
Полей для отслеживания немного, Код клиента, Имя, Адрес, Мобильный телефон, Почта. Проблема что все они разбросаны по разным таблицам
Решал ли кто-нибудь такую задачу и как решали? Версия АХ2012
Старый 12.01.2021, 15:06   #2  
EVGL is offline
EVGL
Banned
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
4,445 / 3001 (0) ++++++++++
Регистрация: 09.07.2002
Адрес: Parndorf, AT
Может быть, решить в D365-стиле:
https://community.dynamics.com/ax/b/...g-in-ax2012-r3
?
За это сообщение автора поблагодарили: Vadik (1), trud (2).
Старый 12.01.2021, 15:22   #3  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
"Из коробки" есть поддержка change tracking в AIF, но .getChangedKeys() пожалуй слишком "тяжело" реализован чтобы его раз в минуту вызывать, тем более на таких объемах, плюс чувствителен к рассинхронизации времени на вызывающей стороне и в AX.
У "интервал хотят около минуты" требования есть какие-то разумные обоснования ?
__________________
-ТСЯ или -ТЬСЯ ?
Старый 12.01.2021, 15:24   #4  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Спасибо. Я правда не очень понимаю, как это будет работать,
1 - change tracking он же включится для всей таблицы, получается будет много пустых срабатываний(когда поменялись не нужные нам поля)?
2 - плюс по связям - для примера LOGISTICSELECTRONICADDRESS напрямую с клиентом не связана(связана через несколько таблиц). что в этом случае будет трекаться
3 - как делать горизонтальную фильтрацию, т.е. мне нужны клиенты которые менялись из заданной группы, причем группа может быть разной. Описанный способ после запроса просто помечает весь запрос как использованный
Старый 12.01.2021, 15:32   #5  
axm2017 is offline
axm2017
Участник
 
1,747 / 292 (13) ++++++
Регистрация: 15.05.2017
Цитата:
Сообщение от trud Посмотреть сообщение
Полей для отслеживания немного, Код клиента, Имя, Адрес, Мобильный телефон, Почта. Проблема что все они разбросаны по разным таблицам
Почему не создать табличку нужных данных/добавить нужные поля в существующую раз полей немного, с modify date time и обновлением данных при изменении первичных данных?
Старый 12.01.2021, 15:35   #6  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от trud Посмотреть сообщение
change tracking он же включится для всей таблицы?
да

Цитата:
плюс по связям - для примера LOGISTICSELECTRONICADDRESS напрямую с клиентом не связана(связана через несколько таблиц). что в этом случае будет трекаться
CT включается для всех таблиц в документе / запросе (AxdCustomer если я правильно помню)

Цитата:
как делать горизонтальную фильтрацию, т.е. мне нужны клиенты которые менялись из заданной группы, причем группа может быть разной
через document filters (фильтры правда только на датасорсе "верхнего" уровня можно задавать, т.е. на CustTable но не на адресах)
__________________
-ТСЯ или -ТЬСЯ ?

Последний раз редактировалось Vadik; 12.01.2021 в 15:42.
Старый 12.01.2021, 15:41   #7  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от axm2017 Посмотреть сообщение
обновлением данных при изменении первичных данных?
Пока пытаюсь понять направление. В этом как раз и загвоздка, как делать такое обновление. Могу ошибаться, но припоминаю проблемы типа что при изменении LOGISTICSELECTRONICADDRESS (именно в момент вставки), связи с клиентом там еще не будет, т.е. DIRPARTYLOCATION создастся позже, т.е. надо именно искать место в классах где это меняется, что трудоемко.
Старый 12.01.2021, 15:42   #8  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
вот тут CT достаточно подробно обсуждалось
__________________
-ТСЯ или -ТЬСЯ ?

Последний раз редактировалось Vadik; 12.01.2021 в 16:03.
За это сообщение автора поблагодарили: trud (3).
Старый 12.01.2021, 16:13   #9  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от Vadik Посмотреть сообщение
CT включается для всех таблиц в документе / запросе (AxdCustomer если я правильно помню)
А как он тогда будет получать то что поменялось? просто адресов там тоже много(17млн).
Клиент уже пробовал реализовать это включением даты модификации на каждой таблице, и запросом где дата модификации фильтруется по ИЛИ для каждой таблицы. Это работает, но медленно, т.е. запрос выполняется больше чем минуту, а таких приходит по десятку в минуту для разных групп
Еще вопрос с ченж трекингом - он живет несколько дней, как быть если они захотят обновить данные полностью (к примеру получить клиентов за последние несколько лет)? писать отдельную обработку?

Пример запроса который они сделали


X++:
..
	 INNER JOIN LOGISTICSELECTRONICADDRESS leaEmail
	    ON dplEmail.LOCATION = leaEmail.LOCATION
	   AND leaEmail.TYPE = 2 -- Email
	   AND (leaEmail.VALIDFROM <= @sync_new_received_anchor AND leaEmail.VALIDTO > @sync_new_received_anchor)
	  LEFT OUTER join DIRNAMEAFFIX dna
	    ON dna.RECID = dp.PERSONALTITLE
	   AND dna.AFFIXTYPE = 1
	  LEFT OUTER JOIN (
		   SELECT dpl.PARTY as PARTY, lea.LOCATOR as LOCATOR,
				  ll.MODIFIEDDATETIME as LL_MODIFIEDDATETIME,
				  lea.MODIFIEDDATETIME as LEA_MODIFIEDDATETIME
			 FROM DIRPARTYLOCATION dpl
			INNER JOIN LOGISTICSLOCATION ll
			   ON ll.RECID = dpl.LOCATION
			  AND ll.DESCRIPTION = 'Home' -- Home phone number
			INNER JOIN LOGISTICSELECTRONICADDRESS lea
			   ON lea.LOCATION = dpl.LOCATION
			  AND lea.TYPE = 1 -- Phone
			  AND (lea.VALIDFROM <= @sync_new_received_anchor AND lea.VALIDTO > @sync_new_received_anchor)
			WHERE (dpl.VALIDFROM <= @sync_new_received_anchor AND dpl.VALIDTO > @sync_new_received_anchor)
			) homePhone
		ON homePhone.PARTY = dp.RECID
	  LEFT OUTER JOIN (
		   SELECT dpl.PARTY as PARTY, lea.LOCATOR as LOCATOR,
				  ll.MODIFIEDDATETIME as LL_MODIFIEDDATETIME,
				  lea.MODIFIEDDATETIME as LEA_MODIFIEDDATETIME
			 FROM DIRPARTYLOCATION dpl
			INNER JOIN LOGISTICSLOCATION ll
			   ON ll.RECID = dpl.LOCATION
			  AND ll.DESCRIPTION = 'Mobile' -- Mobile phone number
			INNER JOIN LOGISTICSELECTRONICADDRESS lea
			   ON lea.LOCATION = dpl.LOCATION
			  AND lea.TYPE = 1 -- Phone
			  AND (lea.VALIDFROM <= @sync_new_received_anchor AND lea.VALIDTO > @sync_new_received_anchor)
			WHERE (dpl.VALIDFROM <= @sync_new_received_anchor AND dpl.VALIDTO > @sync_new_received_anchor)
			) mobilePhone
		ON mobilePhone.PARTY = dp.RECID
	 WHERE c.DATAAREAID = @AXDataAreaId
	   AND c.OWIGUID != '00000000-0000-0000-0000-000000000000'
	   AND c.ONETIMECUSTOMER = 0 -- Don't sync one time customers
	   AND c.CUSTGROUP = @custGroup
	   AND (
			   (c.MODIFIEDDATETIME >= @sync_last_received_anchor AND c.MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (dpn.MODIFIEDDATETIME >= @sync_last_received_anchor AND dpn.MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (leaEmail.MODIFIEDDATETIME >= @sync_last_received_anchor AND leaEmail.MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (dna.MODIFIEDDATETIME >= @sync_last_received_anchor AND dna.MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (homePhone.LEA_MODIFIEDDATETIME >= @sync_last_received_anchor AND homePhone.LEA_MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (homePhone.LL_MODIFIEDDATETIME >= @sync_last_received_anchor AND homePhone.LL_MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (mobilePhone.LEA_MODIFIEDDATETIME >= @sync_last_received_anchor AND mobilePhone.LEA_MODIFIEDDATETIME < @sync_new_received_anchor)
			OR (mobilePhone.LL_MODIFIEDDATETIME >= @sync_last_received_anchor AND mobilePhone.LL_MODIFIEDDATETIME < @sync_new_received_anchor)
		   )
Старый 12.01.2021, 16:27   #10  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,890 / 5647 (194) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Мне всегда казалось, что для таких задач не modifieddatetime используется, а modifiedTransactionId. По нему проще индекс построить да и highmark тоже проще хранить.
Плюс я бы попробовал логику сбора изменений слегка изменить. Сначала я бы завел временную таблицу с CustAccount. Потом написал бы запрос по custTable->DirpartyTable->LogisticsPostalAddress (в общем - я точную последовательность не помню, но в общем развернул бы полную цепочку), в последнюю таблицу добавил бы условие modifiedTransactionId>highMark. Результаты сложил бы во временную табличку. Потом аналогичный запрос, но уже более простой custTable->DirpartyTable к последней табличке опять бы добавил условие ModifiedTransactionId>highMark.
В итоге - после того как каждый уровень запроса опрошен на предмет наличия изменений, сделал бы обычный джойн со всеми данными, но в этот джойн добавил бы exists join ко временной таблице. Ну и чтобы не тормозило - по всем табличкам добавил бы индекс modifiedTransactionId+joinField. Каждый отдельный запрос относительно простой и можно в случае чего индексов достроить. Ну и в итоге - запрос обычного набора данных для уже отобранных 10-15 годовых клиентских кодов вполне можно быстро прочитать.
Старый 12.01.2021, 16:42   #11  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Да, это кстати тоже идея - разбить этот большой запрос на подзапросы и потом все объединить, получается на первый взгляд проще чем с созданием лога или ченж трекинга
Старый 12.01.2021, 18:12   #12  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от trud Посмотреть сообщение
А как он тогда будет получать то что поменялось? просто адресов там тоже много(17млн)
AIF в runtime генерит запрос который из CT вытаскивает изменения по каждой таблице в Query, их по relations "пристыковывает" к "главной" таблице и объединяет все через UNION. Т.е. одному клиенту поменяли группу, второму отчество, третьему адрес а четвертому - мобильный, но getChangedKeys() увидит и вернет все четыре AxdCustomer документа как измененные. Надежно, удобно, но достаточно затратно чтобы такие трюки раз в минуту на 6М клиентов проворачивать.

Цитата:
Еще вопрос с ченж трекингом - он живет несколько дней, как быть если они захотят обновить данные полностью (к примеру получить клиентов за последние несколько лет)? писать отдельную обработку?
"Измененные данные", "быстро и часто" и "все за несколько лет" это же в некотором смысле требования противоречащие друг другу ? В теории, если изначально реализовано нормально (с поддержкой paging-а), это может работать (но быстро точно не будет), но я бы не бросался за реализацию пока клиент не определился с тем что и как он хочет интегрировать
__________________
-ТСЯ или -ТЬСЯ ?
Старый 12.01.2021, 18:27   #13  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
ну требования просты - есть функция(хранимая процедура) которая принимает группу клиентов, и интервал. надо вернуть все данные которые менялись в этом интервале.
в нормальном режиме будут запускать раз в минуту по группе(т.е. в минуту будет даже больше запусков, несколько десятков)-это хотелось бы сделать быстро. но ничего не мешает запустить произвольный интервал, например за год
Тут еще в процессе обдумывания возникло понимание что дату модификации вообще использовать неправильно, она может принадлежать прошлому периоду, если у нас большая транзакция.
т.е. процесс должен иметь несколько стадий, первая стадия помечает запись как измененную. Вторая стадия должна как-то этому изменению присваивать дату(можно наверное заложиться что транзакция точно закомитится в пределах секунды) и присваивать дату как текущую плюс 1 секунда
Что-то получается все не просто

Последний раз редактировалось trud; 12.01.2021 в 18:53.
Старый 12.01.2021, 18:41   #14  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от Vadik Посмотреть сообщение
AIF в runtime генерит запрос который из CT вытаскивает изменения по каждой таблице в Query, их по relations "пристыковывает" к "главной" таблице и объединяет все через UNION. Т.е. одному клиенту поменяли группу, второму отчество, третьему адрес а четвертому - мобильный, но getChangedKeys() увидит и вернет все четыре AxdCustomer документа как измененные.
А вот этот момент можешь раскрыть подробнее? т.е. в исходном запросе будут outer join для всяких адресов, емейлов и прочего.
Будет ли эта штука(с пристыковываниями) работать с таблицей которая присоединена по outer join(при том что таких таблиц несколько в одном запросе)?

Последний раз редактировалось trud; 12.01.2021 в 18:43.
Старый 12.01.2021, 19:08   #15  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от trud Посмотреть сообщение
ну требования просты - есть функция(хранимая процедура) которая принимает группу клиентов, и интервал. надо вернуть все данные которые менялись в этом интервале
вот так (CT + фильтр наложенный в runtime) AIF не умеет, document filters жестко на уровне порта заданы

Цитата:
А вот этот момент можешь раскрыть подробнее? т.е. в исходном запросе будут outer join для всяких адресов, емейлов и прочего.
Будет ли эта штука(с пристыковываниями) работать с таблицей которая присоединена по outer join(при том что таких таблиц несколько в одном запросе)?
Да, понимаю что звучит как черная магия но - будет. Самый простой способ это увидеть/пощупать/потрейсить - это настроить входящий порт с операцией getChangedKeys() на интересующем документе и CT, и дернуть ее из WCF client от Visual Studio, ну или джобом
__________________
-ТСЯ или -ТЬСЯ ?
Старый 12.01.2021, 20:26   #16  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от trud Посмотреть сообщение
ну требования просты - есть функция(хранимая процедура) которая принимает группу клиентов, и интервал. надо вернуть все данные которые менялись в этом интервале.
в нормальном режиме будут запускать раз в минуту по группе(т.е. в минуту будет даже больше запусков, несколько десятков)
Гонять запросы "а что там поменялось в 6М клиентов" между двумя системами чуть ли не ежесекундно - не понимаю кому и зачем это нужно. Сложно обсуждать реализацию не понимая цели. Одно скажу точно - AIF+CT в таких условиях я бы не использовал
__________________
-ТСЯ или -ТЬСЯ ?

Последний раз редактировалось Vadik; 12.01.2021 в 20:37.
Старый 12.01.2021, 23:45   #17  
axm2017 is offline
axm2017
Участник
 
1,747 / 292 (13) ++++++
Регистрация: 15.05.2017
Цитата:
Сообщение от trud Посмотреть сообщение
Могу ошибаться, но припоминаю проблемы типа что при изменении LOGISTICSELECTRONICADDRESS (именно в момент вставки), связи с клиентом там еще не будет, т.е. DIRPARTYLOCATION создастся позже, т.е. надо именно искать место в классах где это меняется, что трудоемко.
В транзакции?
Если да то можно сделать пакетник который бегает по измененным с прошлого прохода записям LOGISTICSELECTRONICADDRESS скидывая изменения в соответствующую табличку.
Старый 13.01.2021, 00:33   #18  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,038 / 1629 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от axm2017 Посмотреть сообщение
В транзакции?
Если да то можно сделать пакетник который бегает по измененным с прошлого прохода записям LOGISTICSELECTRONICADDRESS скидывая изменения в соответствующую табличку.
Да, в транзакции. А как получить измененные с прошлого прохода записи? Причем таблиц будет 8
Цитата:
Гонять запросы "а что там поменялось в 6М клиентов" между двумя системами чуть ли не ежесекундно - не понимаю кому и зачем это нужно
Ну вроде требование логичное, я так понимаю вторая система - это система в конкретном офисе(локальный веб сайт), когда тебе меняют что-то в АХ, изменения должны как можно скорее отобразиться в этой системе
для этого каждая из них днем когда работают люди дергает хранимую процедуру(дай мне измененные данные за последнюю минуту), как правило они возвращают пусто или 1-2 записи
Цитата:
Да, понимаю что звучит как черная магия но - будет.
скорее всего outer join заменится на inner join. Только опять же проблема что таблиц 8, получается если использовать changetracking будет 8 различных union, есть некоторая вероятность что это перейдет в полный скан на каком -то этапе
Старый 13.01.2021, 08:24   #19  
axm2017 is offline
axm2017
Участник
 
1,747 / 292 (13) ++++++
Регистрация: 15.05.2017
Цитата:
Сообщение от trud Посмотреть сообщение
Да, в транзакции. А как получить измененные с прошлого прохода записи? Причем таблиц будет 8
Типичное ( во всяком случае видел реализации подобной идеи на пакете)

Параметр - время запуска

При запуске процесса выбираем все измененные записи от параметра до текущего момента (ессно при включенном modified time).

Далее после обработки сохраняем в параметре текущее время.

Обработку табличек можно распараллелить: не думаю что в таблицах идет какой то вал массовых изменений.

Последний раз редактировалось axm2017; 13.01.2021 в 08:28.
Старый 13.01.2021, 12:49   #20  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от trud Посмотреть сообщение
Ну вроде требование логичное, я так понимаю вторая система - это система в конкретном офисе(локальный веб сайт), когда тебе меняют что-то в АХ, изменения должны как можно скорее отобразиться в этой системе. Для этого каждая из них днем когда работают люди дергает хранимую процедуру(дай мне измененные данные за последнюю минуту), как правило они возвращают пусто или 1-2 записи
Найти изменения в данных 6М клиентов - это недешевая операция с точки зрения затраченных ресурсов. Делать это раз в минуту - это достаточно дорого. Делать это 10 раз в минуту для 10 групп клиентов - примерно раз в 10 дороже, так как сам по себе наложеный фильтр запрос сильно дешевле не делает, а количество запросов растет

Не думаю что непрерывно вхолостую дрючить источник (AX) запросами с сайта десятки раз в минуту это оптимальное решение. Как мне кажется, логичнее было бы перехватывать изменения в самом источнике и выталкивать сообщения в очередь (AIF outbound queue, MQ, Service Bus, еще что-то)

Из спортивного интереса, я бы позадавал вопросы о том сколько клиентов обновляется в час / сутки (средние и пиковые значения), и что ужасного может произойти если изменения опубликуются скажем в среднем в течение получаса (опрашиваем CT раз в час)
__________________
-ТСЯ или -ТЬСЯ ?

Последний раз редактировалось Vadik; 14.01.2021 в 10:53.
Теги
aif, ax2012, change tracking, интеграция, как правильно

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
AX2012 Общие справочники поставщиков и клиентов PTG DAX: Функционал 2 11.06.2015 15:39
Импорт адресов для существующих клиентов и поставщиков IKA DAX: Программирование 0 10.12.2013 21:04
ax 3.0 Экспорт справочников во внешнюю систему, по какому ключу связаться? Shakr DAX: Программирование 2 11.11.2008 11:34
Сергей Герасимов: О технической поддержке клиентов по продуктам Microsoft Dynamics Blog bot DAX Blogs 4 13.02.2007 14:58
Коды клиентов в CRM - проблема Zabr DAX: Функционал 5 01.12.2003 12:41
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 21:12.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.