16.01.2019, 19:18 | #1 |
Участник
|
Временные SQL таблички в ax2009
Всем привет.
Коллеги, доводилось слышать, что в 2009-й аксапте можно в AOT объявить табличку название которой начинается с # Тогда SQL Server при синхронизации будет создавать времянку в SQL и этим можно успешно пользоваться. Попробовал руками в AOT ее создать - не дает. Импортом из xpo - тоже. Если кто так делал, поделитесь как это можно сделать. |
|
|
За это сообщение автора поблагодарили: S.Kuskov (2). |
16.01.2019, 20:28 | #2 |
Роман Долгополов (RDOL)
|
Делал так - создавал обычную таблицу и переименовывал джобом
X++: static void rdol_createSQLTmpTable(Args _args) { TreeNode tn= TreeNode::findNode("\\Data Dictionary\\Tables\\mInventSumDeltaKeyTmp"); ; tn.AOTsetProperties('PROPERTIES\nName\n#\#mInventSumDeltaKeyTmp\nENDPROPERTIES'); tn.AOTsave(); } Последний раз редактировалось db; 16.01.2019 в 20:33. |
|
|
За это сообщение автора поблагодарили: mazzy (10), AlGol (3), Logger (10), alex55 (3), S.Kuskov (10), skuull (5). |
17.01.2019, 06:28 | #3 |
Мрачный тип
|
Ругается при попытке синхронизации из AOT таблицы с измененным таким образом именем.
У меня SQL сервер 10.0.6556
__________________
Мы летаем, кружимся, нагоняем ужасы ... Последний раз редактировалось TasmanianDevil; 17.01.2019 в 07:05. |
|
17.01.2019, 07:14 | #4 |
Участник
|
Сделайте ее временной inMemory. Или коныигурауионным ключом отключите. Тогда при синзронизации она удалится из базы.
Затем верните обратно. При синхронизации она создастся. Но уже как sql времянка. |
|
|
За это сообщение автора поблагодарили: TasmanianDevil (5). |
17.01.2019, 09:56 | #5 |
Мрачный тип
|
Вопрос № 2 - я таки дико извиняюсь, но как к ней после всех этих хакерских игрищ обращаться из кода ? Компилятор теперь считает такое имя как макрос, который отсутсвует в AOT
__________________
Мы летаем, кружимся, нагоняем ужасы ... |
|
17.01.2019, 13:17 | #6 |
Участник
|
Попробуйте через QueryRun или через DictTable(tableId).makeRecord Получить значение поля по TableId, FieldId, RecId
|
|
18.01.2019, 07:11 | #7 |
Мрачный тип
|
S.Kuskov, спасибо, но мне не проблема через Dict-классы играться с данными произвольных таблиц и пользоваться произвольными вызовами произвольных методов таблиц/объектов
Проблем для данного случая две - первая состоит в кодах таблиц/полей, которые мы в таких действиях используем и которые могут отличаться в разработческом и рабочем приложениях. Используемые ранее конструкции в виде X++: tablenum(MyTempTable) X++: fieldnum(MyTempTable, MyField) Хардкодить коды таблиц/полей- не вариант. Единственный способ замены tablenum()/fieldnum() вижу только в поиске в AOT по имени соответствующих узлов и сбор оттуда ID-шников. Вторая проблема - ухудшение читабельность кода и затруднение отладки при работе с данными через Dict-классы и Common. Не спорю, работа с данными через Dict-классы и Common - вещь местами хорошая, сам зачастую пользую ее с удовольствием, т.к. позволяет в некоторых случаях радикально оптимизировать объемы кода в случаях, когда это используется при создании каких-либо широко используемых фреймворков. Положительный эффект от таких разработок с лихвой покрывает эту проблему Но в данном случае - не совсем в коня овес. Времянка, как правило, лепится под определенную задачу, и усложнять код в ней через Common и коды полей особого смысла не вижу - разве что объем накапливаемых во времянке данных дает реальную просадку быстродействия и только перевод на TempDB дает кратное ускорение
__________________
Мы летаем, кружимся, нагоняем ужасы ... Последний раз редактировалось TasmanianDevil; 18.01.2019 в 07:14. |
|
18.01.2019, 07:26 | #8 |
Участник
|
Может попробовать сделать табличный мап без # в именах и работать через него.
Тогда можно обойтись без common. Только new dictTable(...).makeRecord при инициализации. |
|
18.01.2019, 11:03 | #9 |
Участник
|
Цитата:
Цитата:
fieldnum(MyTempTable, MyField) - не самая удобная конструкция Ром, опубликуй, наконец. Ты сделал реально круто! Заодно и отрефакторишь |
|
18.01.2019, 13:28 | #10 |
Мрачный тип
|
А что еще у нас есть для решения задачи поиска ID таблицы и ее полей по известным имени и типу искомого объекта ? Таблица UtilIdElements? Можно и ее ...
Только вот в обоих случаях для поиска ID придется передавать строчные константы наименования объектов и ошибки в них придется ловить только в run-time. Не кошерно как-то это все по сравнению с халяльными tablenum() /fieldnum(), которых мы лишаемся из-за использования в названии таблицы символа #, и в которых компилятор сразу нас мордой тычет в ошибки, если что не так. Предложенная Logger'ом идея работы через Map в AOT как-то ближе
__________________
Мы летаем, кружимся, нагоняем ужасы ... |
|
18.01.2019, 15:15 | #11 |
Участник
|
Цитата:
Цитата:
fieldstr если же речь идет о таблицах с первым символом # то можно использовать таблицу SqlDictionary или Dictionary.TableCnt2Id() treeNode - вполне себе способ. но это НЕ единственный способ. а для работы с DictTable, вообще говоря, достаточно ID таблицы. |
|
18.01.2019, 17:28 | #12 |
Роман Долгополов (RDOL)
|
Как обращаться к такой таблице в коде? Первый вариант уже сказали - табличный мап и инициализация через 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(); } 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; } 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] )); } |
|
|
За это сообщение автора поблагодарили: raz (20), Logger (20), Ace of Database (19). |
19.01.2019, 17:12 | #13 |
Moderator
|
Цитата:
Сообщение от db
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta и LedgerBalancesTransDelta. Результат - ПОЛНОЕ отсутствие блокировок при обновлении запасов в наличии и балансов по ГК. Полное означает не облегчение, а устранение проблемы на корню и возможность многопоточной разноски документов в тысячи реально параллельных не ждущих друг друга потоков. И такие странные методы стоили достигнутого результата
|
|
|
За это сообщение автора поблагодарили: Logger (3). |
20.01.2019, 10:29 | #14 |
Участник
|
|
|
20.01.2019, 14:08 | #15 |
Moderator
|
Цитата:
А когда мы индекс по RecId удаляем, системе не остается ничего другого кроме как использовать единственный кластерный индекс. А при этом блокировки случаются эдак на 2-3 порядка реже чем при использовании индекса по RecId. Ну то есть - решение Романа, вероятно, еще более универсально, поскольку вероятность блокировок доводит до нуля, но отрубить индекс по RecId не долго, а результат дает почти такой же, как и использование временной таблицы.... Последний раз редактировалось fed; 20.01.2019 в 14:24. |
|
|
За это сообщение автора поблагодарили: Logger (3), Raven Melancholic (5). |
20.01.2019, 16:07 | #16 |
Участник
|
Насколько понимаю, такое поведение из-за того, что в этой таблице постоянные добавления/удаления и она небольшая и поэтому сложно поддерживать правильную статистику чтобы SQL мог определить нормальный план запроса даже если за базой "следят".
Получается, что в таблицах, в которых очень часто меняются данные, влияющие на индексы, и записей немного, нужно свести индексы к тому, чтобы остались только те, что нужны для конкретных поисков. Не исключено, что в конкретном месте нужна подсказка какой индекс использовать, иначе получается ошибка оптимизации? |
|
20.01.2019, 16:50 | #17 |
Moderator
|
Цитата:
Сообщение от Raven Melancholic
Насколько понимаю, такое поведение из-за того, что в этой таблице постоянные добавления/удаления и она небольшая и поэтому сложно поддерживать правильную статистику чтобы SQL мог определить нормальный план запроса даже если за базой "следят".
Получается, что в таблицах, в которых очень часто меняются данные, влияющие на индексы, и записей немного, нужно свести индексы к тому, чтобы остались только те, что нужны для конкретных поисков. Не исключено, что в конкретном месте нужна подсказка какой индекс использовать, иначе получается ошибка оптимизации? |
|
20.01.2019, 17:37 | #18 |
Участник
|
Цитата:
Сообщение от db
Не в коня корм от задачи зависит как обычно. В моем случае темповыми стали InventSumDelta и LedgerBalancesTransDelta. Результат - ПОЛНОЕ отсутствие блокировок при обновлении запасов в наличии и балансов по ГК. Полное означает не облегчение, а устранение проблемы на корню и возможность многопоточной разноски документов в тысячи реально параллельных не ждущих друг друга потоков. И такие странные методы стоили достигнутого результата
__________________
Мои утилиты для Аксапты версий 3.0-2012: http://aceofdatabase.blogspot.com/ Последний раз редактировалось Ace of Database; 20.01.2019 в 18:03. |
|
20.01.2019, 18:22 | #19 |
Участник
|
Цитата:
С другой стороны, размер места для таблицы несколько десятков мегабайт, то есть еженочная оптимизация и перестройка индексов не затронула кластерный индекс этой таблицы (при его перестройке место освобождается). Стоит задуматься над тем, что там у нас ночью происходит или над тем, что в течение дня было, что так увеличился размер, если ночью он был высвобожден. Не исключено, что именно перевод в TempDB поможет. |
|
20.01.2019, 18:37 | #20 |
Участник
|
Цитата:
Если перевести InventSumDelta в TempDB, то обращение в отдельном классе будет обращаться к той же темповой таблице, что и в другом месте или нужны какие-то дополнительные действия? В DAX2012, чтобы в разных местах были обращения к одной и той же темповой таблице из TempDB приходится немного помашанить иначе разные курсоры создают разные #что-то_в_TempDB. |
|
Теги |
dispose, inventsumdelta, ledgerbalancestransdelta, tempdb |
|
|