При разработке отчетов по план-фактному анализу (например, контроль выдачи спецодежды, оборудования или материалов) разработчики часто сталкиваются с задачей объединения двух наборов данных. У нас есть первая таблица с эталонными нормами и вторая таблица с фактическими остатками. Рассмотрим подробнее, как правильно написать запрос, чтобы не потерять сотрудников, которым еще ничего не выдали, и отсечь лишние данные.
Проанализируем ситуацию, описанную в задаче. У нас имеются две таблицы:
Требуется получить сводную таблицу, где по каждому сотруднику будет видна положенная номенклатура, ее нормативное количество, а также фактический остаток и дата выдачи. Главное условие: из второй таблицы не должны попадать излишки (то, что не положено по норме), а из первой таблицы должны выводиться позиции, которые сотруднику положены, но еще ни разу не выдавались.
Если мы просто применим ЛЕВОЕ СОЕДИНЕНИЕ от таблицы норм к таблице остатков, мы получим данные только по должностям, без привязки к конкретным Ивановым и Петровым (ведь в таблице норм нет фамилий). Если же сделать левое соединение от таблицы остатков к нормам, мы потеряем те позиции, которые сотруднику положены, но в регистре остатков по ним еще нет записей.
Разберем по шагам правильную архитектуру такого запроса. Нам необходимо применить подход с двумя итерациями и использованием временных таблиц. В первую очередь создадим полную сетку «Сотрудник — Положенная номенклатура».
Для этого выберем всех актуальных сотрудников и соединим их с таблицей норм по полю Должность. Таким образом, мы сгенерируем все положенные строки для каждого физического лица.
ВЫБРАТЬ
Сотрудники.Сотрудник КАК Сотрудник,
Сотрудники.Должность КАК Должность,
ВТНормыВыдачи.Номенклатура КАК Номенклатура,
ВТНормыВыдачи.Количество КАК НормаКоличество,
ВТНормыВыдачи.СрокИспользования КАК СрокИспользования
ПОМЕСТИТЬ ВТ_СотрудникиНормы
ИЗ
ВТ_СписокСотрудников КАК Сотрудники
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НормыВыдачи КАК ВТНормыВыдачи
ПО Сотрудники.Должность = ВТНормыВыдачи.Должность
Посмотрим на частую ошибку: прямое соединение с таблицей остатков (виртуальной таблицей регистра или физической таблицей документов). Одному и тому же сотруднику могли выдать одну номенклатуру несколькими документами (например, две пары перчаток в разные дни). Если не свернуть эти данные перед соединением, левое соединение задвоит нам нормативное количество.
Сгруппируем данные из регистра материалов перед итоговым соединением:
ВЫБРАТЬ
Остатки.ФизЛицо КАК ФизЛицо,
Остатки.ВидСпецодежды КАК Номенклатура,
СУММА(Остатки.КоличествоОстаток) КАК КоличествоОстаток,
МАКСИМУМ(Остатки.ДокументПередачиДата) КАК ДатаВыдачи
ПОМЕСТИТЬ ВТ_АгрегированныеОстатки
ИЗ
ВТ_СпецодеждаВНаличии КАК Остатки
СГРУППИРОВАТЬ ПО
Остатки.ФизЛицо,
Остатки.ВидСпецодежды
Теперь выполним финальное ЛЕВОЕ СОЕДИНЕНИЕ нашей подготовленной матрицы норм и агрегированных остатков. Соединять таблицы будем по двум ключам: сотруднику и номенклатуре.
Чтобы выполнить требование по отсечению лишней номенклатуры (которая есть в остатках, но которой нет в нормах), нам достаточно использовать именно левое соединение от временной таблицы норм. В этом случае всё, что не нашло соответствия в левой таблице, будет просто проигнорировано.
Кроме того, выясним причину пустых полей: когда номенклатура положена, но выдачи не было, база вернет значение NULL. Воспользуемся функцией ЕСТЬNULL, чтобы преобразовать такие значения в нули.
ВЫБРАТЬ
МатрицаНорм.Сотрудник КАК Сотрудник,
МатрицаНорм.Должность КАК Должность,
МатрицаНорм.Номенклатура КАК Номенклатура,
МатрицаНорм.НормаКоличество КАК КоличествоНорма,
МатрицаНорм.СрокИспользования КАК Срок,
ЕСТЬNULL(Факт.КоличествоОстаток, 0) КАК Остаток,
Факт.ДатаВыдачи КАК ДатаВыдачи
ИЗ
ВТ_СотрудникиНормы КАК МатрицаНорм
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_АгрегированныеОстатки КАК Факт
ПО МатрицаНорм.Сотрудник = Факт.ФизЛицо
И МатрицаНорм.Номенклатура = Факт.Номенклатура
УПОРЯДОЧИТЬ ПО
Сотрудник,
Номенклатура
Разберем еще несколько практических моментов, о которых стоит помнить при реализации таких запросов:
ВидНоменклатуры или НоменклатурнаяГруппа.ДОБАВИТЬКДАТЕ(Факт.ДатаВыдачи, МЕСЯЦ, МатрицаНорм.СрокИспользования).Используя этот подход с разбиением логики на временные таблицы и грамотным применением левых соединений, вы создадите надежный и легко читаемый запрос, который корректно соберет план-фактный отчет без искажения числовых показателей.