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