Как объединить значения запроса в 1С и избежать дублирования строк

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Разберем причину возникновения дублей

Проанализируем ситуацию: у нас есть документ РСУ_РасходныйОрдер, в котором несколько строк в табличной части ТЧОтгруженныеТМЦ. Также есть регистр сведений ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке, где по одному и тому же маршруту (документу-основанию) может быть сделано несколько записей (например, по каждой отсканированной упаковке).

Когда мы соединяем эти таблицы по полю Маршрут, происходит так называемое декартово произведение. Если в документе 3 строки с товарами, а в регистре по этому маршруту 2 записи от сканировщика, система создаст все возможные комбинации, и в итоге мы получим 6 строк (3 * 2). В отчете это приведет к тому, что количество, вес и объем товара будут завышены в два раза.

Шаг 1: Использование временных таблиц для подготовки данных

Чтобы избежать "каши" в запросе, лучше всего разделить получение данных на логические блоки с помощью временных таблиц. Это позволит нам предварительно свернуть (сгруппировать) данные регистра или документа до того, как они будут соединены.

Рассмотрим, как подготовить данные из регистра сведений. Нам нужно получить информацию о том, кто готовил маршрут к отгрузке. Поскольку записей в регистре может быть много, а нам нужен только автор, мы выберем уникальные значения:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке.ДокОснование КАК ДокументПодготовки,
    ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке.Автор КАК АвторПодготовки
ПОМЕСТИТЬ ВТ_Подготовка
ИЗ
    РегистрСведений.ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке КАК ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке
ГДЕ
    ПС_ОтсканированныеУпаковкиПодготовкаКОтгрузке.ДокОснование ССЫЛКА Документ.Маршрут
ИНДЕКСИРОВАТЬ ПО
    ДокументПодготовки
;

Важный момент: Мы использовали оператор РАЗЛИЧНЫЕ, чтобы схлопнуть все записи по упаковкам до уникальных пар "Маршрут - Автор". Также мы добавили ИНДЕКСИРОВАТЬ ПО, чтобы последующее соединение работало максимально быстро.

Шаг 2: Получение данных из табличной части документа

Теперь подготовим данные из документа. Нам важно учитывать только проведенные документы и отобрать нужные поля (Номенклатура, Количество, Маршрут). Проанализируем пример создания второй временной таблицы:


ВЫБРАТЬ
    ТЧ.Ссылка КАК Ссылка,
    ТЧ.Ссылка.Автор КАК АвторРасходника,
    ТЧ.Ссылка.ПС_ДокМаршрут КАК Маршрут,
    ТЧ.Номенклатура КАК Номенклатура,
    ТЧ.Кол КАК Количество,
    ТЧ.Ссылка.ДокРасход КАК ДокументОснование
ПОМЕСТИТЬ ВТ_ДанныеДокумента
ИЗ
    Документ.РСУ_РасходныйОрдер.ТЧОтгруженныеТМЦ КАК ТЧ
ГДЕ
    ТЧ.Ссылка.Проведен
    И ТЧ.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
ИНДЕКСИРОВАТЬ ПО
    Маршрут
;

Шаг 3: Финальное соединение и расчет итогов

Теперь, когда у нас есть две подготовленные таблицы, мы можем соединить их. Выясним причину, почему в предыдущих попытках автора данные могли пропадать. В 1С ЛЕВОЕ СОЕДИНЕНИЕ превращается во ВНУТРЕННЕЕ, если в секции ГДЕ накладывается условие на поля правой таблицы, которые могут быть NULL.

Рассмотрим правильный вариант финального запроса с использованием СУММА для числовых показателей:


ВЫБРАТЬ
    ДанныеДок.Маршрут КАК Маршрут,
    ДанныеДок.АвторРасходника КАК АвторРасходника,
    Подготовка.АвторПодготовки КАК АвторПодготовки,
    ДанныеДок.Номенклатура КАК Номенклатура,
    СУММА(ДанныеДок.Количество) КАК ОбщееКоличество,
    СУММА(ДанныеДок.Номенклатура.Вес * ДанныеДок.Количество) КАК ОбщийВес,
    СУММА(ДанныеДок.Номенклатура.Объем * ДанныеДок.Количество) КАК ОбщийОбъем
ИЗ
    ВТ_ДанныеДокумента КАК ДанныеДок
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Подготовка КАК Подготовка
        ПО ДанныеДок.Маршрут = Подготовка.ДокументПодготовки
СГРУППИРОВАТЬ ПО
    ДанныеДок.Маршрут,
    ДанныеДок.АвторРасходника,
    Подготовка.АвторПодготовки,
    ДанныеДок.Номенклатура

Типичные ошибки, которых следует избегать

Проанализируем основные ошибки, которые допускают разработчики при решении подобных задач:

  1. Группировка по количеству: Никогда не включайте числовые поля (количество, вес, сумма) в секцию СГРУППИРОВАТЬ ПО. Если в одном документе будут две строки с одинаковой номенклатурой и количеством "10", группировка по этому полю схлопнет их в одну строку, и вы потеряете данные. Используйте функцию СУММА().
  2. Избыточные соединения: Как было замечено в обсуждении, обращение к таблице Документ.РСУ_РасходныйОрдер часто бывает лишним, если все нужные реквизиты (Автор, Дата, Номер) можно получить через точку от ссылки в табличной части (ТЧ.Ссылка.Автор). Однако помните, что обращение через точку в запросе — это неявное левое соединение, и злоупотреблять ими в больших циклах не стоит.
  3. Проверка на проведение: Всегда добавляйте фильтр ГДЕ Ссылка.Проведен или проверяйте состояние документа, если вам нужны только актуальные данные складского учета.
  4. Типы данных: Если поле Маршрут имеет составной тип, убедитесь, что в регистре сведений ДокОснование имеет сопоставимый тип. В сложных случаях полезно использовать функцию ВЫРАЗИТЬ(Поле КАК Документ.ИмяДокумента).

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

← На главную