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