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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 18.01.2019, 17:28   #1  
db is offline
db
Роман Долгополов (RDOL)
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
 
393 / 692 (24) +++++++
Регистрация: 01.04.2004
Адрес: Москва
Как обращаться к такой таблице в коде? Первый вариант уже сказали - табличный мап и инициализация через MakeRecord. Второй вариант - дублировать таблицу чтобы FieildId были такие как в таблице с решетом и то-же инициализация через MakeRecord. В этом случае заработают insert-update-delete_recordset. Этакий хакерский мап в виде таблицы.

Работа с мапом обычным (mInventSumDeltaKeyMap - мап)
X++:
   mInventSumDeltaKeyMap   inventSumDeltaKey = mInventSumDeltaKeyMap::tmpTableInit();
    InventSum               inventSum;
    InventSum               inventSumNew;
    RecordInsertList        insertList;
    ;
    while select ItemId, InventDimId from inventSumDeltaKey
        group by ItemId, InventDimId
        notexists join inventSum
            where inventSum.ItemId         == inventSumDeltaKey.ItemId &&
                  inventSum.InventDimId    == inventSumDeltaKey.InventDimId
    {
        if (!inventSumDeltaKey.ItemId || !inventSumDeltaKey.InventDimId)
        {
            throw error(strfmt("@SYS68912",funcname()));
        }
        if (! insertList)
        {
            insertList = new RecordInsertList(tablenum(InventSum), true, true, true);
        }
        inventSumNew.ItemId         = inventSumDeltaKey.ItemId;
        inventSumNew.InventDimId    = inventSumDeltaKey.InventDimId;
        inventSumNew.ClosedQty      = NoYes::Yes;
        inventSumNew.Closed         = NoYes::Yes;
        insertList.add(inventSumNew);
    }
    if (insertList)
    {
        insertList.insertDatabase();
    }
или так (mLedgerBalancesTransDeltaMap - мап)
X++:
    mLedgerBalancesTransDeltaMap    ledgerBalancesTransDelta = trecord::init(this.getBalanceDeltaTableId());
    ;
    unchecked(Uncheck::TableSecurityPermission)
    {
        insert_recordset
            ledgerBalancesTrans (AccountNum, PeriodCode, TransDate, SystemGeneratedUltimo, DebitMST,
                                 CreditMST, DebitOPRMST, CreditOPRMST, DebitTaxMST, CreditTaxMST, DebitMSTSecond,
                                 CreditMSTSecond, DebitOPRMSTSecond, CreditOPRMSTSecond, DebitTaxMSTSecond, CreditTaxMSTSecond, Qty)
        select forceLiterals
            LedgerAccountNum, PeriodCode, TransDate, SystemGeneratedUltimo, sum(DebitMST), sum(CreditMST), sum(DebitOPRMST), sum(CreditOPRMST), sum(DebitTaxMST),
            sum(CreditTaxMST), sum(DebitMSTSecond), sum(CreditMSTSecond), sum(DebitOPRMSTSecond), sum(CreditOPRMSTSecond), sum(DebitTaxMSTSecond), sum(CreditTaxMSTSecond), sum(Quantity)
        from
            ledgerBalancesTransDelta group by LedgerAccountNum, PeriodCode, TransDate, SystemGeneratedUltimo;
    }
Работа с таблицей для подмены имени (mInventSumDateTransTmp - таблица)
X++:
    mInventSumDateTransTmp      transTmp = trecord::init(_tableId, false, true);
    mInventSumDateDim           dim;
    mInventSumDateTrans         trans;

    #mInventSumDateDimDevelop

    insert_recordset transTmp (TransDate, ItemId, FinancialDimId)
        select maxof(TransDate), ItemId, FinancialDimId from trans
            group by ItemId, FinancialDimId
            where trans.TransDate <= transDate
               && (!inventSumDateDimParmCriteria.ItemIdFlag || (trans.ItemId == inventSumDateDimCriteria.ItemId))
            join dim
                where dim.FinancialDimId == trans.FinancialDimId
                   && (!inventSumDateDimParmCriteria.InventSiteIdFlag          || (dim.InventSiteId          == inventSumDateDimCriteria.InventSiteId         ))
                   && (!inventSumDateDimParmCriteria.InventLocationIdFlag      || (dim.InventLocationId      == inventSumDateDimCriteria.InventLocationId     ))
                   && (!inventSumDateDimParmCriteria.InventProfileIdFlag       || (dim.InventProfileId       == inventSumDateDimCriteria.InventProfileId      ))
                   && (!inventSumDateDimParmCriteria.InventProfileItemTypeFlag || (dim.InventProfileItemType == inventSumDateDimCriteria.InventProfileItemType))
                   && (!inventSumDateDimParmCriteria.AccountFlag               || (dim.Account               == inventSumDateDimCriteria.Account              ))
                   && (!inventSumDateDimParmCriteria.Account2Flag              || (dim.Account2              == inventSumDateDimCriteria.Account2             ))
                   && (!inventSumDateDimParmCriteria.AccountOffsetFlag         || (dim.AccountOffset         == inventSumDateDimCriteria.AccountOffset        ))
                   && (!inventSumDateDimParmCriteria.AccountOffset2Flag        || (dim.AccountOffset2        == inventSumDateDimCriteria.AccountOffset2       ))
                   && (!inventSumDateDimParmCriteria.StornoFlag                || (dim.Storno                == inventSumDateDimCriteria.Storno               ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[1]          || (dim.Dimension[1]          == inventSumDateDimCriteria.Dimension[1]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[2]          || (dim.Dimension[2]          == inventSumDateDimCriteria.Dimension[2]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[3]          || (dim.Dimension[3]          == inventSumDateDimCriteria.Dimension[3]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[4]          || (dim.Dimension[4]          == inventSumDateDimCriteria.Dimension[4]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[5]          || (dim.Dimension[5]          == inventSumDateDimCriteria.Dimension[5]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[6]          || (dim.Dimension[6]          == inventSumDateDimCriteria.Dimension[6]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[7]          || (dim.Dimension[7]          == inventSumDateDimCriteria.Dimension[7]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[8]          || (dim.Dimension[8]          == inventSumDateDimCriteria.Dimension[8]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[9]          || (dim.Dimension[9]          == inventSumDateDimCriteria.Dimension[9]         ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[10]         || (dim.Dimension[10]         == inventSumDateDimCriteria.Dimension[10]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[11]         || (dim.Dimension[11]         == inventSumDateDimCriteria.Dimension[11]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[12]         || (dim.Dimension[12]         == inventSumDateDimCriteria.Dimension[12]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[13]         || (dim.Dimension[13]         == inventSumDateDimCriteria.Dimension[13]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[14]         || (dim.Dimension[14]         == inventSumDateDimCriteria.Dimension[14]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[15]         || (dim.Dimension[15]         == inventSumDateDimCriteria.Dimension[15]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[16]         || (dim.Dimension[16]         == inventSumDateDimCriteria.Dimension[16]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[17]         || (dim.Dimension[17]         == inventSumDateDimCriteria.Dimension[17]        ))
                   && (!inventSumDateDimParmCriteria.DimensionFlag[18]         || (dim.Dimension[18]         == inventSumDateDimCriteria.Dimension[18]        ));
}
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta и LedgerBalancesTransDelta. Результат - ПОЛНОЕ отсутствие блокировок при обновлении запасов в наличии и балансов по ГК. Полное означает не облегчение, а устранение проблемы на корню и возможность многопоточной разноски документов в тысячи реально параллельных не ждущих друг друга потоков. И такие странные методы стоили достигнутого результата
За это сообщение автора поблагодарили: raz (20), Logger (20), Ace of Database (19).
Старый 19.01.2019, 17:12   #2  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,914 / 5737 (197) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от db Посмотреть сообщение
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta и LedgerBalancesTransDelta. Результат - ПОЛНОЕ отсутствие блокировок при обновлении запасов в наличии и балансов по ГК. Полное означает не облегчение, а устранение проблемы на корню и возможность многопоточной разноски документов в тысячи реально параллельных не ждущих друг друга потоков. И такие странные методы стоили достигнутого результата
Хотя это некоторый офтопик в этой теме, но проблема блокировок и дидлоков по LedgerBalancesTransDelta лечится простым удалением индекса по RecId. Просто ставишь в свойствах таблицы createRecIdIdx==No и проблема решается.
За это сообщение автора поблагодарили: Logger (3).
Старый 20.01.2019, 10:29   #3  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от fed Посмотреть сообщение
Просто ставишь в свойствах таблицы createRecIdIdx==No и проблема решается.
А есть какое-либо техническое объяснение такому поведению? На первый взгляд, не совсем понятное поведение.
Старый 20.01.2019, 14:08   #4  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,914 / 5737 (197) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
А есть какое-либо техническое объяснение такому поведению? На первый взгляд, не совсем понятное поведение.
В LedgerBalancesTransDelta есть нормальный кластерный индекс, в котором в самом начале стоит SessionId. Соответственно, если этот индекс используется, то конфликтов между сессиями не возникает, поскольку все поиски идут с указанием сессии, и, соответственно, SQL Server вешает блокировки на разные ключи и страницы. (Вероятно может все-таки блокировка случаться, если несколько разных сессий в одну страницу попало, но это не очень часто случается). Проблема в том, что авторы фичи добавили еще и индекс по RecId (который реально использоваться не должен). Почему добавили - не знаю. Может машинально, может долбоархитекторы заставили. Проблема в том, что табличка эта - часто обновляется и основной кластерный индекс становится очень фрагментированым. В этой ситуации, SQL Server время от времени решает что надо использовать индекс по RecIc, но только для того, чтобы отобрать записи по условию dataaareaid. В итоге - вешаются блокировки на заметную часть этого самого индекса, поскольку записи с одинаковым SessionId могут там попасть в совсем разные страницы.
А когда мы индекс по RecId удаляем, системе не остается ничего другого кроме как использовать единственный кластерный индекс. А при этом блокировки случаются эдак на 2-3 порядка реже чем при использовании индекса по RecId.
Ну то есть - решение Романа, вероятно, еще более универсально, поскольку вероятность блокировок доводит до нуля, но отрубить индекс по RecId не долго, а результат дает почти такой же, как и использование временной таблицы....

Последний раз редактировалось fed; 20.01.2019 в 14:24.
За это сообщение автора поблагодарили: Logger (3), Raven Melancholic (5).
Старый 20.01.2019, 16:07   #5  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Насколько понимаю, такое поведение из-за того, что в этой таблице постоянные добавления/удаления и она небольшая и поэтому сложно поддерживать правильную статистику чтобы SQL мог определить нормальный план запроса даже если за базой "следят".
Получается, что в таблицах, в которых очень часто меняются данные, влияющие на индексы, и записей немного, нужно свести индексы к тому, чтобы остались только те, что нужны для конкретных поисков. Не исключено, что в конкретном месте нужна подсказка какой индекс использовать, иначе получается ошибка оптимизации?
Старый 20.01.2019, 16:50   #6  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,914 / 5737 (197) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
Насколько понимаю, такое поведение из-за того, что в этой таблице постоянные добавления/удаления и она небольшая и поэтому сложно поддерживать правильную статистику чтобы SQL мог определить нормальный план запроса даже если за базой "следят".
Получается, что в таблицах, в которых очень часто меняются данные, влияющие на индексы, и записей немного, нужно свести индексы к тому, чтобы остались только те, что нужны для конкретных поисков. Не исключено, что в конкретном месте нужна подсказка какой индекс использовать, иначе получается ошибка оптимизации?
Сформулируем это так: неудачный индекс не только увеличивает время обновления, но и вызывает повышенный риск блокировок. Просто на некоторых стадиях поиска по индексу, SQL блокирует не только те записи, которые он реально обновил, но и просто те записи, которые он собирается прочитать. Если индекс построен таким образом, что при обновлении, поиск по индексу не очень селективный, вероятность дидлоков и долгих блокировок изрядно возрастает.
Старый 20.01.2019, 18:22   #7  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от fed Посмотреть сообщение
Сформулируем это так: неудачный индекс не только увеличивает время обновления, но и вызывает повышенный риск блокировок.
Конечно, воскресенье не показатель, но сейчас у меня в базе в таблице LedgerBalancesTransDelta всего меньше десятка записей. На мой взгляд, это для SQL настолько ничто, то нет разницы что там оптимизируется.
С другой стороны, размер места для таблицы несколько десятков мегабайт, то есть еженочная оптимизация и перестройка индексов не затронула кластерный индекс этой таблицы (при его перестройке место освобождается). Стоит задуматься над тем, что там у нас ночью происходит или над тем, что в течение дня было, что так увеличился размер, если ночью он был высвобожден. Не исключено, что именно перевод в TempDB поможет.
Старый 20.01.2019, 17:37   #8  
Ace of Database is offline
Ace of Database
Участник
Аватар для Ace of Database
 
877 / 649 (23) +++++++
Регистрация: 14.10.2004
Цитата:
Сообщение от db Посмотреть сообщение
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta и LedgerBalancesTransDelta. Результат - ПОЛНОЕ отсутствие блокировок при обновлении запасов в наличии и балансов по ГК. Полное означает не облегчение, а устранение проблемы на корню и возможность многопоточной разноски документов в тысячи реально параллельных не ждущих друг друга потоков. И такие странные методы стоили достигнутого результата
Ура, значит теперь АХ2012 будет работать так же быстро как АХ3.0! Надо попробовать
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/

Последний раз редактировалось Ace of Database; 20.01.2019 в 18:03.
Старый 20.01.2019, 18:37   #9  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от db Посмотреть сообщение
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta
Не секрет, что в InventOnHand* в стандарте по каким-то причинам не учитывается InventSumDelta, приходится дописывать.
Если перевести InventSumDelta в TempDB, то обращение в отдельном классе будет обращаться к той же темповой таблице, что и в другом месте или нужны какие-то дополнительные действия?
В DAX2012, чтобы в разных местах были обращения к одной и той же темповой таблице из TempDB приходится немного помашанить иначе разные курсоры создают разные #что-то_в_TempDB.
Старый 21.01.2019, 10:09   #10  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от db Посмотреть сообщение
Как обращаться к такой таблице в коде? Первый вариант уже сказали - табличный мап и инициализация через MakeRecord. Второй вариант - дублировать таблицу чтобы FieildId были такие как в таблице с решетом и то-же инициализация через MakeRecord.
Это понятно, когда уже есть каким-то образом полученный TableId. Насколько я понял, TasmanianDevil больше интересуется не тем, как делать выборку, когда уже имеем запись, а то, что происходит в:
Код:
mInventSumDeltaKeyMap::tmpTableInit();
Код:
trecord::init(this.getBalanceDeltaTableId());
В первую очередь getBalanceDeltaTableId
То есть, существует ли вариант получения TableId таблицы таким образом, чтобы сохранялись перекрестные ссылки, чтобы про изменения например наименования сообщал компилятор при компиляции перед релизом, а не разъяренный пользователь после релиза.
А мне интересен технический момент: если я в InventUpd_* буду использовать эти временные InventSumDelta, то смогу ли обратиться именно к этому конкретному экземпляру временной таблицы в InventOnHand?
Ясно, что если этот подход помогает, то с чем-то можно смириться.
Старый 23.01.2019, 09:42   #11  
db is offline
db
Роман Долгополов (RDOL)
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
 
393 / 692 (24) +++++++
Регистрация: 01.04.2004
Адрес: Москва
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
А мне интересен технический момент: если я в InventUpd_* буду использовать эти временные InventSumDelta, то смогу ли обратиться именно к этому конкретному экземпляру временной таблицы в InventOnHand?
Ясно, что если этот подход помогает, то с чем-то можно смириться.
Экземпляры темповой таблицы с решетом существуют в разрезе сессий SQL сервера. AOS может произвольно брать такие сессии из своего пула коннектов, но если идет транзакция, то (что логично) переключения сессий SQL на ходу для одной сессии аксапты не происходит. Дельты имеют смысл только внутри транзакций, так что ответ да, обратиться сможешь. Возможно для исключения каких либо экстремальных случаев использования функциональности дельт стоит предусмотреть проверку на appl.ttsLevel() перед обращениям к таким таблицам. У себя этого не делали, но код на предмет что всё в транзакциях, просматривал. Возможно что то и обернул в транзакции, уже не вспомню. Полет нормальный около 5 лет.
Старый 23.01.2019, 10:34   #12  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от db Посмотреть сообщение
Возможно для исключения каких либо экстремальных случаев использования функциональности дельт стоит предусмотреть проверку на appl.ttsLevel() перед обращениям к таким таблицам.
Тут даже проще можно, есть метод для проверки того, нужно ли использовать дельты для номенклатуры:
X++:
InventUpdateOnhandGlobal::mustAddInventSumDeltaOnhand(itemId)
Спасибо за информацию, поэкспериментирую на кошках.
Теги
dispose, inventsumdelta, ledgerbalancestransdelta, tempdb

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
emeadaxsupport: AX Performance - Analyzing key SQL Server configuration and database settings Blog bot DAX Blogs 0 28.09.2015 14:11
Какое оптимальное сочетание версий SQL и AX2009 ? AXcons DAX: Администрирование 14 02.09.2015 11:27
emeadaxsupport: AX Performance Troubleshooting Checklist Part 1A [Introduction and SQL Configuration] Blog bot DAX Blogs 0 05.09.2014 21:11
zakharov: Внедряем AX2009. Поиск "тяжелых" запросов используя Microsoft SQL Server Activity Monitor Blog bot DAX Blogs 5 22.08.2013 11:18
Помогите с выбором версии SQL Server для Ax2009 Predator DAX: Администрирование 9 02.02.2010 21:38

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 05:19.