Почему 1С генерирует запрос с конструкцией SELECT TOP 25000?

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

При администрировании крупных информационных баз на платформе «1С:Предприятие» разработчики и системные администраторы часто сталкиваются с анализом производительности СУБД. В процессе исследования технологического журнала (ТЖ) или трассировок SQL Server можно обнаружить весьма специфические запросы, содержащие жестко заданное ограничение SELECT TOP 25000. На первый взгляд, такая конструкция кажется странной, особенно если в самом коде конфигурации подобные лимиты явно не прописывались.

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

Анализ структуры SQL-запроса

Проанализируем представленный в теме SQL-запрос. Мы видим, что платформа выполняет выборку данных из таблицы документа _Document391 (в терминах 1С это может быть, например, документ «Поступление на расчетный счет») и его табличной части _Document391_VT10469 («Расшифровка платежа»). Результат запроса помещается во временную таблицу СУБД:


INSERT INTO #tt69 WITH(TABLOCK) (_C_1RRef, _C_2_TYPE, ...)
SELECT TOP 25000 T1._IDRRef, ...
FROM dbo._Document391 T1
...

В условиях выборки WHERE присутствует массовый поиск по полям субконто и реквизитам табличных частей. Платформа ищет ссылки определенного типа (в данном случае тип идентифицируется шестнадцатеричным кодом 0x00000192) и конкретных значений, передаваемых через параметры СУБД (@P2, @P3 и так далее).

Такой сложный и «тяжелый» запрос с использованием множественных логических операторов OR EXISTS и условий по разным полям шапки и табличной части характерна для поиска связей между объектами. Это подводит нас к главной причине появления этой конструкции.

Основная причина: Контроль ссылочной целостности и поиск ссылок

Конструкция SELECT TOP 25000 является жестко зашитым на уровне платформы «1С:Предприятие» ограничением (встроенным в слой трансляции SDBL — System Database Layer). Она генерируется исключительно платформенными механизмами контроля ссылочной целостности в следующих случаях:

  1. Вызов метода НайтиПоСсылкам(): Когда разработчик программно вызывает этот метод для поиска всех мест использования определенных элементов справочников или документов.
  2. Интерактивное удаление помеченных объектов: При запуске стандартной обработки «Удаление помеченных объектов» система опрашивает все связанные таблицы базы данных, чтобы определить, можно ли безвозвратно удалить выбранные элементы.
  3. Непосредственный вызов метода Удалить(): При попытке удаления объекта объектная техника платформы неявно выполняет проверку на наличие существующих ссылок в базе данных, чтобы не допустить появления «битых» ссылок.

Таким образом, когда система пытается удалить объект или найти его использование, она выполняет каскадный обход метаданных. Для каждой таблицы, которая теоретически может содержать ссылку на проверяемый объект, платформа формирует индивидуальный SQL-запрос с ограничением TOP 25000.

Почему лимит установлен именно в 25 000 строк?

Выбор именно этого числового значения обусловлен архитектурными компромиссами разработчиков платформы 1С:

  1. Защита от переполнения оперативной памяти (OutOfMemory): Если удаляемый или анализируемый объект (например, предопределенная статья движения денежных средств или ключевой контрагент) используется в миллионах документов, попытка СУБД моментально выгрузить все связанные записи привела бы к исчерпанию ресурсов сервера приложений 1С и зависанию рабочего процесса rphost.
  2. Порционность обработки: Для принятия решения о невозможности удаления объекта платформе достаточно найти хотя бы одну активную ссылку. Однако для вывода детального отчета пользователю (списка «мест использования») требуется собрать больше информации. Лимит в 25 000 записей на одну таблицу — это разумный порог, позволяющий показать пользователю репрезентативный список связей, не перегружая при этом память.
  3. Оптимизация работы временных таблиц: Платформа собирает результаты поиска во временные таблицы (например, #tt69) порциями. При превышении лимита в 25 000 записей алгоритм может остановить дальнейшее сканирование конкретной таблицы и перейти к следующей, экономя время выполнения операции.

Проблема производительности: Составные типы данных и блокировки

Несмотря на заложенную оптимизацию, запросы с SELECT TOP 25000 могут стать источником серьезных проблем с производительностью и вызывать массовые транзакционные блокировки в СУБД. Выясним, почему это происходит.

Главная опасность кроется в использовании реквизитов с **составным типом данных**, особенно если в состав входит неограниченный тип ЛюбаяСсылка (в терминах СУБД — AnyRef). Когда система ищет ссылки на объект, тип которого может быть записан в такой составной реквизит, платформа вынуждена генерировать аналогичные запросы ко всем физическим таблицам СУБД, где этот реквизит объявлен.

В результате СУБД выполняет сканирование таблиц (Table Scan или Index Scan) вместо быстрого поиска по индексу (Index Seek), поскольку СУБД не может эффективно использовать индексы при сложных условиях с OR EXISTS по нескольким полям и табличным частям. Это приводит к лавинообразному росту нагрузки на дисковую подсистему и процессор сервера.

Кроме того, использование хинта WITH(TABLOCK) при записи во временные таблицы СУБД при массовом параллельном поиске ссылок может приводить к монопольным блокировкам системной базы данных tempdb, парализуя работу остальных пользователей.

Как оптимизировать работу системы и локализовать проблему

Если в вашей базе данных начали регулярно фиксироваться длительные запросы с конструкцией SELECT TOP 25000, разберем по шагам план действий для исправления ситуации:

  1. Идентификация источника через Технологический Журнал: Настройте сбор событий DBMSSQL с отбором по подстроке "SELECT TOP 25000". Анализируя свойство Context, вы легко определите конкретный модуль 1С и строчку кода, которая инициировала вызов (например, конкретную кнопку на форме или регламентное задание).
  2. Определение метаданных: Используйте функцию платформы ПолучитьСтруктуруХраненияБазыДанных(), чтобы сопоставить имена таблиц СУБД (например, _Document391) с реальными объектами конфигурации.
  3. Сокращение составных типов данных: Проанализируйте структуру метаданных. По возможности избегайте использования типа ЛюбаяСсылка и избыточно широких составных типов в реквизитах документов и справочников. Сужение типа данных существенно сократит количество таблиц, опрашиваемых платформой при поиске ссылок.
  4. Оптимизация кода поиска ссылок: Если вам программно требуется проверить наличие ссылок на объект, не всегда обязательно использовать ресурсоемкий метод НайтиПоСсылкам() для всей базы данных. Если бизнес-логика позволяет, лучше написать точечный и быстрый запрос к конкретным таблицам, где эти связи наиболее вероятны.

Рассмотрим пример того, как можно оптимизировать программную проверку наличия ссылок, избегая тяжелого платформенного механизма:


// Вместо медленного НайтиПоСсылкам([МассивСсылок])
// Используем точечный запрос к известным местам использования

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	Ссылка
    |ИЗ
    |	Документ.РеализацияТоваровУслуг
    |ГДЕ
    |	Контрагент = &СсылкаДляПроверки";

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

Использование подобных точечных проверок в критически важных участках кода позволяет полностью обойти генерацию платформенных запросов SELECT TOP 25000, снижая нагрузку на СУБД и предотвращая избыточное наложение транзакционных блокировок.

← На главную