28.09.2010, 17:22 | #61 |
Участник
|
Пересчет можно запустить из формы периодов по конкретному периоду
__________________
Ivanhoe as is.. |
|
|
За это сообщение автора поблагодарили: glibs (1). |
28.09.2010, 17:39 | #62 |
Moderator
|
Ладно - уговорили - пусть живут эти ваши балансы.
Хотя все равно не считаю задачу мгновенного получения баланса по счету ГК первостепенной для ERP-системы. |
|
28.09.2010, 17:51 | #63 |
Member
|
Цитата:
Одно индексирование LedgerBalancesDimTrans способствует более быстрому получению оборотов (сальдо = оборот с начала периода) за период по счету с фильтрацией по аналитикам или без такового. Одно дело сканировать фрагменты кластерного индекса, и совсем другое лукапить хренову тучу проводок по индексу, а то и свалиться в сканирование таблицы (как уж решит оптимизатор в той или иной ситуации). Выборки то огроменные получаются.
__________________
С уважением, glibs® |
|
28.09.2010, 18:16 | #64 |
Moderator
|
Цитата:
Сообщение от glibs
Давайте найдем большую БД и проверим.
Одно индексирование LedgerBalancesDimTrans способствует более быстрому получению оборотов (сальдо = оборот с начала периода) за период по счету с фильтрацией по аналитикам или без такового. Одно дело сканировать фрагменты кластерного индекса, и совсем другое лукапить хренову тучу проводок по индексу, а то и свалиться в сканирование таблицы (как уж решит оптимизатор в той или иной ситуации). Выборки то огроменные получаются. |
|
28.09.2010, 19:04 | #65 |
Участник
|
Посмотрел две живые БД. За базу берется значение для таблицы проводок ГК, ниже приведено отношение значения для соответствующей таблицы к базе. Данные взяты стандартным отчетом "Размер компании".
БД1 кол-во записей: Проводки = 1,000 Сальдо ГК = 0,012 Сальдо ГК + аналит = 0,066 БД1 объем таблицы: Проводки = 1,000 Сальдо ГК = 0,020 Сальдо ГК + аналит = 0,129 БД2 кол-во записей: Проводки = 1,000 Сальдо ГК = 0,016 Сальдо ГК + аналит = 0,226 БД2 объем таблицы: Проводки = 1,000 Сальдо ГК = 0,282 Сальдо ГК + аналит = 5,038
__________________
Ivanhoe as is.. |
|
|
За это сообщение автора поблагодарили: Logger (3). |
28.09.2010, 20:06 | #66 |
Member
|
Цитата:
Сообщение от fed
...
Ну насчет кластерных индексов - как раз сомнительно. ... Цитата:
Сообщение от fed
...
Если у тебя стандартный индекс accountNum+dimension, а я ищу выборку по AccountNum+dimension[2], то индекс будет использоваться только для отбора всех проводок по счету. Фильтрация по аналитике будет делаться уже за счет данных, а не за счет индекса. ... А вот если бы индекс был некластерный, то записи будут лукапиться последовательно (сначала будет сканироваться кусками индекс, а потом будут лукапиться уже данные). В случае с кластерным индексом каждая страница с данными гарантировано считается только один раз (оптимально). А в случае с лукапом по индексу одна и та же страница может считаться много раз (зависит от ресурсов памяти, которыми располагает сервер БД и насколько много сессий он обслуживает и какую нагрузку они ему обеспечивают). Если весь LedgerTrans поместится в кеше, то запрос может и быстро отработать, но если пользователи-конкуренты его в момент запроса сильно нагрузят, то памяти на кеш на всех может не хватить, и будет прилично ввода-вывода. Индекс некластерный эффективен при выборке одной или нескольких записей из большой таблицы. Цитата:
Сообщение от fed
...
И если условие по dimension[2] высокоселективно, то надо по нему индекс строить (типа transDate+accountNum+dimension[2]) (независимо от того - ищем мы по ledgerTrans или ledgerBalancesDimTrans). Может заметно быстрее получиться - несмотря на лукап ... Если бы индекс на LedgerBalancesdimTrans был не кластерным, то на выборе из огромной таблицы нескольких записей он бы дал большую скорость. Но при выборе очень большого количества записей большую скорость даст кластерный индекс. Цитата:
Сообщение от fed
...
Кстати - длинный ключ кластерного индекса по ledgerBalances тут может негативную роль сыграть ... Блокировки? Скорость разноски на OLTP операциях? Разумеется. OLTP оно такое. Должно учитывать интересы аналитических задач. Но при грамотной архитектуре БД тормоза OLTP пользователь не ощутит. При этом будет учтен интерес аналитических пользователей.
__________________
С уважением, glibs® Последний раз редактировалось glibs; 28.09.2010 в 20:20. |
|
|
За это сообщение автора поблагодарили: Logger (1). |
28.09.2010, 20:29 | #67 |
Moderator
|
Цитата:
Сообщение от glibs
А вот если бы индекс был некластерный, то записи будут лукапиться последовательно (сначала будет сканироваться кусками индекс, а потом будут лукапиться уже данные). В случае с кластерным индексом каждая страница с данными гарантировано считается только один раз (оптимально). А в случае с лукапом по индексу одна и та же страница может считаться много раз (зависит от ресурсов памяти, которыми располагает сервер БД и насколько много сессий он обслуживает и какую нагрузку они ему обеспечивают). Если весь LedgerTrans поместится в кеше, то запрос может и быстро отработать, но если пользователи-конкуренты его в момент запроса сильно нагрузят, то памяти на кеш на всех может не хватить, и будет прилично ввода-вывода.
[/QUOTE] Цитата:
. Ну если у тебя по таблице есть кластерный индекс, то все остальные индексы содержат не rowId, а значение ключа кластерного индекса. Поскольку ключ в стандартном ledgerBalancesDimTrans длинный, то размер индексной запси (ключ некластерного индекса+ключ для поиска по кластерному индексу) будет заметно больше. Длинее индексная запись - меньше индексных записей в странице. Меньше индексных записей в странице - выше дерево. Выше дерево - больше операций чтения. Кроме того - если я отбираю в некластерном индексе те же 60% записей, то опять таки может сложится ситуация при котором эти 60% записей раскиданы по всем страницам кластерного индекса. И все это выльется в итоге в фулл-скан. Так что для того чтобы кластерный индекс был действительно эффективным, надо чтобы большая часть поисков (ну то есть - эдак процентов 70 хотя бы) выполнялось всегда по одному и тому же ключу. Это хорошо выполняется для custTable, например а вот для LedgerBalanceDimTrans - не выполняется, поскольку фильтрация всегда идет по сочетанию Счет+некий набор аналитик. Просто я тут наблюдал как работает некоторое специализированное распределение счетов, в которой автор как раз использует ledgerBalancesDimTrans, и для разных видов затрат задает разные фильтры по аналитикам. Так вот - индекс использовался только для фильтрации по номеру счета+дата. |
|
29.09.2010, 00:51 | #68 |
Member
|
Цитата:
Сообщение от fed
...
Не совсем так. Цитата:
Сообщение от fed
...
MS SQL прочитает из страниц некластерного индекса rowId страниц с данными (то есть - сочетание номер файла:номер страницы:смешение в странице), отсортирует по файлам и страницам и начнет читать. Каждая страница будет прочитана один раз. Цитата:
Сообщение от fed
...
Ну если у тебя по таблице есть кластерный индекс, то все остальные индексы содержат не rowId, а значение ключа кластерного индекса. Поскольку ключ в стандартном ledgerBalancesDimTrans длинный, то размер индексной запси (ключ некластерного индекса+ключ для поиска по кластерному индексу) будет заметно больше. Длинее индексная запись - меньше индексных записей в странице. Меньше индексных записей в странице - выше дерево. Выше дерево - больше операций чтения. Цитата:
Сообщение от fed
...
Кроме того - если я отбираю в некластерном индексе те же 60% записей, то опять таки может сложится ситуация при котором эти 60% записей раскиданы по всем страницам кластерного индекса. И все это выльется в итоге в фулл-скан. ... Разумеется, если в условии запроса не будет критериев по первым полям кластерного индекса, то будет full scan. О том что кластерный индекс исключает full scan речи не было и быть не могло. Цитата:
Сообщение от fed
...
Так что для того чтобы кластерный индекс был действительно эффективным, надо чтобы большая часть поисков (ну то есть - эдак процентов 70 хотя бы) выполнялось всегда по одному и тому же ключу. ... Цитата:
Сообщение от fed
...
а вот для LedgerBalanceDimTrans - не выполняется, поскольку фильтрация всегда идет по сочетанию Счет+некий набор аналитик. ... Цитата:
Сообщение от fed
...
Так вот - индекс использовался только для фильтрации по номеру счета+дата. ... А проектировать систему под full scan...
__________________
С уважением, glibs® |
|
29.09.2010, 01:04 | #69 |
Member
|
Кстати про LedgerBalancesDimTrans.
Есть еще очень экстремальная, но смертельно убойная вещь. Если, например, окажется так, что в компании не используются слои управленческого и налогового учета, то можно попробовать создать индекс по аналитикам, счету, дате, дебетовой и кредитовой сумме, компании и периоду. Как вариант не все аналитики включать. Вариант в общем случае плохой, но скорость поднять может зверски просто. В эксклюзивных случаях я таким балуюсь. Жаль, кстати, что управленческий и налоговый учет не отключаются параметрически/конфигурационно. Это к вопросу о размере записи в таблице. Ну и стоит дописать к слову что при отключении вторичной валюты размер записи LedgerBalancesDimTrans укоротится ощутимо.
__________________
С уважением, glibs® Последний раз редактировалось glibs; 29.09.2010 в 01:07. |
|
29.09.2010, 01:33 | #70 |
Участник
|
отмотал назад по версиям... пока до 2.5...
нашел конкорд, но не помню пароля. посмотрел что было раньше. Цитата:
Сообщение от mazzy
Не, Денис, ты ошибаешься.
Здесь тебе стоит пересмотреть свои взгляды. Просто у денежных полей в таблицах LedgerBalancesTrans и LedgerBalancesDimTrans установлено свойство FieldUpdate = Relative. http://msdn.microsoft.com/en-us/library/aa577032.aspx Это значит, что программисту не надо писать Update, не нужно маятся с вилкой (find?update:insert). Программисту достаточно давать команду Insert. Система сама сложит, если найдет уже существующую запись. Это свойство пришло еще из Конкорда. Цитата:
Сообщение от AlexSD
Попробывал - не получилось.
Создал в ax2009 таблицу с двумя полями: текстовое Id, числовое Amount с FieldUpdate = Relative. Уникальный индекс по полю Id. Джобом пытаюсь добавлять, обновлять запись разными способами - никак не получается воспроизвести ситуацию, что бы поле Amount обновлялось с учетом предыдущего значения. Каждый раз значение поля переписывается. Может, я что-то не так делаю? в 2.5, 3.0, 4.0, 2009 пока считаю, что это я что-то серьезно напутал. мало того, в ax2.5 суммовые поля в таблицах LedgerBalances не являются Relative! оказывается механизм переписывался. и существенно. надо будет покопаться потщательнее. пусть пока перекрестные ссылки построятся. |
|
|
За это сообщение автора поблагодарили: Logger (1). |
29.09.2010, 08:25 | #71 |
Участник
|
Сергей, для первого скриншота write() - это просто замена проверки существования записи.
Т.е. вместо X++: select firstonly forupdate ledgerBalances where ledgerBalances.accountNum == accountNum && ledgerBalances.transDate == transDate && ledgerBalances.periodCode == periodCode; if (ledgerBalances) { ... ledgerBalances.update(); } else { ... ledgerBalances.insert(); } Обрати внимание, что всегда записываются значения в X++: ledgerBalances.accountNum = accountNum; ledgerBalances.transDate = transDate; ledgerBalances.periodCode = periodCode; X++: update ledgerBalancesTrans
set debitMST = debitMST + delta
where ... X++: update ledgerBalancesTrans
set debitMST = value
where ...
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: belugin (3). |
29.09.2010, 09:59 | #72 |
Участник
|
Цитата:
2. Спасибо 3. Я просто хотел сказать, что раньше был не только insert |
|
29.09.2010, 10:09 | #73 |
Участник
|
Цитата:
Сообщение от AndyD
А Relative просто при обновлении записи делает
X++: update ledgerBalancesTrans
set debitMST = debitMST + delta
where ... X++: update ledgerBalancesTrans
set debitMST = value
where ... только я всегда думал, что дельтой является просто ledgerBalances.debitMST Попробуй Job сделать. У меня сходу не получилось. Тупо записывается последнее значение. Видимо что-то неправильно делал. |
|
29.09.2010, 10:30 | #74 |
Участник
|
Вот такой код
X++: Table3 table3; ; ttsbegin; select forupdate table3; table3.Field1 = table3.Field1+10; table3.update(); ttscommit; X++: UPDATE TABLE3 SET FIELD1=(FIELD1+@P1),RECVERSION=((((cast(2147483647 as bigint) + RECVERSION+@P2 - 1)%2147483647) + 1)) WHERE ((DATAAREAID=@P3) AND (RECID=@P4)) а с Absolute такой X++: UPDATE TABLE3 SET FIELD1=@P1,RECVERSION=@P2 WHERE (((DATAAREAID=@P3) AND (RECID=@P4)) AND (RECVERSION=@P5)) Это на 2009-й Ax
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: Logger (3). |
29.09.2010, 11:07 | #75 |
Участник
|
Кстати, обрати внимание на использование RECVERSION с включенным Relative.
Он не используется в предложении WHERE и не проверяется его изменение с момента последнего чтения записи. Т.е. если между select и update другая транзакция успела изменить данные (и RECVERSION в том числе), то update успешно завершится без генерации исключения конфликта версий. Аксапийная оптимистическая конкуренция (а у меня она включена по умолчанию) для таких полей не используется. Замечу, что и при чтении с select forUpdate при этом не происходит блокировки записи. Для того, что бы накладывалась блокировка, надо использовать select pessimisticLock PS По-моему, от темы блога совсем в сторону отклонились
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
29.09.2010, 12:16 | #76 |
Участник
|
Цитата:
Зато выяснили, что на LedgerBalances* не ставятся блокировки |
|
03.07.2013, 14:16 | #77 |
Участник
|
эксперимент
Здравствуйте.
Решил возобновить тему, чтобы еще раз попытаться разобраться с FieldUpdate = Relative С помощью таких действий X++: static void Job_1(Args _args) { Ltable t_1; Ltable t_2; Ltable t_3;; // point_0 ttsbegin; {// block_1 select forupdate t_1 where t_1.id == 5; {// block_2 select forupdate t_2 where t_2.id == 5; { // block_3 select forupdate t_3 where t_3.id == 5; t_3.Val = 14; t_3.update(); } t_2.Val = 700; t_2.update(); } t_1.Val = 50000; t_1.update(); } ttscommit; } Код: W = V + (-V + U_1) + (-V + U_2) + ... + (-V + U_n), В частности, в самых интересных для практики случаях, которые обсуждались выше, получаем 1. i = 1 : W = U_1 (отличия от FieldUpdate = Absolute не видны). 2. i = 2 : после первого запуска джоба имеем W = U_1 + U_2 - V после второго запуска джоба имеем W = V (любители арифметики могут убедиться, что при других i подобных циклов нет). ---------------------- Надеюсь, кто-нибудь сможет пояснить, для чего заложена именно такая формула для Relative update, поскольку из предыдущего обсуждения мне показалось, что люди ожидали работы по формуле W = V + U_1 + U_2 + ... + U_n. В качестве офтопа еще хочу спросить, почему не возникает deadlock при выполнении block_2 после block_1.
__________________
Axapta 3.0 SP 4 Последний раз редактировалось bodeaux; 03.07.2013 в 14:53. |
|
03.07.2013, 15:22 | #78 |
Участник
|
Цитата:
Сообщение от bodeaux
удалось установить, что в подобных случаях в таблице остается значение W = V + (-V + U_1) + (-V + U_2) + ... + (-V + U_n), где V - значение в точке point_0, U_i - значение, переданное в update номер i.
Надеюсь, кто-нибудь сможет пояснить, для чего заложена именно такая формула для Relative update, поскольку из предыдущего обсуждения мне показалось, что люди ожидали работы по формуле W = V + U_1 + U_2 + ... + U_n. X++: select forupdate t_1 where t_1.id == 5; {// block_2 select forupdate t_2 where t_2.id == 5; { // block_3 select forupdate t_3 where t_3.id == 5; t_3.Val += 14; t_3.update(); } t_2.Val += 700; t_2.update(); } t_1.Val += 50000; t_1.update(); Цитата:
Цитата:
...К счастью, среда времени выполнения Microsoft Dynamics AX управляет данной ситуацией. При исполнении оператора обновления среда времени выполнения находит все другие буфера записей, содержащие ту же запись, и если при этом они были извлечены с ключевым словом forupdate, то среда времени выполнения изменяет значение поля RecVersion данных буферов записей на новое значение из базы данных. Следовательно, второе обновление не вызовет ошибки.
|
|
|
За это сообщение автора поблагодарили: S.Kuskov (5). |
03.07.2013, 17:09 | #79 |
Участник
|
Цитата:
Так что, убедиться будет проблематично
__________________
Axapta v.3.0 sp5 kr2 |
|
Теги |
ledgerbalance, ledgerbalancesdimtrans, ledgerbalancestrans, главная книга, итоги, сальдо, crm2011 |
|
|