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