Как в запросе 1С найти строки, которые были изменены пользователем на форме?

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

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

Метод 1: Математическая свертка показателей (Метод "минусовых" значений)

Одним из самых изящных и производительных способов выявления дельты (разницы) между двумя состояниями данных является метод объединения с инверсией знака. Разберем этот подход по шагам. Представим, что у нас есть две временные таблицы: ВТ_ИсходныеДанные и ВТ_ТекущиеДанные.

Проанализируем алгоритм:

  1. Выбираем данные из первой таблицы как есть.
  2. Выбираем данные из второй таблицы, но умножаем числовые показатели (количество, сумма) на -1.
  3. Объединяем эти выборки через ОБЪЕДИНИТЬ ВСЕ.
  4. Группируем результат по ключевым полям (например, номенклатура, склад, идентификатор строки).
  5. В секции ГДЕ или ИМЕЮЩИЕ отсекаем строки, где итоговая сумма и количество равны нулю.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    Т.Номенклатура КАК Номенклатура,
    Т.Количество КАК Количество,
    Т.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_Развернутая
ИЗ
    (ВЫБРАТЬ
        Исходные.Номенклатура,
        Исходные.Количество КАК Количество,
        Исходные.Сумма КАК Сумма
    ИЗ
        ВТ_ИсходныеДанные КАК Исходные
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        Текущие.Номенклатура,
        -Текущие.Количество,
        -Текущие.Сумма
    ИЗ
        ВТ_ТекущиеДанные КАК Текущие) КАК Т
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Рез.Номенклатура,
    СУММА(Рез.Количество) КАК ДельтаКоличество,
    СУММА(Рез.Сумма) КАК ДельтаСумма
ИЗ
    ВТ_Развернутая КАК Рез
СГРУППИРОВАТЬ ПО
    Рез.Номенклатура
ИМЕЮЩИЕ
    СУММА(Рез.Количество) <> 0
    ИЛИ СУММА(Рез.Сумма) <> 0

Важный нюанс: как было отмечено в обсуждении, при использовании этого метода нужно быть осторожным с дублями строк. Если в одной таблице есть две одинаковые строки, а в другой одна, результат может быть корректным математически, но неверным логически. Для исключения "эффектов" дублей рекомендуется добавлять в группировку уникальный идентификатор строки или использовать синтетическую колонку (например, хеш-сумму полей).

Метод 2: Использование ПОЛНОГО СОЕДИНЕНИЯ (FULL OUTER JOIN)

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

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


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

Критически важно: при сравнении полей, которые могут быть не заполнены, всегда используйте функцию ЕСТЬNULL. В языке запросов 1С сравнение NULL <> 100 не вернет "Истина", оно вернет "Неизвестно", и такая строка просто выпадет из результата. Поэтому условие должно выглядеть так: ЕСТЬNULL(Т1.Количество, 0) <> ЕСТЬNULL(Т2.Количество, 0).

Метод 3: Сравнение по системным полям (КлючСвязи)

Часто программисты пытаются соединять строки по номеру (индексу). Однако, как справедливо замечено, это опасно: если пользователь удалит вторую строку из десяти, номера всех последующих строк изменятся, и запрос покажет изменения там, где их не было.

Проанализируем правильный подход: в современных версиях платформы 8.3 (особенно в типовых конфигурациях) для строк табличных частей предусмотрен КлючСвязи или ИдентификаторСтроки. Если вы передаете таблицу значений с формы в запрос, убедитесь, что вы включили этот идентификатор во временную таблицу. Сравнение именно по этому полю гарантирует точность сопоставления конкретной строки до и после редактирования.

Оптимизация: Индексы и производительность

Когда мы работаем с большими объемами данных (например, в документе сотни или тысячи строк), скорость работы запроса напрямую зависит от наличия индексов. Разберем, как правильно подготовить временные таблицы:


// Создаем первую временную таблицу с индексом по ключевому полю
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные1 ИЗ &ТЗ1 КАК ТЗ1 ИНДЕКСИРОВАТЬ ПО КлючСвязи";

// Аналогично для второй таблицы
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные2 ИЗ &ТЗ2 КАК ТЗ2 ИНДЕКСИРОВАТЬ ПО КлючСвязи";

Без индексации СУБД будет вынуждена использовать вложенные циклы для сопоставления строк, что приведет к деградации производительности. Индекс по полям связи — это "золотой стандарт" при работе с временными таблицами в задачах поиска изменений.

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

Если строк очень много и полей для сравнения тоже (например, 20 колонок), писать длинное условие ГДЕ Т1.Поле1 <> Т2.Поле1 ИЛИ Т1.Поле2 <> Т2.Поле2... становится трудоемко. В таких случаях можно заранее на стороне клиента или сервера (в коде 1С) вычислить контрольную сумму (хеш) для каждой строки и передать её в запрос. Тогда в самом запросе вам достаточно будет сравнить только два поля: ИдентификаторСтроки и ХешСтроки.

Подведем итог: выбор метода зависит от вашей цели. Если нужно быстро получить итоговые отклонения по суммам — используйте метод свертки со знаком минус. Если же необходим детальный аудит действий пользователя (что именно добавлено, а что изменено) — используйте ПОЛНОЕ СОЕДИНЕНИЕ с обязательной проверкой на NULL и индексацией по уникальному ключу строки.

← На главную