При работе с виртуальными таблицами регистров накопления (особенно ОстаткиИОбороты) программисты часто сталкиваются с ограничением: если в параметрах таблицы указана периодичность День, то поле Регистратор становится недоступным для выбора. Это логично с точки зрения платформы 1С, так как данные за день суммируются (схлопываются), и конкретный документ-регистратор перестает быть однозначным идентификатором строки. Однако в реальных задачах часто требуется вывести обороты за день и рядом показать хотя бы один из документов или «развернуть» данные до документов, сохранив расчет итогов по дням. Рассмотрим, как решить эту задачу несколькими способами.
Для начала проанализируем ситуацию. Виртуальная таблица ОстаткиИОбороты с периодичностью День выполняет предварительную группировку записей на уровне СУБД. Если за 10 октября было 50 документов РеализацияТоваровУслуг, запрос вернет одну строку за эту дату. Платформа не «знает», какой именно из 50 регистраторов вам нужно показать в этой единственной строке. Поэтому поле Регистратор исключается из состава доступных полей. Чтобы обойти это ограничение, нам придется либо изменить подход к архитектуре запроса, либо использовать соединение с физической таблицей регистра.
Разберем первый метод. Мы можем сначала получить агрегированные данные (обороты по дням), поместить их во временную таблицу, а затем «подклеить» к ним регистраторы из основной таблицы движений регистра. Посмотрим на пример кода, который позволяет получить максимальный регистратор за каждый день для каждой номенклатуры.
// Шаг 1: Получаем обороты в разрезе дней
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ТоварыНаСкладахОстаткиИОбороты.Период, ДЕНЬ) КАК День,
ТоварыНаСкладахОстаткиИОбороты.Номенклатура,
ТоварыНаСкладахОстаткиИОбороты.КоличествоОборот
ПОМЕСТИТЬ втПоДню
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ТоварыНаСкладахОстаткиИОбороты
;
// Шаг 2: Находим какой-либо регистратор (например, максимальный) за этот день
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ) КАК День,
ТоварыНаСкладах.Номенклатура,
МАКСИМУМ(ТоварыНаСкладах.Регистратор) КАК Регистратор
ПОМЕСТИТЬ втМаксРегПоДню
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
(ТоварыНаСкладах.Номенклатура, НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ)) В
(ВЫБРАТЬ
втПоДню.Номенклатура,
втПоДню.День
ИЗ
втПоДню КАК втПоДню)
СГРУППИРОВАТЬ ПО
ТоварыНаСкладах.Номенклатура,
НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ)
;
// Шаг 3: Соединяем данные
ВЫБРАТЬ
втПоДню.День,
втПоДню.Номенклатура,
ЕСТЬNULL(втМаксРегПоДню.Регистратор, "Нет документа") КАК Регистратор,
втПоДню.КоличествоОборот
ИЗ
втПоДню КАК втПоДню
ЛЕВОЕ СОЕДИНЕНИЕ втМаксРегПоДню КАК втМаксРегПоДню
ПО втПоДню.Номенклатура = втМаксРегПоДню.Номенклатура
И втПоДню.День = втМаксРегПоДню.День
Проанализируем этот алгоритм. В первой временной таблице втПоДню мы получаем чистые данные из виртуальной таблицы. Это работает быстро. Во второй таблице втМаксРегПоДню мы обращаемся к физической таблице РегистрНакопления.ТоварыНаСкладах. Использование функции МАКСИМУМ позволяет нам схлопнуть все документы дня в одну строку, чтобы количество строк совпало с первой таблицей. Однако помните: это решение ресурсоемкое, так как чтение физической таблицы движений при больших объемах данных замедляет отчет. Для оптимизации работы с памятью в сложных пакетах запросов можно использовать обработку для анализа и расстановки команд уничтожения временных таблиц.
Выясним причину, по которой многие избегают периодичности Регистратор. Обычно это страх получить слишком детальную выборку, которую сложно обрабатывать. Но в 1С есть механизм, который называется периодичность Авто. Рассмотрим, как это работает в запросе:
Авто, нам становятся доступны поля: ПериодСекунда, ПериодДень, ПериодМесяц и Регистратор.ВЫБРАТЬ мы укажем и ПериодДень, и Регистратор, система вернет детальные записи.Рассмотрим пример настройки в СКД. Мы выбираем данные с периодичностью Регистратор. В настройках отчета создаем иерархию группировок: сначала ПериодДень, а внутри нее — детальные записи с полем Регистратор. При такой структуре 1С автоматически рассчитает остатки и обороты правильно для каждого уровня: и на начало дня, и на каждый конкретный документ. Это гораздо эффективнее, чем вручную соединять таблицы. Если же требуется вывести несколько документов дня в одну строку без создания вложенных группировок, можно применить способ сбора значений колонки из нескольких строк в СКД.
Бывают задачи, когда нужно не просто вывести «какой-нибудь» регистратор, а распределить дневной оборот между всеми документами дня. Рассмотрим сложный пример, где мы считаем количество документов за день и пытаемся сопоставить их с общим итогом.
ВЫБРАТЬ
втМаксРегПоДню.День,
втМаксРегПоДню.Номенклатура,
СУММА(1) КАК КоличествоДокументовЗаДень
ПОМЕСТИТЬ втДокументовВДень
ИЗ
втМаксРегПоДню КАК втМаксРегПоДню
СГРУППИРОВАТЬ ПО
втМаксРегПоДню.День,
втМаксРегПоДню.Номенклатура
;
ВЫБРАТЬ
втПоДню.День,
втПоДню.Номенклатура,
втМаксРегПоДню.Регистратор,
втПоДню.КоличествоОборот / ЕСТЬNULL(втДокументовВДень.КоличествоДокументовЗаДень, 1) КАК РаспределенныйОборот
ИЗ
втПоДню КАК втПоДню
ЛЕВОЕ СОЕДИНЕНИЕ втМаксРегПоДню КАК втМаксРегПоДню
ПО втПоДню.Номенклатура = втМаксРегПоДню.Номенклатура
И втПоДню.День = втМаксРегПоДню.День
Этот подход (предложенный в обсуждении) довольно специфичен. Он может пригодиться, если вам нужно визуально «размазать» итоги дня по строкам с регистраторами. Однако на практике чаще всего достаточно использовать правильную настройку ролей полей в СКД (поле Регистратор — роль «Период, 1», поле ПериодДень — роль «Период, 2»).
Проанализируем основные ошибки, которые могут привести к зависанию отчета при попытке вытащить регистратор в «дневной» отчет:
ПО НАЧАЛОПЕРИОДА(Таблица1.Период, ДЕНЬ) = НАЧАЛОПЕРИОДА(Таблица2.Период, ДЕНЬ). Это убивает использование индексов. Лучше заранее подготовить поле «День» во временной таблице.ОстаткиИОбороты при периодичности День выдает остатки на начало и конец дня. Если вы добавите регистратор через левое соединение, ваши остатки останутся «дневными». Они не будут меняться от документа к документу внутри дня. Если вам нужны актуальные остатки после каждой продажи — используйте только периодичность Регистратор.ПериодДень, ПериодНеделя. Используйте их вместо функций НАЧАЛОПЕРИОДА в тексте запроса, это работает быстрее.Подведем итог: Самый простой путь — использовать СКД с периодичностью Авто или Регистратор и настроить группировку по дням в интерфейсе настроек. Если же вы ограничены чистым запросом и вам нужен именно один документ в строке дня — используйте метод с временной таблицей и агрегатной функцией МАКСИМУМ(Регистратор).