В практике разработки на платформе 1С часто возникает специфическая задача: необходимо вывести в одном отчете два абсолютно независимых списка документов (например, поступления денежных средств и реализации товаров) таким образом, чтобы они располагались в параллельных колонках «друг напротив друга». Сложность заключается в том, что прямой связи между этими документами нет, а стандартное левое или полное соединение по контрагенту порождает «декартово произведение», когда на каждую строку поступления выводятся все строки реализаций, что приводит к дублированию данных и неверным итогам.
В данной статье мы разберем, как решить эту задачу исключительно средствами языка запросов 1С, используя механизмы временных таблиц и искусственной нумерации строк. Мы проанализируем различные подходы, их преимущества и ограничения.
Рассмотрим основной способ решения задачи, который не зависит от особенностей конкретной СУБД. Суть метода заключается в том, чтобы пронумеровать записи в каждой выборке (поступления и расходы) отдельно, а затем соединить их по этому порядковому номеру.
Разберем этот процесс по шагам. Сначала подготовим первую временную таблицу с поступлениями, добавив в нее поле-константу для последующего подсчета:
ВЫБРАТЬ
ПоступлениеВКассу.Ссылка КАК Ссылка,
ПоступлениеВКассу.СуммаДокумента КАК Сумма,
ПоступлениеВКассу.Дата КАК Дата,
1 КАК ПолеДляСчетчика
ПОМЕСТИТЬ ВТ_Поступления_Подготовка
ИЗ
Документ.ПоступлениеВКассу КАК ПоступлениеВКассу
ГДЕ
ПоступлениеВКассу.Контрагент = &Контрагент
И ПоступлениеВКассу.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания;
Теперь применим «хитрый трюк» для нумерации. Мы соединим таблицу саму с собой по условию Т1.Дата >= Т2.Дата. Количество записей, у которых дата меньше или равна текущей, и будет являться порядковым номером строки. Проанализируем этот запрос:
ВЫБРАТЬ
Т1.Ссылка КАК ДокументПоступления,
Т1.Сумма КАК СуммаПоступления,
КОЛИЧЕСТВО(Т2.Ссылка) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Поступления_Итог
ИЗ
ВТ_Поступления_Подготовка КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Поступления_Подготовка КАК Т2
ПО Т1.Дата >= Т2.Дата
СГРУППИРОВАТЬ ПО
Т1.Ссылка,
Т1.Сумма,
Т1.Дата;
Аналогичные действия необходимо выполнить для второй таблицы (например, «Расходная накладная»). В итоге мы получим две временные таблицы, в каждой из которых есть уникальный номер строки (1, 2, 3...).
Завершающим этапом станет ПОЛНОЕ СОЕДИНЕНИЕ этих таблиц по номеру строки. Это позволит нам вывести документы в параллельных колонках, даже если их количество в разных списках не совпадает:
ВЫБРАТЬ
ПКО.ДокументПоступления КАК Поступление,
ПКО.СуммаПоступления КАК СуммаПКО,
РКО.ДокументРахода КАК РасходнаяНакладная,
РКО.СуммаРахода КАК СуммаРН
ИЗ
ВТ_Поступления_Итог КАК ПКО
ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Расходы_Итог КАК РКО
ПО ПКО.НомерСтроки = РКО.НомерСтроки;
При использовании этого метода крайне важно учитывать ситуацию, когда несколько документов имеют абсолютно одинаковый момент времени (вплоть до секунды). Если просто соединять по Дата >= Дата, такие документы получат одинаковый номер строки, и при финальном соединении возникнет дублирование («размножение») строк.
Чтобы этого избежать, рекомендуем модифицировать условие соединения, добавив сравнение ссылок. Это обеспечит уникальность порядка даже при совпадении времени:
ПО (Т1.Дата > Т2.Дата ИЛИ (Т1.Дата = Т2.Дата И Т1.Ссылка >= Т2.Ссылка))
Такой подход гарантирует, что каждый документ получит свой уникальный инкрементный номер.
В более современных версиях платформы 1С v8.3 появилась возможность использовать функцию АВТОНОМЕРЗАПИСИ(). Это значительно упрощает текст запроса и повышает его читаемость. Выясним, как это работает на практике.
Функция работает только при помещении результата во временную таблицу. Посмотрим на пример кода:
ВЫБРАТЬ
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
ПКО.Ссылка КАК Ссылка,
ПКО.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_ПКО
ИЗ
Документ.ПоступлениеВКассу КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.Дата;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
РН.Ссылка КАК Ссылка,
РН.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_РН
ИЗ
Документ.РасходнаяНакладная КАК РН
УПОРЯДОЧИТЬ ПО
РН.Дата;
После подготовки таких таблиц мы также соединяем их через ПОЛНОЕ СОЕДИНЕНИЕ по полю НомерСтроки. Однако стоит помнить замечание опытных разработчиков: 1С не гарантирует, что АВТОНОМЕРЗАПИСИ() всегда будет начинаться с единицы во всех типах СУБД. Хотя для задачи сопоставления двух таблиц это не критично (главное, чтобы шаг был одинаковым), метод с расчетом номера через соединение (Метод 1) считается более «каноническим» и надежным для чистого SQL.
Нужно понимать, что метод нумерации через соединение ПО Т1.Дата >= Т2.Дата имеет квадратичную сложность — $O(n^2)$. Рассмотрим ситуацию: если у контрагента за период накопилось 1000 документов поступления, системе придется обработать 1 000 000 записей при формировании номеров.
Практические советы по оптимизации:
НомерСтроки перед финальным соединением, если в выборке более пары сотен строк.Если ваша задача позволяет использовать Систему компоновки данных, то проблему «параллельных колонок» можно решить без сложных манипуляций в запросе. Достаточно создать два набора данных и в настройках отчета (вкладка «Настройки» -> «Структура») расположить их горизонтально относительно друг друга (используя объединяющую группировку или таблицу).
Однако, если требуется именно программное получение таблицы значений с четким соответствием «Строка 1 — Строка 1», описанные выше методы с временными таблицами остаются единственным эффективным инструментом в арсенале разработчика 1С.