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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 07.09.2013, 04:32   #1  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Целостность данных при длительных запросах
Здравствуйте.

Есть запрос к InventSum, который делается через Query. Запрос представляет собой связку InventSum->InventTable->InventDim.
Есть второй запрос к InventTrans, который делается через Query. Запрос представляет собой связку InventTrans->InventTable->InventDim.
Есть третий запрос к InventSettlement, который делается через Query. Запрос представляет собой связку InventSettlement->InventTrans->InventTable->InventDim.

Запросы используются для получения остатков на определенную дату, с заданными группировками. По сути, это переработка классов InventSumDate* с той лишь разницей, что позволяют получать остатки на дату не раздельно для каждой номенклатуры, а в цикле с любой группировкой. То есть вначале получаем текущие остатки из InventSum, затем вычитаем проводки InventTrans. Это позволяет выводить остатки на дату по большому списку номенклатур с максимальный скоростью, так как кол-во запросов в базу сводится к минимуму.

Проблема возникает в следующем. В то время пока перебираются в цикле записи первого Query (по InventSum), кто-то создает проводку в InventTrans. Затем происходит выборка из InventTrans c помощью второго Query (уже с учетом этой созданной проводки). Таким образом, далее мы уже будем вычитать эти самые проводки из "старого" InventSum и получим неверные данные. Каким образом получить консистентные данные по InventSum, InventTrans и InventSettlement?

ps: обрамление этих трех запросов с помощью ttsbegin;/ttscommt; не помогает.

ps2: удалось снизить интервал времени между этими тремя запросами, переместив итерацию по queryRun-м в самый конец, уже после выполнения запроса в БД.

ps3: возможно, поможет обрамление (дополнительно к аксаптовым ttsbegin;ttscommit) userConnection.ttsbegin()/userConnection.ttscommit(). Но, вроде как, для курсоров нужно делать setConnection(), а здесь это не применимо, так как работа идет с Query.
Старый 07.09.2013, 11:58   #2  
raz is offline
raz
NavAx
Аватар для raz
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,490 / 1060 (38) ++++++++
Регистрация: 22.07.2003
Адрес: МО
Ничего не поможет.
Мы эту проблему решали так:
Делали два запроса по InventSum в начале и в конце расчетов, по позициям, которые изменились пересчитывали еще раз, и так до тех пор, пока все не совпадало. Иногда это может приводить к довольно долгому расчету, если паралельно разносятся несколько больших журналов.
За это сообщение автора поблагодарили: Logger (3), Eldar9x (4).
Старый 07.09.2013, 12:10   #3  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Eldar9x Посмотреть сообщение
ps: обрамление этих трех запросов с помощью ttsbegin;/ttscommt; не помогает.
версия то какая? и что у вас с оптимистическими блокировками?
Старый 07.09.2013, 12:41   #4  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Цитата:
Ничего не поможет.
Мы эту проблему решали так:
Делали два запроса по InventSum в начале и в конце расчетов, по позициям, которые изменились пересчитывали еще раз, и так до тех пор, пока все не совпадало. Иногда это может приводить к довольно долгому расчету, если параллельно разносятся несколько больших журналов.
жесть какая-то... А я ведь еще и сомневался, что расхождения возникают именно по этой причине. Хотя время вывода отчета и время возникновения проводок совпадало. Тоже были мысли, в разных потоках что ли начинать запросы в InventSum и InventTrans, чтобы минимизировать время между ними. И ведь тогда получается, что классы InventSumDate* также могут давать неверный результат, хотя вероятность в этом случае уже мизерная?

Цитата:
версия то какая? и что у вас с оптимистическими блокировками?
это 3-ка. В InventSumTTS Admin галка Active отключена, а CheckMode = Оптимистичный. Какие настройки на SQL сервере - не выяснял.
Старый 07.09.2013, 12:50   #5  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от mazzy Посмотреть сообщение
версия то какая? и что у вас с оптимистическими блокировками?
А при чем тут это если отчет несколькими запросами рассчитывается и сериализуемых блокировок на INVENTTRANS / INVENTSUM в процессе рассчета (я надеюсь) не накладывается ?

Eldar9x, вариантов два:
- примите как факт что некоторые (как правило незначительные) расхождения при рассчете остатков по складу "задним числом" (иначе обошлись бы INVENTSUM и INVENTTRANS не шерстили бы) на рабочей БД теоретически могут быть by design
- стройте рядом с рабочей (OLTP) базой данных AX свой DWH (с блэкджеком и шлюхами) и выгружайте данные в него (что в принципе тоже не так просто как может показаться, особенно при работе 24x7)
__________________
-ТСЯ или -ТЬСЯ ?
Старый 07.09.2013, 13:09   #6  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Цитата:
сериализуемых блокировок на INVENTTRANS / INVENTSUM в процессе рассчета (я надеюсь) не накладывается ?
Нет. Это просто выборка данных с помощью Query, безо всяких там "forupdate".

Цитата:
- примите как факт что некоторые (как правило незначительные) расхождения при рассчете остатков по складу "задним числом" (иначе обошлись бы INVENTSUM и INVENTTRANS не шерстили бы) на рабочей БД могут быть by design
- стройте рядом с рабочей (OLTP) базой данных AX свой DWH (с блэкджеком и шлюхами) и выгружайте данные в него (что в принципе тоже не так просто как может показаться, особенно при работе 24x7)
Остатки (кол-во и суммы) должны сходиться до нескольких знаков после запятой. И какие-то внешние ср-ва использовать нельзя, нужно обойтись стандартными. Плюс еще требование скорости.
Старый 07.09.2013, 13:17   #7  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Имеет ли смысл в данной ситуации делать следующее:

X++:
UserConnection userConnection = new UserConnection();
ttsbegin;
userConnection.ttsbegin();
selectRemains(); // выбираем остатки с помощью Query из InventSum и InventTrans
userConnection.ttcommit();
ttscommit;
?
Старый 07.09.2013, 18:35   #8  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
нет, смысла не имеет userConnection, если к нему не привязывать запросы или курсор, ни на что не влияет. АОС и так держит пул этих соединений с БД, и одно из них вы используете в выборке. "Вложенное" соединение - это просто ещё одно соединение из пула, поддерживаемого АОСом, как если бы ещё одна клиентская сессия запустилась бы и что-то читала из БД.
За это сообщение автора поблагодарили: Eldar9x (4).
Старый 07.09.2013, 19:30   #9  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Ясно, спасибо. А каким образом привязать userConnection к запросу (к Query) ?
Старый 08.09.2013, 00:47   #10  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Честно говоря, не знаю, ни разу не видел примера использования отдельного соединения с Query/QueryRun - только с табличными курсорами и обычными select'ами либо с прямыми SQL-запросами.
Старый 09.09.2013, 08:45   #11  
raz is offline
raz
NavAx
Аватар для raz
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,490 / 1060 (38) ++++++++
Регистрация: 22.07.2003
Адрес: МО
Цитата:
Сообщение от Eldar9x Посмотреть сообщение
Ясно, спасибо. А каким образом привязать userConnection к запросу (к Query) ?
Можно попробовать сделать как обычно, userConnection привязать к курсору, а курсор привязать к QueryRun.
Старый 09.09.2013, 10:44   #12  
twilight is offline
twilight
MCTS
MCBMSS
 
870 / 237 (9) ++++++
Регистрация: 17.10.2004
Адрес: Королёв
На одном из проектов для ускорения подсчета остатков на дату на паллетах делали табличку, где хранились изменения остатков на дату: код паллеты, остаток, дата начала, дата окончания. Места она занимает немного, вставляются записи быстро и остаток на дату можно получить элементарным запросом с фильтром по дате. Можно то же самое сделать и для общего случая.
__________________
I could tell you, but then I would have to bill you.

Последний раз редактировалось twilight; 09.09.2013 в 10:46.
Старый 09.09.2013, 16:23   #13  
ice321i is offline
ice321i
Участник
 
60 / 47 (2) +++
Регистрация: 22.10.2007
Если версия 4 и выше, то можно использовать в первом запросе таблицу inventSumDelta.
Пример использования смотри в классе InventUpd_Reservation метод updateReserveMore
Старый 09.09.2013, 17:12   #14  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,894 / 5650 (194) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Я этого никогда не пробовал, но можно попытаться. Начиная с версии SQL 2005 есть такой специальный уровень изоляции транзакций: SNAPSHOT ISOLATION. Если ты в скрипте на T-SQL пишешь SET TRANSACTION ISOLATION SNAPSHOT, а потом начинаешь транзакцию через вызов BEGIN TRANSACTION, то в системе включается версионная машина и твои запросы не видят ВСЕ изменения данных, которые были сделаны после того как ты выполнил вызов BEGIN TRANSACTION. То есть - консистентное состояние сохраняется не в рамках одного запроса, а для всех запросов внутри транзакции.
Есть, правда, некоторые "НО":
  1. Поддержку этого режима надо включать на уровне базы данных.ALTER DATABASE SET ALLOW_SNAPSHT_ISOLATION ON. Это некоторый оверхид создает.
  2. Главная проблема: Насколько я понимаю, до тех пор пока транзакция в коде на X++ не начата, все пользовательские сесии шарят одно соединение и если ты просто так туда залезешь через Connection/Statement и засунешь режим изоляции SET TRANSACTION ISOLATION SNAPSHOT, то этот режим включиться не только для твоей сесии, а вообще для всех пользователей в системе. Если же ты вызовешь ttsbegin, то система тебе выделит отдельную сесию, в которой ты можешь безбоязненно менять уровень изоляции. Но вот поскольку ты уже находишься в режиме транзакции, то смена режима изоляции на SNAPSHOT уже не возможна...

В общем - я бы попробовал все-таки тупо засовывать уровень изоляции через connection/statement, а потом вызвал бы connection.ttsbegin(), чтобы начать транзакцию. Есть большая надежда (хотя я и не уверен), что менежеру соединений в AOS хватит ума выделить отдельное соединение в тот момент когда ты connection вызываешь, а не в тот момент когда ты транзакцию начинаешь. Кроме того, помнится мне говорили, что то самое разделяемое соединение используется ТОЛЬКО логикой на формах, а если ты пишешь запрос в классе, то система использует выделенное соединение, которое прикрепляется к пользовательской сесии до тех пор, пока класс не отработает...

Последний раз редактировалось fed; 09.09.2013 в 17:23.
Старый 09.09.2013, 22:59   #15  
anikulichev is offline
anikulichev
Участник
 
76 / 23 (1) +++
Регистрация: 26.12.2002
Адрес: г.Москва
Цитата:
Сообщение от fed Посмотреть сообщение
Поддержку этого режима надо включать на уровне базы данных.ALTER DATABASE SET ALLOW_SNAPSHT_ISOLATION ON. Это некоторый оверхид создает.
Кстати говоря в документе "Planning database configuration for Microsoft Dynamics AX" есть прямое указание на включение этого режима.
Старый 10.09.2013, 06:33   #16  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,894 / 5650 (194) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от anikulichev Посмотреть сообщение
Кстати говоря в документе "Planning database configuration for Microsoft Dynamics AX" есть прямое указание на включение этого режима.
Не совсем. Там требуют включать Read Committed Snapshot Isolation. В этом режиме, в режиме Read Committed включается версионная машина (ВНУТРИ ОДНОГО ЗАПРОСА) не ставяться блокировки чтения. SNAPSHOT ISOLATION - это отдельный режим изоляции транзакций и для его поддержки надо другую опцию у базы данных включать.
За это сообщение автора поблагодарили: belugin (5).
Старый 10.09.2013, 12:45   #17  
raz is offline
raz
NavAx
Аватар для raz
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,490 / 1060 (38) ++++++++
Регистрация: 22.07.2003
Адрес: МО
Цитата:
Сообщение от raz Посмотреть сообщение
Можно попробовать сделать как обычно, userConnection привязать к курсору, а курсор привязать к QueryRun.
Рабочий пример. НО! Решить проблему не поможет, решение есть выше.

X++:
static void edd_testUserConnection(Args _args)
{
    UserConnection          userConnection;
    InventTable             inventTable;
    InventSum               inventSum;
    
    Query                   query;
    QueryBuildDataSource    qbds;
    QueryBuildRange         qdr;
    QueryRun                queryRun;
    
    boolean                 useAlterConnection = true;
    int                     i;
    ;
    query = new Query();
    qbds  = query.addDataSource(tableNum(InventSum));
    qdr   = qbds.addRange(fieldNum(InventSum, ItemId));
    qbds.addSelectionField(fieldNum(InventSum, PhysicalInvent), SelectionField::Sum);
    qbds.addSortField(fieldNum(InventSum, ItemId));
    qbds.orderMode(OrderMode::GroupBy);

    if (useAlterConnection)
    {
        userConnection = new UserConnection();
        inventSum.setConnection(userConnection);
    }
    
    while select inventTable
    {
        i++;
        
        qdr.value(queryValue(inventTable.ItemId));
        
        queryRun = new QueryRun(query);
        queryRun.setCursor(inventSum);
        queryRun.next();
        
        inventSum = queryRun.getNo(1);
    
        info(strfmt("%1: %2", inventTable.ItemId, inventSum.PhysicalInvent));
        
        sleep(1000);
        
        if (i >= 20)
        {
            break;
        }
    }
}
ЗЫ. Результат видно в активных пользователях.
Старый 11.09.2013, 19:13   #18  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,656 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Как вариант

1. В таблице InventTrans установить свойства

CreatedDate = Yes
CreatedTime = Yes

2. Непосредственно перед выборкой в InventSum сохранить в переменные systemDateGet() и TimeNow()

3. В запрос по InventTrans непосредственно перед выборкой добавить примерно такое условие (пишу в псевдокоде для Query надо будет адаптировать)

X++:
where 
(...)
AND (InventTrans.createdDate < systemDateGet() 
        OR (InventTrans.createdDate = systemDateGet() AND InventTrans.createdTime <= timeNow()))
Т.е. отсечь все складские проводки, которые могут быть созданы после начала выборки в InventSum по дате и времени создания.

Вопрос только в том, какое именно время записывается в CreatedTime. В смысле, это время SQL-сервера, AOS или клиента? Поскольку, если это все 3 физически разных компьютера, то их время может различаться. Если не ошибаюсь, то это должно быть время AOS. Т.е. timeNow() надо вычислять на стороне сервера
__________________
- Может, я как-то неправильно живу?!
- Отчего же? Правильно. Только зря...
За это сообщение автора поблагодарили: Eldar9x (5).
Старый 11.09.2013, 21:11   #19  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,429 / 1772 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от Владимир Максимов Посмотреть сообщение
Как вариант

1. В таблице InventTrans установить свойства

CreatedDate = Yes
CreatedTime = Yes
тогда уж ещё и ModifiedDate и ModifiedTime. Во время формирования первого запроса могут быть разнесены ранее созданные поводки. И что делать с этими изменёнными проводками? заново пересчитывать данные по таким номенклатурам?
Старый 11.09.2013, 21:27   #20  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,656 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
тогда уж ещё и ModifiedDate и ModifiedTime. Во время формирования первого запроса могут быть разнесены ранее созданные поводки. И что делать с этими изменёнными проводками? заново пересчитывать данные по таким номенклатурам?
Да. Наверное лучше использовать Modified вместо Create. Разумеется, если в процессе выборки не выполняется закрытие склада
__________________
- Может, я как-то неправильно живу?!
- Отчего же? Правильно. Только зря...
Теги
остатки на дату

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Связывание источников данных в запросах r25 DAX: Программирование 10 17.04.2013 22:45
Создание снимков изменений в базе данных Ace of Database DAX: Программирование 17 01.11.2011 12:34
Скрипт для переноса данных Ax3.0 (Oracle) - Ax2009 (MSSQL) someOne DAX: Программирование 2 14.06.2011 14:53
Невозможно выполнить команду языка определения данных в () iHomer13 DAX: Программирование 8 18.07.2008 10:56
Как проверить целостность данных ledenezz DAX: База знаний и проекты 5 07.12.2007 05:35

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

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

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