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