|  17.04.2007, 18:01 | #1 | 
| Участник | aEremenko: Update_RecordSet 
			
			Источник: http://blogs.msdn.com/aeremenk/archi...7/2161619.aspx ============== Update_RecordSet относится к многострочным функциям, позволяющим производить обновление либо вставку нескольких записей за одну операцию. Такие операции существенно уменьшают число запросов к базе данных и позволяют улучшить производительность операций. Судя по сообщениям коллег, работающих в центре разработки, в 5.0 (а может и раньше, если успеют) планируется использование объединений (inner и outer) в update_RecordSet. Например, возьмем код: X++: while select * from custCollectionLetterTrans where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum && custCollectionLetterTrans.AccountNum == this.AccountNum && custCollectionLetterTrans.CollectionLetterIssued { custTrans = CustTrans::find(custCollectionLetterTrans.CustTransId, true); custTrans.CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode; custTrans.update(); } X++: update_recordset custTrans setting CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode join custCollectionLetterTrans where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum && custCollectionLetterTrans.AccountNum == this.AccountNum && custCollectionLetterTrans.CollectionLetterIssued && custTrans.RecId == custCollectionLetterTrans.CustTransId; Источник: http://blogs.msdn.com/aeremenk/archi...7/2161619.aspx 
				__________________ Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. | 
|  | |
| За это сообщение автора поблагодарили: Recoilme (5). | |
|  17.04.2007, 21:01 | #2 | 
| Участник | Цитата: 1. в обрабатываемой таблице нет переопределенных методов insert, update, delete соответственно 2. обрабатываемая таблица не включена в RLS 3. обрабатываемая таблица не содержит deleteAction (для удаления) Да, можно отключить эти факторы при помощи skip-методов. Но отключение переопределенных методов чревато тем, что логическая целостность базы нарушится. Подробнее см хелп, ключевая фраза "What prevents fast SQL operations?" Кроме того, подобные операции может быть и повышают производительность, но гарантировано уменьшают наглядность и юзабилити. Обычно при длительной обработке данных показывают прогресс-бар, который показывает сколько процентов осталось. Групповые операции ничего не показывают. Если групповая операция выполняется несколько минут, то вы не знаете: повисла она, заблокирована или таки выполняется. И кроме того, групповые операции блокируют ВСЕ обрабатываемые записи за один раз. А обработка каждой записи, при правильном написании, блокирует только обрабатываемую запись. См. aEremenko: Ресурс заблокирован, ждите... Ну, и напоследок, групповые операции не позволяют работать с критериями из query. Что напрочь лишает их полезность, если пользователь может задавать критерии со спец-символами http://axapta.mazzy.ru/lib/search/ В общем, групповые операции дело хорошее. Но у них тоже есть минусы. Цитата: 
		
			Сообщение от Blog bot
			   Например, возьмем код:  X++: while select * from custCollectionLetterTrans where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum && custCollectionLetterTrans.AccountNum == this.AccountNum && custCollectionLetterTrans.CollectionLetterIssued { custTrans = CustTrans::find(custCollectionLetterTrans.CustTransId, true); custTrans.CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode; custTrans.update(); }  Более правильный код: X++: SysProgressOperation progress;
;
      select count(recid) from custCollectionLetterTrans 
              where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum 
                 && custCollectionLetterTrans.AccountNum          == this.AccountNum 
                 && custCollectionLetterTrans.CollectionLetterIssued;
      progress = SysProgressOperation::newGeneral('','',custCollectionLetterTrans.recid);
      while select * from custCollectionLetterTrans 
              where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum 
                 && custCollectionLetterTrans.AccountNum          == this.AccountNum 
                 && custCollectionLetterTrans.CollectionLetterIssued 
        { 
              ttsbegin;
             custTrans = CustTrans::find(custCollectionLetterTrans.CustTransId, true); 
              custTrans.CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode; 
              custTrans.update(); 
              ttscommit;
              progress.inccount();
         }X++: SysProgressOperation progress; Query q; QueryBuildDataSource = qbds; QueryRun qr; ; q = new Query(queryStr(mySupercustCollectionLetterTransQuery)); qbds = q.datasourcetable(tablenum(custCollectionLetterTrans)); SysQuery::findOrCreateRange(qbds,fieldnum(custCollectionLetterTrans,CollectionLetterNum ).value(this.CollectionLetterNum); SysQuery::findOrCreateRange(qbds,fieldnum(custCollectionLetterTrans,AccountNum).value(this.AccountNum ); SysQuery::findOrCreateRange(qbds,fieldnum(custCollectionLetterTrans,CollectionLetterIssued).value(SysQuery::notEmptyString()); qr = new QueryRun(q); progress = SysProgressOperation::newGeneral('','',SysQuery::CountTotal(qr)); while( qr.next() ) { custCollectionLetterTrans = qr.getTable(tablenum(custCollectionLetterTrans)); ttsbegin; custTrans = CustTrans::find(custCollectionLetterTrans.CustTransId, true); custTrans.CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode; custTrans.update(); ttscommit; progress.inccount(); } 1. отделить запрос и процесс его оптимизации от программирования. 2. позволяет работать с пользовательскими критериями 3. полностью совместим со всей функциональностью форм и отчетов 4. полностью совместим с пакетной обработкой 5. информирует пользователя о ходе выполнения. Поэтому... Если уж начали двигаться в сторону групповых операций... Если это возможно.... Нужен не update_recordset, а дополнительный режим групповой обработки для Query. | 
|  | |
| За это сообщение автора поблагодарили: Recoilme (-2), kashperuk (3), oip (9). | |
|  18.04.2007, 14:06 | #3 | 
| Участник | Цитата: 
		
			Сообщение от Blog bot
			   Источник: [url]Update_RecordSet относится к многострочным функциям, позволяющим производить обновление либо вставку нескольких записей за одну операцию. В Axapta, класс Common, родитель для классов таблиц предоставляет мощный механизм OBJECT-RELATIONAL MAPPING (ORM), что является ключевой особенностью объектого подхода в моделировании и разработке приложений. Реляционные подходы в объектной системе только испортят наглядность. | 
|  | 
|  18.04.2007, 14:49 | #4 | 
| NavAx | Цитата: 
		
			Сообщение от mazzy
			   Это отвратительный код, за который надо штрафовать программиста и снижать его зарплату. Надеюсь, что этот код упрощен только для демонстрационных целей   Более правильный код: ..... Этот код позволяет: 1. отделить запрос и процесс его оптимизации от программирования. 2. позволяет работать с пользовательскими критериями 3. полностью совместим со всей функциональностью форм и отчетов 4. полностью совместим с пакетной обработкой 5. информирует пользователя о ходе выполнения.  1.) Ваш код замедляет работу из-за множества мелких транзакций. 2.) Визуализация тоже стоит времени - на длительных операциях замечал разницу, если не ошибаюсь, в 30%. 
				__________________ С уважением, Игорь Ласийчук. Последний раз редактировалось Garic; 18.04.2007 в 14:52. | 
|  | |
| За это сообщение автора поблагодарили: Recoilme (5). | |
|  18.04.2007, 15:35 | #5 | 
| Участник | 
			
			В работе операций update_recordset есть некоторые ньюансы которые дают сложноотлавливаемые баги. На одной из версий Аксапты (уже и не помню какой) применение двух последовательных update_recordset давало некорректный результат. Осталось предположение, что отправив запрос на обновление на sqlсервер система продолжила выполнение последующего кода, не дожидаясь полного выполнения запроса. Как результат, следующий update_recordset, использующий результаты предыдущего давал некорректные результаты. Поборолось обрамлением каждого update_recordset в отдельных ttsbegin/ttscommit. Но осадочек остался...   | 
|  | 
|  18.04.2007, 16:05 | #6 | 
| злыдень | 
			
			У каждого подхода есть свои плюсы минусы. Можно 30 минут показывать пользователю "градусник", а можно за пару минут выполнить туже операцию, но без градусника. И каждый метод может дать "неожиданные" результаты, особенно если не понимать как они работают. Я лишь присоединюсь к первоначальному посту А.Еременко: Супер! 
				__________________ Ибо зло есть лучшая сила человека. "Человек должен становиться все лучше и злее" -- так учу я. /Ф. Ницше/ | 
|  | 
|  18.04.2007, 21:23 | #7 | 
| Участник | Цитата: Приведенный мной код замедляет работу ТОЛЬКО одного пользователя. НО приведенный мной код ускоряет работу НЕСКОЛЬКИХ одновременно работающих пользователей. Из-за снижения вероятности блокировок. За подробностями обращайтесь к совету Еременко. Цитата: Пользуйтесь стандартным классом SysOperationProgress. Разберитесь как и когда он обновляется и при каких условиях. Обсуждение этого класса наверное оффтопик в этой ветке. | 
|  | 
|  18.04.2007, 21:25 | #8 | 
| Участник | 
			
			Можно пример?
		 | 
|  | 
|  19.04.2007, 06:22 | #9 | 
| сибиряк | 
			
			Особенно чудные результаты дает этот класс при работе с несколькими компаниями.
		 
				__________________ С уважением, Вячеслав. | 
|  | 
|  19.04.2007, 08:05 | #10 | 
| Участник | Цитата: Здесь давайте вернемся к групповым операциям в Аксапте? | 
|  | 
|  19.04.2007, 09:01 | #11 | 
| злыдень | Цитата: 
		
			Сообщение от mazzy
			   Более правильный код: X++: SysProgressOperation progress;
;
      select count(recid) from custCollectionLetterTrans 
              where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum 
                 && custCollectionLetterTrans.AccountNum          == this.AccountNum 
                 && custCollectionLetterTrans.CollectionLetterIssued;
      progress = SysProgressOperation::newGeneral('','',custCollectionLetterTrans.recid);
      while select * from custCollectionLetterTrans 
              where custCollectionLetterTrans.CollectionLetterNum == this.CollectionLetterNum 
                 && custCollectionLetterTrans.AccountNum          == this.AccountNum 
                 && custCollectionLetterTrans.CollectionLetterIssued 
        { 
              ttsbegin;
             custTrans = CustTrans::find(custCollectionLetterTrans.CustTransId, true); 
              custTrans.CollectionLetterCode = custCollectionLetterTrans.CollectionLetterCode; 
              custTrans.update(); 
              ttscommit;
              progress.inccount();
         }... Представьте простую ситуацию, Вы разносите накладную из 100 строк. каждая строка обрабатывается в отдельной транзакции. На строке 87 возникает исключительная ситуация , товара недостаточно на складе, а предыдущие 86 строк уже списали товар.... Помедитируйте на тему зачем вообще нужны транзакции, как они работают и что такое целостность данных Буквально пару дней назад думал убью долго и подробно объяснял 3 одинэсникам почему, например, импорт документа из аксапты надо облачать в транзакзию, а не обрабатывать построчно как они привыкли. То ли это 1с разрушает моск, то ли просто так совпало 
				__________________ Ибо зло есть лучшая сила человека. "Человек должен становиться все лучше и злее" -- так учу я. /Ф. Ницше/ | 
|  | 
|  19.04.2007, 09:48 | #12 | 
| Участник | 
			
			Recoilme, вы понимаете разницу между "ЭТОТ код" и "ПРЕДСТАВЬТЕ простую ситуацию". Цитата: Цитата: Импорт документа в одной транзакции Но, скорее всего, не должно быть облачающей транзакции для импорта всех документов. Речь об этом, а не о строчках. Строчки естественно должны импортироваться в "облачающей" транзакции. Как то да... Берегите себя.   | 
|  | 
|  19.04.2007, 09:58 | #13 | 
| NavAx | Цитата: Если ожидается что транзакция будет слишком большой что чревато блокировками да и просто тормозит работу, надо стараться разбить её, в многих случаях это возможно. Можно делать ttscommit/ttsbegin например после каждых 500 строк. Но за маленькие транзакции я бью по рукам   Чтобы не быть голословным - провёл тест (по 5 тестов на каждый вариант) на табличке inventTrans (14 тыс. записей). Добавил в неё текстовое поле 10 - в него пишу timenow. В случае одной транзакции - 34,8 сек. (100%) В случае маленьких транзакций с прогрессбаром - 68,3 сек (196%) В случае маленьких транзакций без прогрессбара - 65,2 сек (187%) Версия Axapta - 4.0 SP1 Как показал тест, действительно немного он тратит, зря я его так. Хотя может это в четвёрке стало быстрее. Мне запомнилась цифра в 30%, надо бы проверить на трёшке. 
				__________________ С уважением, Игорь Ласийчук. | 
|  | 
|  19.04.2007, 10:12 | #14 | 
| Участник | 
			
			Да, например. А вот это зря. Цитата: 
		
			Сообщение от Garic
			   Чтобы не быть голословным - провёл тест (по 5 тестов на каждый вариант) на табличке inventTrans (14 тыс. записей). Добавил в неё текстовое поле 10 - в него пишу timenow. В случае одной транзакции - 34,8 сек. (100%) В случае маленьких транзакций с прогрессбаром - 68,3 сек (196%) В случае маленьких транзакций без прогрессбара - 65,2 сек (187%) Вы оптимизируете работу ОДНОГО пользователя. Попробуйте запустить хотя бы 10-20, а лучше 50-100 пользователей, которые работают с InventTrans, пишут и читают один и тот же набор данных. Оцените производительность больших и маленьких транзакций для МНОГОПОЛЬЗОВАТЕЛЬСКОЙ системы. Про прогресс бар пожалуйста сюда Какие проблемы у SysOperationProgress? Здесь предлагаю сосредоточиться на больших и маленьких транзакциях, групповых и негрупповых обработках данных. | 
|  | 
|  19.04.2007, 10:15 | #15 | 
| Участник | 
			
			Кстати, 14 тыс - это небольшое число. Все записи, скорее всего, помещаются в кэш сервера. О влиянии кэша см. http://axapta.mazzy.ru/lib/axapta_itanium/ Но даже на таком маленьком числе записей разница в многопользовательском тесет будет заметна. Пожалуйста, не надо из Аксапты делать однопользовательскую ЕРП. Пожалуйста, тестируйте для многих пользователей. | 
|  | 
|  19.04.2007, 15:05 | #16 | 
| NavAx | 
			
			Был исходный код, вы назвали его ужасным и предложили свой, мне он не понравился. Я не говорил что надо всё обрабатывать в одной транзакции. Я говорил что не надо так однозначно отзываться о коде. В каких-то случаях будет правильным первый вариант, в каких-то ваш. Но для длительных операций, не стоит делать такие маленькие транзакции как в вашем примере. Если уж делать, то объединять в одну транзакцию несколько итераций (например 500). Ну а блокировки - в четвёрке вроде с ними легче стало. 
				__________________ С уважением, Игорь Ласийчук. | 
|  | 
|  19.04.2007, 16:47 | #17 | 
| Участник | Цитата: Я продолжаю настаивать на том, что для многопользовательской системы транзакции надо делать как можно меньше. Пожалуйста, читайте совет Еременко aEremenko: Ресурс заблокирован, ждите... Если не верите, то проведите многопользовательский тест. Пожалуйста. Результаты тестирования в однопользовательском режиме не являются критериями правильности для транзакций (транзакции в однопользовательском режиме вообще не нужны). | 
|  | 
| Теги | 
| recordset, update_recordset, ax2009 | 
|  | 
| 
 |