Почему запрос с таблицей значений возвращает пустой результат на сервере, но работает в файловой базе?

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

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

Анализ исходной проблемы

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


Запрос.Текст =
    "ВЫБРАТЬ
    |    ТаблицаСвободных.СвободныйНомер
    |ПОМЕСТИТЬ Номера
    |ИЗ
    |    &ТаблицаСвободных КАК ТаблицаСвободных
    |ГДЕ
    |    ТаблицаСвободных.СвободныйНомер МЕЖДУ &НачальноеЗначение И &КонечноеЗначение
    |;
    |...

В файловом варианте движок 1С обрабатывает таблицы значений в оперативной памяти, используя собственные алгоритмы, которые весьма «лояльны» к типам данных и структуре запроса. Однако в клиент-серверном варианте механизм в корне меняется. Платформа должна сначала передать данные из ТЗ на сервер СУБД (например, MS SQL или PostgreSQL), создать там физические временные таблицы и только потом выполнить расчет. Именно в этот момент возникают нюансы, приводящие к пустому результату.

Причина №1: Строгая типизация колонок

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

Посмотрим на правильный пример создания колонок ТЗ перед передачей в запрос:


// Обязательно используем квалификаторы для чисел и строк
КвалификаторыЧисла = Новый КвалификаторыЧисла(10, 0, ДопустимыйЗнак.Неотрицательный);
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", КвалификаторыЧисла);

ТаблицаСвободных = Новый ТаблицаЗначений;
ТаблицаСвободных.Колонки.Добавить("СвободныйНомер", ОписаниеТипаЧисло);

Если колонка будет иметь составной тип или тип без ограничений, SQL-сервер может некорректно сопоставить значения при выполнении условия МЕЖДУ или В (...), что приведет к исключению строк из выборки.

Причина №2: Структура пакетного запроса

Проанализируем еще один важный момент, на который указали участники форума. В клиент-серверном режиме рекомендуется разделять процесс наполнения временной таблицы и процесс её фильтрации. Когда мы пишем ПОМЕСТИТЬ из параметра-ТЗ и тут же накладываем ГДЕ, планировщик запросов СУБД иногда не может эффективно построить план выполнения, так как статистика по временной таблице еще не собрана.

Рассмотрим более надежный вариант структуры запроса:

  1. Сначала помещаем все данные из ТЗ во временную таблицу без условий.
  2. В следующем запросе пакета выбираем данные из созданной временной таблицы с наложением необходимых фильтров ГДЕ.

Это гарантирует, что данные сначала «приземлятся» в базу данных, и SQL-сервер сможет корректно обработать типы данных перед сравнением.

Причина №3: Проблема NULL в конструкции «НЕ В»

В тексте запроса автора используется конструкция НЕ Номера.СвободныйНомер В (ВЫБРАТЬ Исключить.Код ИЗ Исключить). Это классическая «ловушка» для SQL-систем. Выясним причину: согласно логике трехзначной логики SQL (True, False, Unknown), если подзапрос во второй части (ВЫБРАТЬ Исключить.Код...) вернет хотя бы одну строку со значением NULL, то весь основной запрос вернет пустой результат.

Чтобы избежать этой проблемы, выполним одно из двух действий:

Рассмотрим пример реализации через ЛЕВОЕ СОЕДИНЕНИЕ:


ВЫБРАТЬ
    Номера.СвободныйНомер КАК СвободныйНомер
ИЗ
    Номера КАК Номера
ЛЕВОЕ СОЕДИНЕНИЕ Исключить КАК Исключить
    ПО Номера.СвободныйНомер = Исключить.Код
ГДЕ
    Исключить.Код ЕСТЬ NULL

Рекомендации по оптимизации

Для стабильной работы на серверных базах придерживайтесь следующих правил:

  1. Индексация: Если ваша таблица значений содержит значительное количество записей (как в примере — до 99 000), обязательно используйте команду ИНДЕКСИРОВАТЬ ПО для временных таблиц по полям связи и фильтрации. Это критично для производительности SQL.
  2. Объем данных: Передача ТЗ объемом в 100 тысяч строк с клиента на сервер, а затем с сервера приложений 1С на сервер СУБД создает серьезную нагрузку на сеть. Проанализируйте ситуацию: возможно, эти данные можно получить запросом к базе данных (к справочникам или регистрам) прямо внутри пакета, не формируя ТЗ в коде.
  3. Отладка на сервере: Если запрос не работает, включите отладку на сервере и проверьте содержимое ТЗ непосредственно перед установкой параметра запроса. Убедитесь, что типы значений в ячейках соответствуют ожидаемым (иногда в колонке типа «Число» может случайно оказаться «Строка» или «Неопределено»).

Таким образом, переход от файлового режима к клиент-серверному требует от разработчика большей дисциплины в работе с типами данных и понимания механизмов работы SQL-сервера. Разделение загрузки данных и их обработки в запросе, а также явная типизация колонок решают большинство проблем с «пустыми» результатами.

← На главную