Как убрать лишние строки и получить однозначное соответствие при соединении таблиц в запросах 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

При работе с базами данных 1С и составлении запросов, мы часто сталкиваемся с ситуацией, когда при соединении двух таблиц получаем больше строк, чем ожидаем. Это происходит из-за того, что для одной комбинации полей, по которым происходит соединение, в каждой из таблиц может существовать несколько связанных записей. В результате такого соединения формируется так называемое декартово произведение, которое приводит к избыточности данных.

Давайте разберем типичный сценарий, описанный вами, и выясним, как получить желаемый результат — однозначное соответствие одного ключа строки одному коду.

Анализ исходной проблемы: почему возникают лишние строки?

Предположим, у нас есть две таблицы с похожей структурой, но разными уникальными идентификаторами:

  1. Таблица 1 (Коды): Содержит номенклатуру, характеристику и коды.

    Пример данных:

    Номенклатура | Характеристика | Код

    Товар L | | 123

    Товар L | | 567

  2. Таблица 2 (КлючиСтроки): Содержит ту же номенклатуру, характеристику и ключи строк.

    Пример данных:

    Номенклатура | Характеристика | КлючСтроки

    Товар L | | 1

    Товар L | | 2

Если мы выполним стандартное соединение этих таблиц по полям Номенклатура и Характеристика (например, ВНУТРЕННЕЕ СОЕДИНЕНИЕ или ЛЕВОЕ СОЕДИНЕНИЕ), мы получим следующий результат:

Номенклатура | Характеристика | КлючСтроки | Код

Товар L | | 1 | 123

Товар L | | 1 | 567

Товар L | | 2 | 123

Товар L | | 2 | 567

Это происходит потому, что для каждой строки из Таблицы 2 (например, "Товар L, 1") запрос находит все соответствующие строки из Таблицы 1 (например, "Товар L, 123" и "Товар L, 567") и комбинирует их. В вашем случае вы хотите получить:

Номенклатура | Характеристика | КлючСтроки | Код

Товар L | | 1 | 123

Товар L | | 2 | 567

Такой результат подразумевает, что каждой записи КлючСтроки из второй таблицы мы хотим сопоставить только одну запись Код из первой таблицы. Здесь возникает ключевой вопрос: по какому принципу мы выбираем, какой именно Код (из 123 или 567) должен соответствовать КлючуСтроки 1, а какой КлючуСтроки 2? Если такого явного правила нет, нам придется его создать.

Определение правила сопоставления: самое важное

Прежде чем писать запрос, мы должны четко определить логику, по которой один КлючСтроки будет соотноситься с одним Кодом. Вот несколько возможных правил:

  1. Порядковая нумерация: Если нам не важен конкретный критерий выбора, но необходимо получить однозначное соответствие, мы можем просто пронумеровать записи в каждой группе (по Номенклатуре и Характеристике) и затем соединить их по этому номеру. Это наиболее универсальный подход, который решает вашу задачу с примером "Товар L 1 123, Товар L 2 567".
  2. Выбор по критерию: Если для каждой комбинации Номенклатура/Характеристика нам нужен, например, самый маленький или самый большой Код, или Код, соответствующий определенному условию.

Рассмотрим подробнее эти решения.

Решение 1: Использование порядковой нумерации (АВТОНОМЕРЗАПИСИ)

Это решение идеально подходит, если вам нужно сопоставить записи "один к одному" внутри общих групп (в нашем случае по Номенклатуре и Характеристике), и нет явного связывающего поля, кроме этих общих групп. Мы присвоим порядковые номера записям в каждой из таблиц, а затем соединим их по этим номерам.

Шаг 1: Нумерация строк в Таблице 1 (Коды)

Мы создадим временную таблицу, в которой для каждой уникальной комбинации Номенклатура и Характеристика присвоим порядковый номер каждому Коду. Порядок нумерации (например, по возрастанию Код) будет определять, какой Код получит номер 1, какой номер 2 и так далее.

Используем конструкцию ПОМЕСТИТЬ для создания временной таблицы и функцию АВТОНОМЕРЗАПИСИ() для нумерации.


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

Пояснение:

Результат для нашего примера:

Номенклатура | Характеристика | Код | НомерПоПорядку

Товар L | | 123 | 1

Товар L | | 567 | 2

Шаг 2: Нумерация строк в Таблице 2 (КлючиСтроки)

Аналогично, мы создадим временную таблицу для КлючейСтроки, присваивая им порядковые номера. Здесь также важно определить порядок нумерации, чтобы обеспечить желаемое соответствие.


ВЫБРАТЬ
    Т2.Номенклатура,
    Т2.Характеристика,
    Т2.КлючСтроки,
    АВТОНОМЕРЗАПИСИ() КАК НомерПоПорядку
ПОМЕСТИТЬ ВТ_КлючиСНомерами
ИЗ
    ТаблицаКлючейСтроки КАК Т2
УПОРЯДОЧИТЬ ПО
    Т2.Номенклатура,
    Т2.Характеристика,
    Т2.КлючСтроки АВТ
;

Пояснение:

Результат для нашего примера:

Номенклатура | Характеристика | КлючСтроки | НомерПоПорядку

Товар L | | 1 | 1

Товар L | | 2 | 2

Шаг 3: Соединение пронумерованных таблиц

Теперь, когда у нас есть две временные таблицы с порядковыми номерами для каждой группы Номенклатура/Характеристика, мы можем соединить их не только по Номенклатуре и Характеристике, но и по нашему новому полю НомерПоПорядку.


ВЫБРАТЬ
    ВТ_Ключи.Номенклатура,
    ВТ_Ключи.Характеристика,
    ВТ_Ключи.КлючСтроки,
    ВТ_Коды.Код
ИЗ
    ВТ_КлючиСНомерами КАК ВТ_Ключи
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КодыСНомерами КАК ВТ_Коды
        ПО ВТ_Ключи.Номенклатура = ВТ_Коды.Номенклатура
        И ВТ_Ключи.Характеристика = ВТ_Коды.Характеристика
        И ВТ_Ключи.НомерПоПорядку = ВТ_Коды.НомерПоПорядку
;

Пояснение:

Финальный результат (соответствует вашему желаемому):

Номенклатура | Характеристика | КлючСтроки | Код

Товар L | | 1 | 123

Товар L | | 2 | 567

Этот метод является наиболее гибким и мощным для решения задач, когда необходимо получить однозначное соответствие из потенциально множественных записей, и порядок соответствия может быть определен через сортировку.

Решение 2: Выбор конкретной записи из группы (ВЫБРАТЬ ПЕРВЫЕ или агрегатные функции)

Это решение подходит, если у нас есть определенный критерий для выбора "одного" кода (или ключа), и мы готовы к тому, что один и тот же выбранный код может быть сопоставлен нескольким ключам строки (если для одной номенклатуры/характеристики есть несколько ключей строки).

Например, если нам нужен минимальный или максимальный Код для каждой комбинации Номенклатура/Характеристика.

Шаг 1: Подзапрос для выбора одного Кода

Мы создадим подзапрос, который для каждой уникальной пары Номенклатура и Характеристика выбирает один Код, основываясь на заданном критерии (например, минимальный). Эта выборка будет содержать одну строку с одним кодом для каждой уникальной связки Номенклатура/Характеристика.


ВЫБРАТЬ
    Т1.Номенклатура КАК Номенклатура,
    Т1.Характеристика КАК Характеристика,
    МИНИМУМ(Т1.Код) КАК Код
ИЗ
    ТаблицаКодов КАК Т1
СГРУППИРОВАТЬ ПО
    Т1.Номенклатура,
    Т1.Характеристика

Пояснение:

Результат для нашего примера:

Номенклатура | Характеристика | Код

Товар L | | 123

Шаг 2: Соединение с таблицей КлючейСтроки

Теперь мы можем соединить эту агрегированную выборку с таблицей КлючейСтроки.


ВЫБРАТЬ
    Т2.Номенклатура,
    Т2.Характеристика,
    Т2.КлючСтроки,
    АгрегированныеКоды.Код
ИЗ
    ТаблицаКлючейСтроки КАК Т2
        ЛЕВОЕ СОЕДИНЕНИЕ (
            ВЫБРАТЬ
                Т1.Номенклатура КАК Номенклатура,
                Т1.Характеристика КАК Характеристика,
                МИНИМУМ(Т1.Код) КАК Код
            ИЗ
                ТаблицаКодов КАК Т1
            СГРУППИРОВАТЬ ПО
                Т1.Номенклатура,
                Т1.Характеристика
        ) КАК АгрегированныеКоды
        ПО Т2.Номенклатура = АгрегированныеКоды.Номенклатура
        И Т2.Характеристика = АгрегированныеКоды.Характеристика
;

Важное замечание: Этот метод даст вам один выбранный код (по критерию МИНИМУМ/МАКСИМУМ) для каждого КлючаСтроки, который соответствует этой Номенклатуре/Характеристике. Если у вас несколько КлючейСтроки для одной Номенклатуры/Характеристики, то каждый из них будет сопоставлен с одним и тем же агрегированным кодом. Например, для нашего примера результат будет:

Номенклатура | Характеристика | КлючСтроки | Код

Товар L | | 1 | 123

Товар L | | 2 | 123

Этот результат отличается от вашего желаемого результата "Товар L 1 123, Товар L 2 567", где каждый ключ строки сопоставляется с разным кодом. Поэтому для вашего конкретного примера решение с порядковой нумерацией (Решение 1) является более подходящим, так как оно позволяет сопоставлять разные ключи строки с разными кодами, сохраняя однозначность соответствия.

Дополнительные рекомендации и советы

  1. Используйте временные таблицы (ПОМЕСТИТЬ): Мы уже использовали их в примерах, и это отличная практика. Они помогают разбить сложные запросы на более мелкие, легко читаемые и отлаживаемые части. Это также может улучшить производительность, так как результаты промежуточных шагов кэшируются.
  2. Тщательно проверяйте условия соединения (ПО): Убедитесь, что вы соединяете таблицы по всем необходимым полям. В нашем случае это Номенклатура, Характеристика и дополнительное поле НомерПоПорядку.
  3. Понимайте свои данные: Всегда начинайте с анализа исходных данных и четкого формулирования желаемого результата. Только так вы сможете выбрать правильный метод соединения и агрегации.
  4. ВЫБРАТЬ ПЕРВЫЕ 1: Можно также использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 внутри подзапроса вместе с УПОРЯДОЧИТЬ ПО для выбора одной записи по определенному критерию, что аналогично использованию агрегатных функций, но часто более наглядно, когда нужно выбрать *всю строку*, а не только одно агрегированное поле.

В заключение, проблема "лишних строк" при соединении таблиц в 1С-запросах является распространенной и решается путем внедрения дополнительной логики выбора или сопоставления. Если нет явного связывающего поля, использование порядковой нумерации с помощью АВТОНОМЕРЗАПИСИ() становится мощным инструментом для достижения желаемого однозначного результата.

← На главную