Как оптимизировать вывод списка номенклатуры с проверкой наличия записей в регистре сведений?

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

При работе с крупными базами данных 1С, где справочник Номенклатура насчитывает десятки тысяч элементов, а регистры сведений — сотни тысяч, стандартные методы вывода данных в динамический список могут приводить к серьезным задержкам или полному зависанию системы. Типичная задача — отобразить в списке флажок (булево значение), который показывает, есть ли по данной позиции связанные записи в определенном регистре сведений. Рассмотрим, как реализовать это максимально эффективно.

Анализ проблемы производительности

Основная причина зависания запросов при соединении таблиц Справочник.Номенклатура и РегистрСведений.Связи заключается в неоптимальном построении запроса и отсутствии необходимых индексов. Проанализируем типичные ошибки:

  1. Обращение через точку: Использование конструкции Связи.Номенклатура.Ссылка заставляет систему выполнять лишнее соединение с таблицей справочника, что существенно замедляет выполнение на больших объемах.
  2. Вложенные запросы в соединениях: Хотя платформа 1С позволяет использовать вложенные запросы, оптимизатор СУБД не всегда эффективно строит план их выполнения внутри динамического списка.
  3. Отсутствие индексов: Если измерение Номенклатура в регистре сведений не проиндексировано, СУБД будет выполнять полное сканирование таблицы (Full Scan) вместо быстрого поиска.

Решение 1: Оптимизация запроса динамического списка

Для того чтобы динамический список работал быстро, нам необходимо минимизировать количество обрабатываемых строк. Рассмотрим вариант использования ЛЕВОЕ СОЕДИНЕНИЕ с предварительной группировкой данных регистра. Это позволит нам получить уникальный список ссылок номенклатуры, которые присутствуют в регистре, и сопоставить их с основным списком.

Рассмотрим пример правильно структурированного запроса для динамического списка:


ВЫБРАТЬ
    Номен.Ссылка КАК Ссылка,
    Номен.Наименование КАК Наименование,
    Номен.Код КАК Код,
    // Вычисляем индекс картинки на лету
    ВЫБОР
        КОГДА Номен.ПометкаУдаления ТОГДА 1
        ИНАЧЕ 0
    КОНЕЦ КАК ИндексКартинки,
    // Проверяем наличие связи через ЕСТЬNULL
    ВЫБОР
        КОГДА Связи.Номенклатура ЕСТЬ NULL ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ КАК ЕстьСвязи
ИЗ
    Справочник.Номенклатура КАК Номен
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ РАЗЛИЧНЫЕ
            РегСвязи.Номенклатура КАК Номенклатура
        ИЗ
            РегистрСведений.Связи КАК РегСвязи
    ) КАК Связи ПО Номен.Ссылка = Связи.Номенклатура
ГДЕ
    НЕ Номен.ЭтоГруппа

В данном примере мы используем подзапрос с ключевым словом РАЗЛИЧНЫЕ. Это критически важно, так как в регистре может быть много записей для одной номенклатуры, а для флажка нам достаточно знать лишь факт наличия хотя бы одной записи. Использование ЛЕВОЕ СОЕДИНЕНИЕ гарантирует, что мы увидим всю номенклатуру, а поле ЕстьСвязи примет значение ИСТИНА только при наличии совпадения.

Решение 2: Денормализация данных (самый быстрый способ)

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

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

  1. Добавим в справочник Номенклатура новый реквизит ЕстьСвязи с типом Булево. Установим для него свойство "Индексировать".
  2. В модуле набора записей регистра сведений Связи пропишем логику обновления этого реквизита при записи или удалении данных.
  3. В динамическом списке будем просто выводить поле Номенклатура.ЕстьСвязи.

Пример кода в модуле набора записей регистра:


Процедура ПередЗаписью(Отказ, Замещение)
    // Определяем список номенклатуры, которая меняется в данном наборе
    СписокНоменклатуры = ЭтотОбъект.Выгрузить().ВыгрузитьКолонку("Номенклатура");
    // Добавляем номенклатуру из старых записей, если идет замещение
    // ... логика получения старых значений ...
КонецПроцедуры

Процедура ПриЗаписи(Отказ, Замещение)
    // Для каждой номенклатуры из списка проверяем наличие оставшихся записей в регистре
    // и обновляем реквизит в справочнике.
    // Это можно делать фоновым заданием для исключения блокировок.
КонецПроцедуры

Важно: Этот метод обеспечивает мгновенную работу списка, так как СУБД читает данные из одной таблицы без соединений. Однако он требует аккуратности при написании кода обновления, чтобы данные в справочнике всегда соответствовали реальности в регистре.

Решение 3: Правильная настройка индексов и СКД

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

Работа с сегментами

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


ВЫБРАТЬ
    Номен.Ссылка,
    Номен.Наименование,
    ВЫБОР КОГДА Связи.Номенклатура ЕСТЬ NULL ТОГДА ЛОЖЬ ИНАЧЕ ИСТИНА КОНЕЦ КАК ЕстьСвязь
ИЗ
    Справочник.Номенклатура КАК Номен
    // Фильтр по сегменту через внутреннее соединение
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НоменклатураСегмента КАК Сегменты
        ПО Номен.Ссылка = Сегменты.Номенклатура
        И Сегменты.Сегмент = &Сегмент
    // Проверка наличия связей
    ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ РегистрСведений.Связи) КАК Связи
        ПО Номен.Ссылка = Связи.Номенклатура
ГДЕ
    НЕ Номен.ЭтоГруппа

Использование ВНУТРЕННЕЕ СОЕДИНЕНИЕ для фильтрации по сегменту обычно работает быстрее, чем конструкция В (ВЫБРАТЬ...), так как позволяет планировщику SQL-сервера эффективнее использовать индексы обеих таблиц.

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

← На главную