Выборка из результата запроса предназначена для обхода его записей. Можно представить себе выборку как некоторый объект, который содержит указатель на текущую запись в результате запроса и предоставляет программе доступ ко всем полям текущей записи. Обход выборки может быть линейный, иерархический или по группировкам. Возьмем результат простого запроса и продемонстрируем на его примере все варианты обхода выборки:
ВЫБРАТЬ Номенклатура, Количество
ИЗ Документ.РасходнаяНакладная.Состав
УПОРЯДОЧИТЬ ПО Номенклатура
ИТОГИ СУММА(Количество) ПО Номенклатура, Номенклатура Иерархия
Результат запроса показан в следующей таблице:
В этой таблице добавлен столбец №1, которого нет в результате запроса, но который будет использоваться нами в дальнейшем
для идентификации записи в результате. Итоговые
записи в таблице выделены курсивом, а итоговые записи
для уровней иерархии справочника выделены жирным
шрифтом.
Линейный обход
Первый и самый простой способ обхода — линейный. При линейном обходе выборка будет выдавать записи в той последовательности, в которой они располагаются в результате запроса. В нашем примере это будут записи с номерами 1, 2, 3, 4, 5 и так далее до записи с номером 20.
Для получения линейной выборки необходимо вызвать метод Выбрать объекта РезультатЗапроса без параметров, либо с параметром ОбходРезультатаЗапроса. Прямой.
СпособВыборки = ОбходРезультатаЗапроса.Прямой;
Выборка = РезультатЗапроса.Выбрать(СпособВыборки);
//или эквивалентная запись
Выборка = РезультатЗапроса.Выбрать();
Для навигации по записям запроса используются три метода:
Следующий
Позволяет перейти к следующей записи результата в соответствии с порядком обхода выборки. При первом вызове этот метод позиционирует выборку на первую запись. Когда будут выбраны все записи, данный метод просигнализирует об этом, вернув значение Ложь.
СледующийПоЗначениюПоля
Позволяет получить следующую запись со значением в заданном поле, отличающимся от значения в этом же поле текущей записи.
НайтиСледующий
Позволяет найти запись с заданными значениями некоторых полей.
Ниже приведен простейший пример обхода выборки с помощью метода Следующий:
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
//действия с записью
КонецЦикла;
Для получения данных текущей записи нужно обращаться к реквизитам объекта Выборка, имена которых совпадают с именами колонок результата запроса:
Запрос = Новый Запрос("
| ВЫБРАТЬ Код, Наименование КАК ФИО
| ИЗ Справочник.Сотрудники");
Выборка = Запрос.Выполнить{).Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Код: " + Выборка.Код);
Сообщить("ФИО: " + Выборка.ФИО);
КонецЦикла;
Метод СледующийПоЗначениюПоля позволяет сгруппироватьзаписи результата по значениям полей, например:
ВЫБРАТЬ Док.Товар. Док.Получатель, Док.Количество
ИЗ Документ.РасходнаяНакладная.Состав Док
УПОРЯДОЧИТЬ ПО Док.Товар.Наименование, Док.Получатель.Наименование
Допустим, в результате запроса мы получили следующие записи:
Организуем линейную выборку из результата запроса и обойдем ее при помощи метода СледующийПоЗначениюПоля:
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл
// здесь мы получим записи с номерами 1, 4, 8, 11
Пока Выборка.СледующийПоЗначениюПоля("Получатель") Цикл
// здесь мы сначала получим записи с номерами 1, 2, 3
// затем 4, 6, 6
// затем 8, 9, 10
// затем 11, 12, 13, 14
КонецЦикла;
КоиецЦикла;
Следует обратить внимание на то, что во внутреннем цикле не была выбрана запись с номером 7, так как в ней такое же значение поля Получатель, как и в предыдущей записи.
Заметим, что если во вложенном цикле получать записи с помощью метода Следующий, то будут выбраны все записи со значением поля, заданным в последнем вызове метода СледующийПоЗначениюПоля:
Выборка = РезультатЗапроса.Выбрать() ;
Пока Выборка.СледующийПоЗначениюПоля("Товар") Цикл
// здесь мы получим записи с номерами 1, 4, 8, 11
Пока Выборка.Следующий() Цикп
// здесь мы сначала получим записи с номерами 1, 2, 3
// затем 4, 5, 6, 7
// затем 8, 9, 10
// затем 11, 12, 13, 14
КонецЦикла;
КонецЦикла;
Метод НайтиСледующий позволяет позиционироваться на запись с нужными значениями полей, при этом текущая запись не рассматривается. В качестве условия поиска можно указать структуру или значение с именем колонки. Если запись найдена, то возвращается Истина, если таких записей больше не найдено, возвращается Ложь.
Метод НайтиСледующий имеет два вариант вызова:
НайтиСледующий(<Структура поиска>)
НайтиСледующий(<Значение>,<Имя колонки>)
Этот метод используется в следующем примере:
Запрос = Новый Запрос("
|Выбрать Наименование,
| ГОД(ДатаПриема) КАК Год,
| МЕСЯЦ(ДатаПриема) КАК Месяц
| Из Справочник.Сотрудники");
Выборка = Запрос.Выполнить().Выбрать();
//1-й вариант применения: поиск по структуре
//выберем всех сотрудников, принятых в июне 2003 года
СтруктураПоиска = Новый Структура("Год,Месяц",2003,5);
Пока Выборка.НайтиСледующий(СтруктураПоиска) Цикл
Сообщить("Сотрудник" + Выборка.Наименование);
КонецЦикла;
//2-й вариант: поиск значения по заданной колонке
//выберем всех сотрудников, принятых в 2003 году
Пока Выборка.НайтиСледующий(2003, "Год") Цикл
Сообщить("Сотрудник" + Выборка.Наименование);
КонецЦикла;
Рассмотрим другой способ обхода результата запроса — иерархический. При данном варианте обходятся только записи, находящиеся на одном уровне. Для получения иерархической выборки из результата необходимо вызвать метод Выбрать объекта РезультатЗапроса с параметром ОбходРезультатаЗапроса.ПоГруппировкамСИерархией:
СпособВыборки = ОбходРезультатаЗапроса.ПоГруппировкамСИерархией;
Выборка = РезультатЗапроса.Выбрать(СпособВыборки);
Выборка из результата запроса с иерархическим обходом в нашем примере обойдет только записи с номерами 1 и 11, так как только эти две записи находятся на самом верхнем уровне.
Проиллюстрируем это, представив наш результат в виде дерева, где узлами будут итоговые записи, а листьями дерева будут детальные записи. Вот что у нас получится:
Из этого рисунка видно, что именно записи с номерами 1 и 11 находятся на первом уровне дерева, и поэтому только они попадают в первый проход иерархической выборки.
Возникает вопрос, как получить остальные записи результата запроса. Для этого у объекта ВыборкаИзРезультатаЗапроса можно получить еще одну выборку, которая будет обходить подчиненные записи текущей записи выборки. В нашем примере в момент, когда объект Выборка будет позиционирован на запись с номером 1, мы запросим у него иерархическую выборку. Таким образом, мы получим выборку, которая вернет записи с номерами 2, 7. А когда Выборка будет спозиционирована на запись с номером 11, то полученная у нее иерархическая выборка вернет записи с номерами 12, 16. Так реализуется иерархический обход результатов запроса.
Заметим, что у выборки можно получать вложенные выборки любого типа. Так, если бы мы запросили у Выборки, спозиционированной на записи 1, линейную выборку, то с ее помощью мы бы получили записи с номерами со 2-го по 10-й.
Проиллюстрируем описанную методику на примере:
Процедура ВыдатьРекурсивно(Выборка) Далее;
Процедура ВыполнитьЗапрос()
Запрос = Новый Запрос("
ВЫБРАТЬ Товар, Количество
ИЗ Документ.РасходнаяНакладная.Состав
УПОРЯДОЧИТЬ ПО Товар
|ИТОГИ СУММА(Количество) ПО Товар, Товар ИЕРАРХИЯ");
СпособВыборки = ОбходРезультатаЗапроса.ПоГруппировкамСИерархией;
Выборка = Запрос.Выполнить().Выбрать(СпособВыборки);
ВыдатьРекурсивно (Выборка) ;
КонецПроцедуры
Процедура ВыдатьРекурсивно(Выборка)
Пока Выборка.Следующий() Цикл
//выведем в окно сообщений поля из результата
Товар = Выборка.Наименование;
Количество = Выборка.Количество;
Сообщить("Товар: " + СокрЛП(Товар) +
"Количество: " + СокрЛП(Количество));
//продолжим выборку подчиненных записей
СпособВыборки = ОбходРезультатаЗапроса.ПоГруппировкамСИерархией;
ВыдатьРекурсивно(Выборка.Выбрать(СпособВыборки));
КонецЦикла;
КонецПроцедуры
Обход по группировкам
Третий, и последний способ обхода результата — по группировкам. Он сходен с иерархическим обходом, но с одним различием: записи с иерархическими итогами при обходе в нем рассматриваются как детальные записи, а не как узловые. Для получения выборки по группировкам из результата запроса необходимо вызвать метод Выбрать объекта РезультатЗапроса с параметром ОбходРезультатаЗапроса.ПоГруппировкам:
СпособВыборки = ОбходРезультатаЗапроса.ПоГруппировкам;
Выборка = РезультатЗапроса.Выбрать(СпособВыборки);
Перебрав такую выборку для нашего примера, мы получим записи с номерами 1, 2, 7, 11, 12, 16, например:
Запрос = Новый Запрос("
| ВЫБРАТЬ Товар, Количество
| ИЗ Документ.РасходнаяНакладная.Состав
| УПОРЯДОЧИТЬ ПО Товар
| ИТОГИ СУММА(Количество) ПО Товар, Товар ИЕРАРХИЯ");
РезультатЗапроса = Запрос .Выполнить();
СпособВыборки = ОбходРезультатаЗапроса.ПоГруппировкам;
Выборка = Результатаапроса.Выбрать(СпособВыборки);
Пока Выборка.Следующий() Цикл
//выведем в окно сообщений поля из результата
Сообщить("Группа товаров: " + СокрЛП(Выборка.Наименование) +
"Количество: " + СокрЛП(Выборка.Количество));
//выберем дочерние записи линейным способом
ВыборкаДочерних = Выборка.Выбрать();
Пока ВыборкаДочерних.Следующий() Цикл
Сообщить(" Товар: " + СокрЛП(Выборка.Наименование) +
" Количество: " + СокрЛП(Выборка.Количество));
КонецЦикла;
КонецЦикла;
При работе с выборкой из результата запроса будут полезны следующие методы:
Группировка
Возвращает в виде строки имя группировки текущей записи. Если группировки нет, возвращается пустая строка.
Количество
Возвращает количество записей в выборке.
Сбросить
Отменяет позиционирование. После вызова метода Следующий выборка позиционируется на первую запись.
Уровень
Возвращает уровень текущей записи в иерархии и группировках. Уровень считается от начальной выборки из результата запроса.
ТипЗаписи
Этот метод возвращает тип текущей записи запроса, который является одним из значений системного перечисления ТипЗаписиЗапроса: ДетальнаяЗапись, ИтогПоГруппировке, ИтогПоИерархии, ОбщийИтог. кровельные работы, кровля в Марьино