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