AXForum  
Go Back   AXForum > Microsoft Dynamics AX > DAX: Программирование
DAX
Forgotten Your Password?
Register Forum Rules FAQ Members List Today's Posts Search

 
 
Thread Tools Search this Thread Display Modes
Old 07.09.2013, 04:32   #1  
Eldar9x is offline
Eldar9x
MCTS
Eldar9x's Avatar
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Join Date: 29.09.2006
Location: Казань
Целостность данных при длительных запросах
Здравствуйте.

Есть запрос к 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.
Old 07.09.2013, 11:58   #2  
raz is offline
raz
NavAx
raz's Avatar
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,499 / 1097 (39) ++++++++
Join Date: 22.07.2003
Location: МО
Ничего не поможет.
Мы эту проблему решали так:
Делали два запроса по InventSum в начале и в конце расчетов, по позициям, которые изменились пересчитывали еще раз, и так до тех пор, пока все не совпадало. Иногда это может приводить к довольно долгому расчету, если паралельно разносятся несколько больших журналов.
This post has been rated by: Logger (3), Eldar9x (4).
Old 07.09.2013, 12:10   #3  
mazzy is offline
mazzy
Участник
mazzy's Avatar
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Join Date: 29.11.2001
Location: Москва
Blog Entries: 10
Quote:
Originally Posted by Eldar9x View Post
ps: обрамление этих трех запросов с помощью ttsbegin;/ttscommt; не помогает.
версия то какая? и что у вас с оптимистическими блокировками?
Old 07.09.2013, 12:41   #4  
Eldar9x is offline
Eldar9x
MCTS
Eldar9x's Avatar
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Join Date: 29.09.2006
Location: Казань
Quote:
Ничего не поможет.
Мы эту проблему решали так:
Делали два запроса по InventSum в начале и в конце расчетов, по позициям, которые изменились пересчитывали еще раз, и так до тех пор, пока все не совпадало. Иногда это может приводить к довольно долгому расчету, если параллельно разносятся несколько больших журналов.
жесть какая-то... А я ведь еще и сомневался, что расхождения возникают именно по этой причине. Хотя время вывода отчета и время возникновения проводок совпадало. Тоже были мысли, в разных потоках что ли начинать запросы в InventSum и InventTrans, чтобы минимизировать время между ними. И ведь тогда получается, что классы InventSumDate* также могут давать неверный результат, хотя вероятность в этом случае уже мизерная?

Quote:
версия то какая? и что у вас с оптимистическими блокировками?
это 3-ка. В InventSumTTS Admin галка Active отключена, а CheckMode = Оптимистичный. Какие настройки на SQL сервере - не выяснял.
Old 07.09.2013, 12:50   #5  
Vadik is offline
Vadik
Модератор
Vadik's Avatar
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1853 (69) ++++++++
Join Date: 18.11.2002
Location: гражданин Москвы
Quote:
Originally Posted by mazzy View Post
версия то какая? и что у вас с оптимистическими блокировками?
А при чем тут это если отчет несколькими запросами рассчитывается и сериализуемых блокировок на INVENTTRANS / INVENTSUM в процессе рассчета (я надеюсь) не накладывается ?

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

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

X++:
UserConnection userConnection = new UserConnection();
ttsbegin;
userConnection.ttsbegin();
selectRemains(); // выбираем остатки с помощью Query из InventSum и InventTrans
userConnection.ttcommit();
ttscommit;
?
Old 07.09.2013, 18:35   #8  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5813 (201) ++++++++++
Join Date: 28.11.2005
Location: Москва
Blog Entries: 3
нет, смысла не имеет userConnection, если к нему не привязывать запросы или курсор, ни на что не влияет. АОС и так держит пул этих соединений с БД, и одно из них вы используете в выборке. "Вложенное" соединение - это просто ещё одно соединение из пула, поддерживаемого АОСом, как если бы ещё одна клиентская сессия запустилась бы и что-то читала из БД.
This post has been rated by: Eldar9x (4).
Old 07.09.2013, 19:30   #9  
Eldar9x is offline
Eldar9x
MCTS
Eldar9x's Avatar
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Join Date: 29.09.2006
Location: Казань
Ясно, спасибо. А каким образом привязать userConnection к запросу (к Query) ?
Old 08.09.2013, 00:47   #10  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5813 (201) ++++++++++
Join Date: 28.11.2005
Location: Москва
Blog Entries: 3
Честно говоря, не знаю, ни разу не видел примера использования отдельного соединения с Query/QueryRun - только с табличными курсорами и обычными select'ами либо с прямыми SQL-запросами.
Old 09.09.2013, 08:45   #11  
raz is offline
raz
NavAx
raz's Avatar
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,499 / 1097 (39) ++++++++
Join Date: 22.07.2003
Location: МО
Quote:
Originally Posted by Eldar9x View Post
Ясно, спасибо. А каким образом привязать userConnection к запросу (к Query) ?
Можно попробовать сделать как обычно, userConnection привязать к курсору, а курсор привязать к QueryRun.
Old 09.09.2013, 10:44   #12  
twilight is offline
twilight
MCTS
MCBMSS
 
890 / 241 (10) ++++++
Join Date: 17.10.2004
Location: Королёв
На одном из проектов для ускорения подсчета остатков на дату на паллетах делали табличку, где хранились изменения остатков на дату: код паллеты, остаток, дата начала, дата окончания. Места она занимает немного, вставляются записи быстро и остаток на дату можно получить элементарным запросом с фильтром по дате. Можно то же самое сделать и для общего случая.
__________________
I could tell you, but then I would have to bill you.

Last edited by twilight; 09.09.2013 at 10:46.
Old 09.09.2013, 16:23   #13  
ice321i is offline
ice321i
Участник
 
63 / 54 (2) ++++
Join Date: 22.10.2007
Если версия 4 и выше, то можно использовать в первом запросе таблицу inventSumDelta.
Пример использования смотри в классе InventUpd_Reservation метод updateReserveMore
Old 09.09.2013, 17:12   #14  
fed is offline
fed
Moderator
fed's Avatar
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,914 / 5737 (197) ++++++++++
Join Date: 13.03.2002
Location: 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 вызываешь, а не в тот момент когда ты транзакцию начинаешь. Кроме того, помнится мне говорили, что то самое разделяемое соединение используется ТОЛЬКО логикой на формах, а если ты пишешь запрос в классе, то система использует выделенное соединение, которое прикрепляется к пользовательской сесии до тех пор, пока класс не отработает...

Last edited by fed; 09.09.2013 at 17:23.
Old 09.09.2013, 22:59   #15  
anikulichev is offline
anikulichev
Участник
 
76 / 23 (1) +++
Join Date: 26.12.2002
Location: г.Москва
Quote:
Originally Posted by fed View Post
Поддержку этого режима надо включать на уровне базы данных.ALTER DATABASE SET ALLOW_SNAPSHT_ISOLATION ON. Это некоторый оверхид создает.
Кстати говоря в документе "Planning database configuration for Microsoft Dynamics AX" есть прямое указание на включение этого режима.
Old 10.09.2013, 06:33   #16  
fed is offline
fed
Moderator
fed's Avatar
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,914 / 5737 (197) ++++++++++
Join Date: 13.03.2002
Location: Hüfingen,DE
Quote:
Originally Posted by anikulichev View Post
Кстати говоря в документе "Planning database configuration for Microsoft Dynamics AX" есть прямое указание на включение этого режима.
Не совсем. Там требуют включать Read Committed Snapshot Isolation. В этом режиме, в режиме Read Committed включается версионная машина (ВНУТРИ ОДНОГО ЗАПРОСА) не ставяться блокировки чтения. SNAPSHOT ISOLATION - это отдельный режим изоляции транзакций и для его поддержки надо другую опцию у базы данных включать.
This post has been rated by: belugin (5).
Old 10.09.2013, 12:45   #17  
raz is offline
raz
NavAx
raz's Avatar
NavAx Club
Лучший по профессии 2014
Лучший по профессии 2009
 
1,499 / 1097 (39) ++++++++
Join Date: 22.07.2003
Location: МО
Quote:
Originally Posted by raz View Post
Можно попробовать сделать как обычно, 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;
        }
    }
}
ЗЫ. Результат видно в активных пользователях.
Old 11.09.2013, 19:13   #18  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,720 / 1207 (44) ++++++++
Join Date: 13.01.2004
Blog Entries: 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() надо вычислять на стороне сервера
__________________
- Может, я как-то неправильно живу?!
- Отчего же? Правильно. Только зря...
This post has been rated by: Eldar9x (5).
Old 11.09.2013, 21:11   #19  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,449 / 1792 (66) ++++++++
Join Date: 28.04.2007
Location: Калуга
Quote:
Originally Posted by Владимир Максимов View Post
Как вариант

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

CreatedDate = Yes
CreatedTime = Yes
тогда уж ещё и ModifiedDate и ModifiedTime. Во время формирования первого запроса могут быть разнесены ранее созданные поводки. И что делать с этими изменёнными проводками? заново пересчитывать данные по таким номенклатурам?
Old 11.09.2013, 21:27   #20  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,720 / 1207 (44) ++++++++
Join Date: 13.01.2004
Blog Entries: 3
Quote:
Originally Posted by S.Kuskov View Post
тогда уж ещё и ModifiedDate и ModifiedTime. Во время формирования первого запроса могут быть разнесены ранее созданные поводки. И что делать с этими изменёнными проводками? заново пересчитывать данные по таким номенклатурам?
Да. Наверное лучше использовать Modified вместо Create. Разумеется, если в процессе выборки не выполняется закрытие склада
__________________
- Может, я как-то неправильно живу?!
- Отчего же? Правильно. Только зря...
Tags
остатки на дату

 

Similar Threads
Thread Thread Starter Forum Replies Last Post
Связывание источников данных в запросах 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

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Рейтинг@Mail.ru
All times are GMT +3. The time now is 17:30.
Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Contacts E-mail, Advertising.