02.09.2021, 08:47 | #1 |
Участник
|
как обойти ошибку - запись не выбиралась для обновления
Привет всем.
При выполнении такого джоба: X++: static void job123(Args _args) { ... void updateFieldValue(Common _common, FieldId _fieldId, anytype _value) { ... try { ttsBegin; _common.selectForUpdate(true); _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; } catch { ttsAbort; } ... } ... ; ... updateFieldValue(APMParameters::find(), fieldnum(APMParameters, Field1), valueField1); ... } генерируется ошибка: Цитата:
Невозможно отредактировать запись в Параметры (APMParameters).
Операция не может быть завершена, так как запись не выбиралась для обновления. Используйте TTSBEGIN/TTSCOMMIT наряду с выражением FORUPDATE. Попытка изменения записи в таблице "APMParameters", которая не выбрана для изменения (RecId = 5637144576). [W-0108] (S)\Classes\xRecord\doUpdate (C)\Jobs\job123 - line 77 Мне получилось ее обойти так: X++: static void job123(Args _args) { ... void updateFieldValue(Common _common, FieldId _fieldId, anytype _value) { ... try { //ttsBegin; // <-- убираем _common.selectForUpdate(true); _common.(_fieldId) = _value; _common.doUpdate(); //ttsCommit; // <-- убираем } catch { ttsAbort; } ... } ... ; ... ttsBegin; // <-- добавляем updateFieldValue(APMParameters::find(), fieldnum(APMParameters, Field1), valueField1); ttsCommit; // <-- добавляем ... } Вопросы: 1. возможно как то обойти данную ошибку но оставив ttsBegin/ttsCommit в методе updateFieldValue ? 2. Заметил что такая ошибка генерируется только для изменения данных у этой таблицы - APMParameters. Если такое же выполнять для других таблиц, например: RassetParameters, CompanyInfo таких ошибок нет. Не подскажете почему для одних таблиц нет ошибки а для APMParameters ошибка при вызове doUpdate()? Последний раз редактировалось oleggy; 02.09.2021 в 08:52. |
|
02.09.2021, 09:38 | #2 |
Administrator
|
Метод _common.selectForUpdate(true); должен выполняться до выборки данных.
Т.е. сначала _common.selectForUpdate(true); А потом - select * from common. Приведенный пример - излюбленный способ тех разработчиков, которым не хочется делать лишний select, а хочется уже после выборки либо обновлять, либо не обновлять запись (сам таким когда-то был - знаю). База данных так не работает. Ей нужно знать до выборки - блокировать ли записи или нет на обновление. Поэтому правильное решение - перестроить код так, чтобы вызов метода selectForUpdate был бы ДО оператора выборки данных. Конструкция, приведенная у Вас - может работать (ядро АХ все-таки старается) - но могут быть разные глюки - либо с блокировками при многопользовательской работе, либо с ошибками как у Вас, либо еще какие-то ошибки
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 02.09.2021 в 09:41. |
|
|
За это сообщение автора поблагодарили: SRF (1), oleggy (1). |
02.09.2021, 09:57 | #3 |
Участник
|
Посмотрите по ключевому слову OCCEnabled, https://docs.microsoft.com/en-us/dyn...rrency-control
Для табличек у которых OCCEnabled = yes, ваш код будет работать, для тех у которых No не будет, им нужна явная выборка в транзакции. Можно сделать примерно так, но получите дополнительное чтение в БД. X++: ttsbegin; _common.selectForUpdate(true); _common.reread(); _common.(_fieldId) = _value; _common.doUpdate(); ttscommit;
__________________
Sergey Nefedov Последний раз редактировалось SRF; 02.09.2021 в 10:03. |
|
|
За это сообщение автора поблагодарили: EVGL (5), sukhanchik (2), S.Kuskov (2), oleggy (1). |
02.09.2021, 10:03 | #4 |
Участник
|
А как сделать select для common таблицы?
Если известна recId записи и tableId этой таблицы. Подскажите подход. К сожалению не сработало. |
|
02.09.2021, 10:09 | #5 |
Участник
|
|
|
02.09.2021, 10:12 | #6 |
Участник
|
А попробуйте вместо reread сделать
select _common where _common.recid == _common.recId;
__________________
Sergey Nefedov |
|
02.09.2021, 10:21 | #7 |
Участник
|
Да, такая же ошибка, я проверил, для таблиц с OCCEnabled = No, reread не помогает, для них надо делать новый запрос в БД, видимо текущий курсор изменить уже нельзя, а нужно создавать новый.
__________________
Sergey Nefedov |
|
02.09.2021, 10:33 | #8 |
Участник
|
Получилось только так:
X++: static void job123(Args _args) { ... void updateFieldValue(Common _common, FieldId _fieldId, anytype _value) { DictField dictField; DictTable dictTable = new DictTable(_common.tableId); Common common; ; ... try { ttsBegin; common = dictTable.makeRecord(); common.selectForUpdate(true); select common where common.RecId == _common.RecId; common.(_fieldId) = _value; ttsCommit; } catch { ttsAbort; } ... } ... ; ... updateFieldValue(APMParameters::find(), fieldnum(APMParameters, Field1), valueField1); ... } |
|
|
За это сообщение автора поблагодарили: sukhanchik (2), SRF (1). |
02.09.2021, 10:40 | #9 |
Участник
|
А при такой конструкции ошибка ?
X++: ttsbegin; _common.selectForUpdate(true); select _common where _common.recid == _common.recId; _common.(_fieldId) = _value; _common.doUpdate(); ttscommit;
__________________
Sergey Nefedov |
|
02.09.2021, 10:41 | #10 |
Участник
|
Цитата:
Потом я создал локальную common и написал так. X++: select common where common.TableId == _common.TableId && common.RecId == _common.RecId; |
|
02.09.2021, 10:56 | #11 |
Участник
|
Ну надежды на то что сработает условие _common.recid == _common.recId нет и не может быть. Это как масло масленое. А то что common нужно инициализировать (типизировать) перед использованием в принципе логично. Иначе система не будет знать к какой таблице делать запрос и дополнительно условие по TableId здесь не спасает (в SQL нет такого понятия как запрос сразу ко всем таблицам).
Не понятно почему не сработал reread |
|
|
За это сообщение автора поблагодарили: SRF (2). |
02.09.2021, 11:26 | #12 |
Administrator
|
Цитата:
X++: TableId myTableId; RecId myRecId; DictTable dictTable = new DictTable(myTableId); Common myRecord; myRecord = dictTable.myRecord(); select myRecord where myRecord.RecId == myRecID; X++: TableId myTableId; RecId myRecId; Query query; QueryBuildDataSource qbds; QueryRun qr; query = new Query(); qbds = query.addDataSource(myTableId); qbds.addRange(fieldnum(Common, RecId)).value(queryvalue(myRecId)); qr = new QueryRun(query); if (qr.next()) { myRecord = qr.getNo(1); }
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 02.09.2021 в 11:33. |
|
02.09.2021, 12:31 | #13 |
Участник
|
Цитата:
X++: recId = _common.recid; select forupdate _common where _common.recid == recId; И по сути это тоже самое что и _common.reread(). Интересно сработает? Последний раз редактировалось S.Kuskov; 02.09.2021 в 12:36. |
|
02.09.2021, 18:51 | #14 |
Участник
|
Интересно, не встречал проблем с работой reread (ну конечно, если к этому моменту запись не удалена).
Там в вызове, случайно, нет какого-либо фиксирования, типа common.data() ? |
|
03.09.2021, 09:22 | #15 |
Участник
|
Цитата:
После этого решил проверить: X++: _common.reread() Зашел на другой терминал, начал выполнять - не сработало. Вернулся на первоначальный терминал и теперь и там не работает. Потом решил проверить это: Цитата:
После этого я снова решил проверить: X++: _common.reread() Последний раз редактировалось oleggy; 03.09.2021 в 09:24. |
|
03.09.2021, 09:35 | #16 |
Участник
|
Я понял из за чего ошибка генерируется у меня.
Данный код отрабатывает корректно: X++: ttsBegin; select forupdate _common where _common.recid == _common.recid; _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; X++: ttsBegin; _common.selectForUpdate(true); _common.reread(); _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; Если я в коде убираю _common.reread(), т.е. хочу сгененрировать ошибку: X++: ttsBegin; _common.selectForUpdate(true); //_common.reread(); // <-- убираю _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; X++: ttsBegin; _common.selectForUpdate(true); _common.reread(); // <-- возвращаю _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; Помогает снова обойти эту ошибку изменение кода на: X++: ttsBegin; select forupdate _common where _common.recid == _common.recid; _common.(_fieldId) = _value; _common.doUpdate(); ttsCommit; Последний раз редактировалось oleggy; 03.09.2021 в 09:40. |
|
03.09.2021, 10:01 | #17 |
Участник
|
Условие where _common.recid == _common.recid равносильно условию where true или что тоже самое отсутствию условия. Если у вас в таблице (APMParameters) одна запись то она и выберется и вы не почувствуете разницы. На таблицах с несколькими записями такой запрос вернёт вам первую попавшуюся запись, а не обязательно ту с которой вы работали.
|
|
03.09.2021, 12:04 | #18 |
Участник
|
Цитата:
Сообщение от S.Kuskov
Условие where _common.recid == _common.recid равносильно условию where true или что тоже самое отсутствию условия. Если у вас в таблице (APMParameters) одна запись то она и выберется и вы не почувствуете разницы. На таблицах с несколькими записями такой запрос вернёт вам первую попавшуюся запись, а не обязательно ту с которой вы работали.
X++: select table where true; А этот меняет. X++: select table where table.recId == table.recId; X++: Address address; RecId recId = 5637314047; ; select address where address.recId == recId; select address where address.recId == address.recId; // тут меняется recId //select address where true; // тут не меняется recId if (recId != address.RecId) info('меняется'); |
|
03.09.2021, 14:13 | #19 |
Участник
|
Ничего странного. Вообще оба запроса могут вернуть абсолютно любую запись случайным образом. Сегодня могут вернуть то, что было в первом запросе, завтра что-нибудь другое.
Оба запроса почти равнозначны. Просто в = true, скорее всего, вообще проигнорируется условие движком MS SQL (или Аксапты), второй что-нибудь попробует сравнить (а может и нет), но все равно для каждой записи таблицы условие выполняется. В базе в таблице хранится "неупорядоченный кортеж", если прямо не сортировать в самом запросе, то порядок выборки неопределен. Конечно, могут влиять кластерные индексы, кэшированные данные, фаза луны, расположение звезд. Но закладываться на то, что это постоянное влияние, не следует. В итоге, обсуждение чисто теоретическое и пользы, немного, какие бы предположения не делались. |
|
|
|