Как соединить две несвязанные таблицы бок о бок в одном запросе 1С?

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

При формировании расчетных листков, справок или специализированных отчетов в системе компоновки данных (СКД) часто возникает задача вывести две независимые таблицы (например, список начислений и список удержаний по сотруднику) параллельно в соседних колонках. Если количество записей в таблицах не совпадает, простое объединение (ОБЪЕДИНИТЬ ВСЕ) выводит данные последовательно друг под другом со сдвигом колонок, а внутреннее соединение без сопоставимого ключа приводит либо к потере строк, либо к некорректному декартову произведению.

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

Решение в запросе: нумерация строк и полное соединение

Проанализируем механику задачи. Чтобы вывести данные в виде параллельных колонок по каждому сотруднику, необходимо сопоставить $N$-ю строку начислений с $N$-й строкой удержаний. Для этого требуется искусственно сгенерировать порядковый номер строки внутри группировки по сотруднику для каждого набора данных, после чего связать их через ПОЛНОЕ СОЕДИНЕНИЕ по паре полей (Работник, НомерСтроки).

Рассмотрим реализацию по шагам:

  1. Формирование исходных временных таблиц: выбираем исходные данные по начислениям и удержаниям во временные таблицы ВТ_Начисления и ВТ_Удержания.
  2. Генерация порядкового номера: соединяем каждую таблицу саму с собой по условию совпадения сотрудника и нестрогого неравенства ключевого поля (например, вида расчета). Количество строк во втором экземпляре таблицы при группировке дает порядковый номер строки.
  3. Итоговое полное соединение: связываем полученные нумерованные таблицы по сотруднику и номеру строки, обязательно оборачивая поля в функцию ЕСТЬNULL, так как при полном соединении в одной из таблиц строк может быть меньше.

Посмотрим на итоговый пример запроса:


ВЫБРАТЬ
	ВТ_Начисления.ВидРасчета КАК ВидРасчетаНачисление,
	ВТ_Начисления.Работник КАК Работник,
	КОЛИЧЕСТВО(ВТ_Начисления1.ВидРасчета) КАК НомерСтрокиНачисление,
	ВТ_Начисления.РазмерПриход КАК РазмерНачисление,
	ВТ_Начисления.СуммаВзаиморасчетаПриход КАК СуммаНачисление
ПОМЕСТИТЬ ВТ_НумерованныеНачисления
ИЗ
	ВТ_Начисления КАК ВТ_Начисления
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Начисления КАК ВТ_Начисления1
		ПО ВТ_Начисления.Работник = ВТ_Начисления1.Работник
			И ВТ_Начисления.ВидРасчета.Наименование >= ВТ_Начисления1.ВидРасчета.Наименование
СГРУППИРОВАТЬ ПО
	ВТ_Начисления.ВидРасчета,
	ВТ_Начисления.Работник,
	ВТ_Начисления.РазмерПриход,
	ВТ_Начисления.СуммаВзаиморасчетаПриход
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Удержания.ВидРасчета КАК ВидРасчетаУдержание,
	ВТ_Удержания.Работник КАК Работник,
	КОЛИЧЕСТВО(ВТ_Удержания1.ВидРасчета) КАК НомерСтрокиУдержание,
	ВТ_Удержания.РазмерПриход КАК РазмерУдержание,
	ВТ_Удержания.СуммаВзаиморасчетаПриход КАК СуммаУдержание
ПОМЕСТИТЬ ВТ_НумерованныеУдержания
ИЗ
	ВТ_Удержания КАК ВТ_Удержания
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Удержания КАК ВТ_Удержания1
		ПО ВТ_Удержания.Работник = ВТ_Удержания1.Работник
			И ВТ_Удержания.ВидРасчета.Наименование >= ВТ_Удержания1.ВидРасчета.Наименование
СГРУППИРОВАТЬ ПО
	ВТ_Удержания.ВидРасчета,
	ВТ_Удержания.Работник,
	ВТ_Удержания.РазмерПриход,
	ВТ_Удержания.СуммаВзаиморасчетаПриход
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ЕСТЬNULL(ВТ_НумерованныеНачисления.Работник, ВТ_НумерованныеУдержания.Работник) КАК Работник,
	ЕСТЬNULL(ВТ_НумерованныеНачисления.НомерСтрокиНачисление, ВТ_НумерованныеУдержания.НомерСтрокиУдержание) КАК НомерСтроки,
	ВТ_НумерованныеНачисления.ВидРасчетаНачисление КАК ВидРасчетаНачисление,
	ВТ_НумерованныеНачисления.РазмерНачисление КАК РазмерНачисление,
	ВТ_НумерованныеНачисления.СуммаНачисление КАК СуммаНачисление,
	ВТ_НумерованныеУдержания.ВидРасчетаУдержание КАК ВидРасчетаУдержание,
	ВТ_НумерованныеУдержания.РазмерУдержание КАК РазмерУдержание,
	ВТ_НумерованныеУдержания.СуммаУдержание КАК СуммаУдержание
ИЗ
	ВТ_НумерованныеНачисления КАК ВТ_НумерованныеНачисления
		ПОЛНОЕ СОЕДИНЕНИЕ ВТ_НумерованныеУдержания КАК ВТ_НумерованныеУдержания
		ПО ВТ_НумерованныеНачисления.Работник = ВТ_НумерованныеУдержания.Работник
			И ВТ_НумерованныеНачисления.НомерСтрокиНачисление = ВТ_НумерованныеУдержания.НомерСтрокиУдержание
УПОРЯДОЧИТЬ ПО
	Работник,
	НомерСтроки

Ограничения и подводные камни нумерации в запросе

При использовании данного способа важно помнить о технических особенностях выполнения подобных запросов в СУБД:

  1. Неуникальные значения и дублирование номеров: если у сотрудника присутствуют две строки с одинаковым видом расчета (или одинаковым наименованием), нестрогое условие соединения >= вернет для них одинаковый номер строки. При последующем полном соединении такие строки размножатся (декартово произведение). Для корректной нумерации условие должно строиться по строго уникальному набору полей (например, ВидРасчета, Регистратор, НомерСтроки).
  2. Снижение производительности: соединение таблицы самой с собой по неравенству имеет квадратичную сложность ($O(N^2)$). На больших базах данных при значительном числе строк на сотрудника это приводит к повышенной нагрузке на СУБД.
  3. Разыменование полей через точку: обращение ВидРасчета.Наименование внутри условия соединения порождает неявный LEFT JOIN. Рекомендуется либо сравнивать саму ссылку ВидРасчета, либо предварительно выгружать наименование в поля временной таблицы.

Альтернативные варианты реализации задачи в СКД

Если отчет разрабатывается на базе схемы компоновки данных, выводить данные бок о бок можно штатными механизмами платформы без усложнения SQL-запросов:

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