При передаче объекта ТаблицаЗначений в параметры запроса разработчики нередко сталкиваются со специфическими ошибками СУБД и платформы 1С. Рассмотрим распространенную ситуацию: при попытке соединить две временные таблицы возникает ошибка «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов», а при изменении условий запроса платформа выдает сообщение о недостатке памяти.
Разберем по шагам, почему возникают эти проблемы, как правильно задавать типы колонок в коде и как эффективно сравнить две таблицы значений с помощью языка запросов.
Проанализируем программный код, при исполнении которого платформа выдает ошибку:
таблица = Новый ТаблицаЗначений;
таблица.Колонки.Добавить("артикул", Новый ОписаниеТипов("Строка"));
таблица.Колонки.Добавить("размер", Новый ОписаниеТипов("Строка"));
Когда мы создаем колонку с типом Строка без указания квалификаторов (длины), платформа 1С считает такое поле строкой неограниченной длины. В базе данных или во временных таблицах такое поле сохраняется в виде неограниченного текстового типа (например, NVARCHAR(MAX) или TEXT).
СУБД не может выполнять операции сравнения (знаки =, <>, <, >) и соединять таблицы по полям с неограниченной длиной. Именно поэтому запрос завершается ошибкой при попытке сопоставить поля артикул или размер.
Выясним, как исправить ограничение типов на этапе создания таблицы значений или непосредственно в самом запросе.
Вариант А. Задание квалификаторов строки при создании колонок
Рассмотрим правильный способ создания колонок таблицы значений. При формировании ОписаниеТипов необходимо явно передавать объект КвалификаторыСтроки с указанием конкретной длины:
КвалификаторСтроки = Новый КвалификаторыСтроки(50); // Указываем длину строки
ОписаниеСтрока50 = Новый ОписаниеТипов("Строка", , КвалификаторСтроки);
таблица = Новый ТаблицаЗначений;
таблица.Колонки.Добавить("артикул", ОписаниеСтрока50);
таблица.Колонки.Добавить("размер", ОписаниеСтрока50);
Вариант Б. Использование функции ВЫРАЗИТЬ в запросе
Если таблица значений уже сформирована сторонней функцией и изменить её колонки невозможно, приведите тип поля к ограниченной строке прямо во временной таблице запроса с помощью конструкции ВЫРАЗИТЬ():
ВЫБРАТЬ
ВЫРАЗИТЬ(таблица2вт.артикул КАК Строка(150)) КАК артикул,
ВЫРАЗИТЬ(таблица2вт.размер КАК Строка(50)) КАК размер
ПОМЕСТИТЬ таблица2вт
ИЗ
&таблица2вт КАК таблица2вт
Разберем вторую проблему. После устранения ошибки неограниченной длины запрос может вызвать падение сеанса или ошибку переполнения памяти. Причина заключается в ошибочном условии соединения по знаку «не равно» (<>):
// НЕПРАВИЛЬНО: Декартово произведение (перемножение всех строк на все)
ЛЕВОЕ СОЕДИНЕНИЕ таблицаВТ КАК таблицаВТ
ПО таблица2вт.артикул <> таблицаВТ.артикул
И таблица2вт.размер <> таблицаВТ.размер
При соединении по условию <> система пытается сопоставить каждую строку первой таблицы со всеми несовпадающими строками второй таблицы. Если в обеих таблицах содержится по несколько тысяч строк, результат соединения увеличивается до миллионов записей. Это вызывает исчерпание оперативной памяти, особенно в файловом режиме работы платформы.
Для поиска различий между двумя таблицами значений необходимо использовать равенство ключей и тип соединения ПОЛНОЕ СОЕДИНЕНИЕ с отбором по пустой ссылке (ЕСТЬ NULL).
Рассмотрим правильную структуру запроса для поиска расхождений между двумя коллекциями:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(Т1.артикул КАК Строка(100)) КАК артикул,
| ВЫРАЗИТЬ(Т1.размер КАК Строка(50)) КАК размер
|ПОМЕСТИТЬ ВТ_Таблица1
|ИЗ
| &Таблица1 КАК Т1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВЫРАЗИТЬ(Т2.артикул КАК Строка(100)) КАК артикул,
| ВЫРАЗИТЬ(Т2.размер КАК Строка(50)) КАК размер
|ПОМЕСТИТЬ ВТ_Таблица2
|ИЗ
| &Таблица2 КАК Т2
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ЕСТЬNULL(ВТ_Таблица1.артикул, ВТ_Таблица2.артикул) КАК артикул,
| ЕСТЬNULL(ВТ_Таблица1.размер, ВТ_Таблица2.размер) КАК размер,
| ВЫБОР
| КОГДА ВТ_Таблица1.артикул ЕСТЬ NULL ТЕНДЕНЦИЯ "Отсутствует в первой"
| ИНАЧЕ "Отсутствует во второй"
| КОНЕЦ КАК Статус
|ИЗ
| ВТ_Таблица1 КАК ВТ_Таблица1
| ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Таблица2 КАК ВТ_Таблица2
| ПО ВТ_Таблица1.артикул = ВТ_Таблица2.артикул
| И ВТ_Таблица1.размер = ВТ_Таблица2.размер
|ГДЕ
| ВТ_Таблица1.артикул ЕСТЬ NULL
| ИЛИ ВТ_Таблица2.артикул ЕСТЬ NULL";
Запрос.УстановитьПараметр("Таблица1", Таблица1);
Запрос.УстановитьПараметр("Таблица2", Таблица2);
Результат = Запрос.Выполнить().Выгрузить();
Сформулируем основные правила эффективной работы с временными таблицами при сравнении:
ТаблицаЗначений или используйте функцию ВЫРАЗИТЬ() в запросе.=).ПОМЕСТИТЬ ВТ_Таблица1 ИНДЕКСИРОВАТЬ ПО артикул, размер.ПОЛНОЕ СОЕДИНЕНИЕ с отбором по ЕСТЬ NULL для нахождения строк, которые присутствуют только в одной из таблиц.