Как объединить самые ранние документы каждого типа в запросе 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При получении начальных данных или формировании сведений о первичном вводе часто возникает задача: выбрать ровно по одному самому раннему документу для каждого вида метаданных и объединить их в единую таблицу результатов. Начинающие разработчики могут столкнуться с проблемой, когда привычная конструкция ВЫБРАТЬ ПЕРВЫЕ 1 и оператор УПОРЯДОЧИТЬ ПО работают не так, как ожидалось в рамках единой секции ОБЪЕДИНИТЬ ВСЕ.

Рассмотри, в чем заключается особенность обработки этих конструкций платформой 1С и какими способами можно правильно решить данную задачу.

Особенности работы конструкций ВЫБРАТЬ ПЕРВЫЕ и УПОРЯДОЧИТЬ ПО

Выясним, как платформа 1С выполняет сортировку и ограничение количества строк. Конструкция УПОРЯДОЧИТЬ ПО всегда выполняется до отсечения строк по условию ВЫБРАТЬ ПЕРВЫЕ N. То есть СУБД сначала упорядочивает выборку по указанным полям и только потом оставляет заданное количество первых записей.

Однако если написать несколько запросов, связанных через ОБЪЕДИНИТЬ ВСЕ, и разместить секцию УПОРЯДОЧИТЬ ПО в самом конце общего запроса, она применится не к каждой ветке по отдельности, а к итоговому результату всего объединения. В результате каждая ветка выберет произвольную первую попавшуюся запись из своей таблицы, а общий результат уже потом будет отсортирован по дате. Чтобы обойти это ограничение, необходимо вынести выборку первого элемента каждого типа в отдельные подзапросы или временные таблицы.

Решение 1. Использование временных таблиц

Разберем вариант с помещением первого документа каждого вида во временные таблицы. В каждой временной таблице применяется конструкция ВЫБРАТЬ ПЕРВЫЕ 1 вместе с явной сортировкой по дате, после чего полученные записи извлекаются и объединяются.

Посмотрим на пример такого запроса:


ВЫБРАТЬ ПЕРВЫЕ 1
	АккредитивПереданный.Ссылка КАК Ссылка,
	АккредитивПереданный.Дата КАК Дата
ПОМЕСТИТЬ ВТ_АккредитивПереданный
ИЗ
	Документ.АккредитивПереданный КАК АккредитивПереданный
УПОРЯДОЧИТЬ ПО
	Дата;

ВЫБРАТЬ ПЕРВЫЕ 1
	АктуализацияБюджетов.Ссылка КАК Ссылка,
	АктуализацияБюджетов.Дата КАК Дата
ПОМЕСТИТЬ ВТ_АктуализацияБюджетов
ИЗ
	Документ.АктуализацияБюджетов КАК АктуализацияБюджетов
УПОРЯДОЧИТЬ ПО
	Дата;

ВЫБРАТЬ
	Ссылка,
	Дата
ИЗ
	ВТ_АккредитивПереданный

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Ссылка,
	Дата
ИЗ
	ВТ_АктуализацияБюджетов;

Благодаря созданию временных таблиц сортировка и ограничение записей гарантированно срабатывают для каждого вида документа отдельно.

Решение 2. Использование подзапросов

Альтернативный подход заключается в использовании подзапросов в секции ИЗ основного запроса. Этот вариант удобен тем, что не создаются временные таблицы СУБД, а весь выбор происходит в рамках одного текста запроса.

Рассмотрим структуру подобного запроса:


ВЫБРАТЬ
	Вложено1.Ссылка КАК Ссылка,
	Вложено1.Дата КАК Дата
ИЗ
	(ВЫБРАТЬ ПЕРВЫЕ 1
		АккредитивПереданный.Ссылка КАК Ссылка,
		АккредитивПереданный.Дата КАК Дата
	ИЗ
		Документ.АккредитивПереданный КАК АккредитивПереданный
	УПОРЯДОЧИТЬ ПО
		Дата) КАК Вложено1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Вложено2.Ссылка КАК Ссылка,
	Вложено2.Дата КАК Дата
ИЗ
	(ВЫБРАТЬ ПЕРВЫЕ 1
		АктуализацияБюджетов.Ссылка КАК Ссылка,
		АктуализацияБюджетов.Дата КАК Дата
	ИЗ
		Документ.АктуализацияБюджетов КАК АктуализацияБюджетов
	УПОРЯДОЧИТЬ ПО
		Дата) КАК Вложено2;

Решение 3. Динамическое формирование запроса по всем документам конфигурации

Если необходимо обработать сразу все виды документов конфигурации, писать текст запроса вручную неудобно. В этом случае мы можем сформировать запрос программно в цикле по коллекции метаданных Метаданные.Документы.

Проанализируем пример кода на встроенном языке 1С:


ТекстЗапроса = "";
Первый = Истина;

Для Каждого МетаДокумент Из Метаданные.Документы Цикл

	ИмяДокумента = МетаДокумент.Имя;
	
	Если Не Первый Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|ОБЪЕДИНИТЬ ВСЕ
		|";
	КонецЕсли;
	
	ТекстЗапроса = ТекстЗапроса + "
	|ВЫБРАТЬ
	|	ВложенныйЗапрос.Ссылка КАК Ссылка,
	|	ВложенныйЗапрос.Дата КАК Дата
	|ИЗ
	|	(ВЫБРАТЬ ПЕРВЫЕ 1
	|		Док.Ссылка КАК Ссылка,
	|		Док.Дата КАК Дата
	|	ИЗ
	|		Документ." + ИмяДокумента + " КАК Док
	|	УПОРЯДОЧИТЬ ПО
	|		Док.Дата) КАК ВложенныйЗапрос";
	
	Первый = Ложь;

КонецЦикла;

Запрос = Новый Запрос(ТекстЗапроса);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Решение 4. Группировка с использованием функции МИНИМУМ

Еще один рабочий подход — получить минимальную дату для каждого вида документа через агрегатную функцию МИНИМУМ(Дата), а затем соединить полученное значение с исходной таблицей документов для получения ссылки.

Разберем этот вариант по шагам:

  1. С помощью функции МИНИМУМ(Дата) находим дату самого раннего документа заданного типа.
  2. Внутренним соединением по полю Дата находим ссылку на документ.
  3. Если на одну и ту же дату приходится несколько документов одного вида, дополнительно применяем группировку МИНИМУМ(Ссылка) для однозначного выбора одной записи.

Важные замечания по производительности и индексам

При реализации любого из решений следует учитывать следующие технические нюансы:

Использование индексов: В платформе 1С для всех документов по умолчанию строится составной индекс по полям Дата и Ссылка. Конструкции ВЫБРАТЬ ПЕРВЫЕ 1 и УПОРЯДОЧИТЬ ПО Дата задействуют этот индекс, поэтому СУБД считывает самую раннюю запись практически мгновенно, не сканируя всю таблицу.

Операторы ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ: Всегда используйте оператор ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ. Ключевое слово ОБЪЕДИНИТЬ заставляет СУБД выполнять дополнительную операцию по поиску и удалению дубликатов строк, что снижает скорость выполнения запроса.

Ограничения СУБД на количество объединений: При динамическом формировании текста запроса по всем документам крупной конфигурации единый запрос с сотнями секций ОБЪЕДИНИТЬ ВСЕ может упереться в ограничения СУБД на количество объединений и глубину дерева запроса. В таких случаях эффективнее выполнять запросы пакетом методом Запрос.ВыполнитьПакет() или обрабатывать метаданные порциями.

← На главную