При получении начальных данных или формировании сведений о первичном вводе часто возникает задача: выбрать ровно по одному самому раннему документу для каждого вида метаданных и объединить их в единую таблицу результатов. Начинающие разработчики могут столкнуться с проблемой, когда привычная конструкция ВЫБРАТЬ ПЕРВЫЕ 1 и оператор УПОРЯДОЧИТЬ ПО работают не так, как ожидалось в рамках единой секции ОБЪЕДИНИТЬ ВСЕ.
Рассмотри, в чем заключается особенность обработки этих конструкций платформой 1С и какими способами можно правильно решить данную задачу.
Выясним, как платформа 1С выполняет сортировку и ограничение количества строк. Конструкция УПОРЯДОЧИТЬ ПО всегда выполняется до отсечения строк по условию ВЫБРАТЬ ПЕРВЫЕ N. То есть СУБД сначала упорядочивает выборку по указанным полям и только потом оставляет заданное количество первых записей.
Однако если написать несколько запросов, связанных через ОБЪЕДИНИТЬ ВСЕ, и разместить секцию УПОРЯДОЧИТЬ ПО в самом конце общего запроса, она применится не к каждой ветке по отдельности, а к итоговому результату всего объединения. В результате каждая ветка выберет произвольную первую попавшуюся запись из своей таблицы, а общий результат уже потом будет отсортирован по дате. Чтобы обойти это ограничение, необходимо вынести выборку первого элемента каждого типа в отдельные подзапросы или временные таблицы.
Разберем вариант с помещением первого документа каждого вида во временные таблицы. В каждой временной таблице применяется конструкция ВЫБРАТЬ ПЕРВЫЕ 1 вместе с явной сортировкой по дате, после чего полученные записи извлекаются и объединяются.
Посмотрим на пример такого запроса:
ВЫБРАТЬ ПЕРВЫЕ 1
АккредитивПереданный.Ссылка КАК Ссылка,
АккредитивПереданный.Дата КАК Дата
ПОМЕСТИТЬ ВТ_АккредитивПереданный
ИЗ
Документ.АккредитивПереданный КАК АккредитивПереданный
УПОРЯДОЧИТЬ ПО
Дата;
ВЫБРАТЬ ПЕРВЫЕ 1
АктуализацияБюджетов.Ссылка КАК Ссылка,
АктуализацияБюджетов.Дата КАК Дата
ПОМЕСТИТЬ ВТ_АктуализацияБюджетов
ИЗ
Документ.АктуализацияБюджетов КАК АктуализацияБюджетов
УПОРЯДОЧИТЬ ПО
Дата;
ВЫБРАТЬ
Ссылка,
Дата
ИЗ
ВТ_АккредитивПереданный
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Ссылка,
Дата
ИЗ
ВТ_АктуализацияБюджетов;
Благодаря созданию временных таблиц сортировка и ограничение записей гарантированно срабатывают для каждого вида документа отдельно.
Альтернативный подход заключается в использовании подзапросов в секции ИЗ основного запроса. Этот вариант удобен тем, что не создаются временные таблицы СУБД, а весь выбор происходит в рамках одного текста запроса.
Рассмотрим структуру подобного запроса:
ВЫБРАТЬ
Вложено1.Ссылка КАК Ссылка,
Вложено1.Дата КАК Дата
ИЗ
(ВЫБРАТЬ ПЕРВЫЕ 1
АккредитивПереданный.Ссылка КАК Ссылка,
АккредитивПереданный.Дата КАК Дата
ИЗ
Документ.АккредитивПереданный КАК АккредитивПереданный
УПОРЯДОЧИТЬ ПО
Дата) КАК Вложено1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Вложено2.Ссылка КАК Ссылка,
Вложено2.Дата КАК Дата
ИЗ
(ВЫБРАТЬ ПЕРВЫЕ 1
АктуализацияБюджетов.Ссылка КАК Ссылка,
АктуализацияБюджетов.Дата КАК Дата
ИЗ
Документ.АктуализацияБюджетов КАК АктуализацияБюджетов
УПОРЯДОЧИТЬ ПО
Дата) КАК Вложено2;
Если необходимо обработать сразу все виды документов конфигурации, писать текст запроса вручную неудобно. В этом случае мы можем сформировать запрос программно в цикле по коллекции метаданных Метаданные.Документы.
Проанализируем пример кода на встроенном языке 1С:
ТекстЗапроса = "";
Первый = Истина;
Для Каждого МетаДокумент Из Метаданные.Документы Цикл
ИмяДокумента = МетаДокумент.Имя;
Если Не Первый Тогда
ТекстЗапроса = ТекстЗапроса + "
|ОБЪЕДИНИТЬ ВСЕ
|";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|ВЫБРАТЬ
| ВложенныйЗапрос.Ссылка КАК Ссылка,
| ВложенныйЗапрос.Дата КАК Дата
|ИЗ
| (ВЫБРАТЬ ПЕРВЫЕ 1
| Док.Ссылка КАК Ссылка,
| Док.Дата КАК Дата
| ИЗ
| Документ." + ИмяДокумента + " КАК Док
| УПОРЯДОЧИТЬ ПО
| Док.Дата) КАК ВложенныйЗапрос";
Первый = Ложь;
КонецЦикла;
Запрос = Новый Запрос(ТекстЗапроса);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Еще один рабочий подход — получить минимальную дату для каждого вида документа через агрегатную функцию МИНИМУМ(Дата), а затем соединить полученное значение с исходной таблицей документов для получения ссылки.
Разберем этот вариант по шагам:
МИНИМУМ(Дата) находим дату самого раннего документа заданного типа.Дата находим ссылку на документ.МИНИМУМ(Ссылка) для однозначного выбора одной записи.При реализации любого из решений следует учитывать следующие технические нюансы:
Использование индексов: В платформе 1С для всех документов по умолчанию строится составной индекс по полям Дата и Ссылка. Конструкции ВЫБРАТЬ ПЕРВЫЕ 1 и УПОРЯДОЧИТЬ ПО Дата задействуют этот индекс, поэтому СУБД считывает самую раннюю запись практически мгновенно, не сканируя всю таблицу.
Операторы ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ: Всегда используйте оператор ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ. Ключевое слово ОБЪЕДИНИТЬ заставляет СУБД выполнять дополнительную операцию по поиску и удалению дубликатов строк, что снижает скорость выполнения запроса.
Ограничения СУБД на количество объединений: При динамическом формировании текста запроса по всем документам крупной конфигурации единый запрос с сотнями секций ОБЪЕДИНИТЬ ВСЕ может упереться в ограничения СУБД на количество объединений и глубину дерева запроса. В таких случаях эффективнее выполнять запросы пакетом методом Запрос.ВыполнитьПакет() или обрабатывать метаданные порциями.