Как получить цены и время из нескольких независимых регистров сведений в 1С

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

В практике разработки на платформе 1С:Предприятие часто встречается ситуация, когда данные распределены по нескольким регистрам, имеющим схожую структуру. Например, если для каждой группы товаров (колбасы, доски, игрушки) был создан отдельный регистр цен. С точки зрения архитектуры это не всегда оптимально, но если задача стоит получить данные из существующих объектов, нам необходимо разобраться, как сделать это эффективно и универсально.

В этой статье мы разберем несколько способов решения: от простого объединения таблиц в конструкторе запросов до динамического формирования текста запроса на основе метаданных. Также мы рассмотрим, почему объединение регистров в один является лучшим долгосрочным решением.

Метод 1: Использование оператора ОБЪЕДИНИТЬ ВСЕ в запросе

Самый очевидный и простой для понимания способ — это написание одного запроса, который собирает данные из виртуальных таблиц СрезПоследних каждого регистра и объединяет их в одну результирующую таблицу. Рассмотрим этот процесс по шагам.

Допустим, у нас есть три регистра: ЦенаПроизводстваКолбас, ЦенаПроизводстваДосок и ЦенаПроизводстваИгрушек. Все они имеют измерение Номенклатура и ресурс Цена.

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


ВЫБРАТЬ
    "Колбасы" КАК Категория,
    СрезКолбас.Номенклатура КАК Номенклатура,
    СрезКолбас.Цена КАК Цена
ИЗ
    РегистрСведений.ЦенаПроизводстваКолбас.СрезПоследних(&Период, ) КАК СрезКолбас

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

ВЫБРАТЬ
    "Доски",
    СрезДосок.Номенклатура,
    СрезДосок.Цена
ИЗ
    РегистрСведений.ЦенаПроизводстваДосок.SрезПоследних(&Период, ) КАК СрезДосок

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

ВЫБРАТЬ
    "Игрушки",
    СрезИгрушек.Номенклатура,
    СрезИгрушек.Цена
ИЗ
    РегистрСведений.ЦенаПроизводстваИгрушек.СрезПоследних(&Период, ) КАК СрезИгрушек

При использовании этого метода важно помнить о следующих правилах:

  1. Совпадение типов данных: Колонки во всех частях объединения должны иметь одинаковые типы. Если в одном регистре Номенклатура — это справочник, а в другом — строка (что бывает крайне редко, но случается), запрос может вернуть ошибку или работать некорректно.
  2. Количество колонок: Количество выбираемых полей во всех блоках ОБЪЕДИНИТЬ должно быть одинаковым.
  3. Производительность: Если регистров очень много (например, более 10), СУБД может построить неоптимальный план запроса.

Метод 2: Динамическое формирование запроса через метаданные

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

Рассмотрим пример кода, который перебирает все регистры сведений, чье имя начинается с определенного префикса, и формирует единый запрос:


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

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

Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Результат = Запрос.Выполнить().Выгрузить();

Выясним причину эффективности этого метода: нам не нужно знать точный список регистров заранее. Если завтра появится регистр ЦенаПроизводстваМороженого, код подхватит его автоматически без изменений в конфигураторе.

Метод 3: Оптимизация через временные таблицы

Когда мы работаем с виртуальными таблицами СрезПоследних внутри объединения, система выполняет сложную операцию поиска последней записи для каждого регистра в отдельности. Если нам нужно дополнительно фильтровать данные или соединять их с другими таблицами, лучше сначала поместить результаты во временную таблицу.

Разберем по шагам, как это сделать:

  1. Сначала создаем текст запроса с объединением всех нужных срезов.
  2. Помещаем этот результат во временную таблицу с помощью команды ПОМЕСТИТЬ ВТ_ВсеЦены.
  3. В основном запросе выбираем данные из ВТ_ВсеЦены, где уже можно легко выполнять сортировку, группировку или левое соединение с остатками товаров.

ВЫБРАТЬ
    Срез.Номенклатура КАК Номенклатура,
    Срез.Цена КАК Цена
ПОМЕСТИТЬ ВТ_ОбщиеЦены
ИЗ
    РегистрСведений.Цена1.СрезПоследних(&Период, ) КАК Срез
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    Срез2.Номенклатура,
    Срез2.Цена
ИЗ
    РегистрСведений.Цена2.СрезПоследних(&Период, ) КАК Срез2;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Номенклатура,
    ВТ.Цена,
    СпрНоменклатура.Артикул
ИЗ
    ВТ_ОбщиеЦены КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНоменклатура
    ПО ВТ.Номенклатура = СпрНоменклатура.Ссылка

Архитектурный совет: Почему стоит отказаться от «кучи» регистров

Проанализируем ситуацию, описанную в теме. Создание отдельных регистров для каждого вида продукции — это архитектурная ошибка. Вот основные проблемы, к которым она приводит:

Решение: Объединить все данные в один регистр ЦеныПроизводства. Чтобы разделять данные, достаточно добавить новое измерение ВидПродукции (используя СправочникСсылка.ВидыНоменклатуры или ПеречислениеСсылка). Тогда запрос на получение всех цен станет максимально простым:


ВЫБРАТЬ
    Цены.Номенклатура,
    Цены.Цена
ИЗ
    РегистрСведений.ЦеныПроизводства.СрезПоследних(&Период, ) КАК Цены
ГДЕ
    Цены.ВидПродукции = &НужныйВид

Резюме

Если вы столкнулись с необходимостью собирать данные из множества регистров, используйте динамическое формирование текста запроса для обеспечения универсальности. Однако, если у вас есть возможность влиять на структуру базы данных, настоятельно рекомендуем выполнить рефакторинг и объединить эти регистры в один с дополнительным измерением. Это значительно упростит поддержку кода и повысит производительность системы в будущем.

← На главную