Сегодня в подборке всего два вопроса. Главное их сходство – это глубина и полнота полученных ответов от тренеров в Мастер-группах. Мы даже не сомневаемся, что первый слушатель уяснил, в чем разница между методами Следующий и СледующийПоЗначениюПоля, а второй – разобрался, почему нельзя использовать последовательность команд Движения.ИмяРегистра.Записывать = Истина и Движения.ИмяРегистра.Записать() при проведении документа по “новой” методике.
Вопрос №1: В чем разница методов навигации по записям результата запроса Следующий и СледующийПоЗначениюПоля?
Ответ
В общем случае эти методы могут давать разные результаты. Поэтому отметим разницу между методами Следующий и СледующийПоЗначениюПоля:
- При помощи метода Следующий происходит позиционирование на следующей записи из результата запроса.
- При помощи метода СледующийПоЗначениюПоля выборка позиционируется на следующую запись со значением, отличающимся от текущего значения, по указанному полю.
- Могут использоваться вложенные циклы, работающие с одной и той же выборкой:
Выборка = РезультатЗапроса.Выбрать(); //внешний цикл Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл //внутренний цикл Пока Выборка.СледующийПоЗначениюПоля("Покупатель") Цикл ... КонецЦикла; КонецЦикла;
Поскольку метод СледующийПоЗначениюПоля вызывается для одной и той же переменной Выборка, внутренний цикл по полю Покупатель будет искать следующих покупателей только в записях с тем товаром, на котором мы спозиционировались во внешнем цикле.
Теперь рассмотрим на примере. Пусть у нас есть следующие данные:
Данные из базы выбираем при помощи такого кода:
Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | Таблица.НомерСтроки КАК НомерСтроки, | Таблица.Товар КАК Товар, | Таблица.Покупатель КАК Покупатель |ИЗ | Таблица КАК Таблица"; Выборка = Запрос.Выполнить().Выбрать();
Сделаем 4 варианта обработки этой выборки:
Пока Выборка.Следующий() Цикл Сообщить("Номер строки - " + Выборка.НомерСтроки); КонецЦикла;
Выводит номера строк – 1, 2, 3, 4, 5, 6. Т.е. обходит последовательно все строки исходной таблицы.
Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл Сообщить("Номер строки - " + Выборка.НомерСтроки); КонецЦикла;
Выводит номер строки – 1, потому что в таблице используется только один товар во всех строках, значит, получается только одна итерация цикла.
Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл Сообщить("Внешний цикл. Номер строки - " + Выборка.НомерСтроки); Пока Выборка.СледующийПоЗначениюПоля("Покупатель") Цикл Сообщить("Внутренний цикл. Номер строки - " + Выборка.НомерСтроки); КонецЦикла; КонецЦикла;
Выводит:
Внешний цикл. Номер строки – 1
Внутренний цикл. Номер строки – 1
Почему именно так? Потому что данный фрагмент кода работает следующим образом:
- Во внешнем цикле вызвали метод Выборка.СледующийПоЗначениюПоля(“Товар”) – спозиционировались на первой строке. Затем выполнение кода переходит во вложенный цикл – по покупателям.
- Вызвали во вложенном цикле метод Выборка.СледующийПоЗначениюПоля(“Покупатель”) – спозиционировались на первом покупателе (Иванов) для текущего товара (Стол). Это тоже первая строка таблицы.
Поэтому и внешний, и внутренний цикл выводят первую строку таблицы.
Оба цикла завершают свою работу, поскольку все товары во всех строках одинаковы, а также все покупатели во всех строках одинаковы. Больше итераций циклов не будет.
Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл Сообщить("Внешний цикл. Номер строки - " + Выборка.НомерСтроки); Пока Выборка.Следующий() Цикл Сообщить("Внутренний цикл. Номер строки - " + Выборка.НомерСтроки); КонецЦикла; КонецЦикла;
Выводит:
Внешний цикл. Номер строки – 1
Внутренний цикл. Номер строки – 1
Внутренний цикл. Номер строки – 2
Внутренний цикл. Номер строки – 3
Внутренний цикл. Номер строки – 4
Внутренний цикл. Номер строки – 5
Внутренний цикл. Номер строки – 6
Внешний цикл выполняется только один раз, поскольку во всех строках таблицы один и тот же товар. А внутренний цикл выполняется 6 раз, обходит все строки таблицы.
Таким образом, нужно просто представлять, как будет работать метод Следующий или СледующийПоЗначениюПоля. И в зависимости от задачи выбирать тот, что подходит для решения задачи.
P.S.
Понимать, как работают запросы и уметь их строить - обязательный навык для всех, кто дорабатывает и внедряет 1С.
После курса Вы сможете:
- Строить сложные запросы с несколькими источниками данных
- Уверенно задействовать вложенные запросы и временные таблицы
- Использовать встроенный язык для обработки результатов запроса
- Учитывать особенности соединений и объединений нескольких таблиц.
- Разрабатывать запросы на уровне задач Аттестации 1С:Специалист по платформе.
Вопрос №2: В каких случаях на экзамене 1С:Специалист по платформе 8.3 нужно использовать команду Движения.Записать() и/или команду Движения.ИмяРН.Записать()?
Не могли бы вы подробно рассказать в каких случаях нужно использовать Движения.Записать(), а в каких Движения.ИмяРН.Записать()?
У меня после контроля остатков (по новой методике) было еще списание себестоимости по другому РН (по старой методике). К заполнению второго регистра претензий нет. Остатки контролировались, себестоимость рассчитывалась правильно. Я так понимаю претензия в том, что набор по первому регистру записывается дважды, сначала при Движения.РН.Записать(), а потом еще раз при окончании проведения.
Ответ
Если в Вашем решении сначала свойство Записывать набора записей регистра было установлено в значение “Истина”, а затем использовался метод Записать() набора записей регистра, то есть была такая последовательность команд:
Движения.ИмяРегистра.Записывать = Истина; Движения.ИмяРегистра.Записать();
то значением свойства Движения.ИмяРегистра.Записывать после этой последовательности команд останется по прежнему значение “Истина“. И в конце обработки проведения набор записей этого регистра действительно будет записан еще раз.
Для “старой” методики такая ситуация вполне нормальна: первая операция записи (явная, Движения.ИмяРегистра.Записать()) удаляет из базы прежние движения документа, записывая пустой набор (актуально при перепроведении). Вторая операция записи (неявная, в конце обработки проведения, на основании значения Движения.ИмяРегистра.Записывать = Истина) записывает в базу новые, сформированные этим документом движения.
Если же в процессе формирования движений будет выставлен признак отмены проведения (Отказ = Истина), то явно устанавливать значение свойства Движения.ИмяРегистра.Записывать в значение “Ложь” не требуется, так как при этом сама транзакция записи будет отменена, и повторной (в данном случае лишней) записи набора в базу данных не произойдет.
Если же указанная выше последовательность команд
Движения.ИмяРегистра.Записывать = Истина; и Движения.ИмяРегистра.Записать();
использовалась при проведении по “новой” методике, то, скорее всего, здесь и была допущена ошибка.
При “новой” методике предварительное удаление существующих движений не требуется, набор записей регистра записывается один раз – перед контролем остатков. И если запись производилась так, как показано выше, то система действительно выполнит лишнюю запись в базу данных при окончании проведения (т.к. значение свойства Движения.ИмяРегистра.Записывать осталось в значении “Истина”). В этом случае претензия экзаменатора вполне обоснована.
Подготовка к аттестации 1С:Специалист по платформе 1С:Предприятие 8.3.