Как правильно сравнить две таблицы значений в запросе 1С и исправить ошибку сравнения полей неограниченной длины

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

При передаче объекта ТаблицаЗначений в параметры запроса разработчики нередко сталкиваются со специфическими ошибками СУБД и платформы 1С. Рассмотрим распространенную ситуацию: при попытке соединить две временные таблицы возникает ошибка «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов», а при изменении условий запроса платформа выдает сообщение о недостатке памяти.

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

Причины возникновения ошибки неограниченной длины

Проанализируем программный код, при исполнении которого платформа выдает ошибку:


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

Когда мы создаем колонку с типом Строка без указания квалификаторов (длины), платформа 1С считает такое поле строкой неограниченной длины. В базе данных или во временных таблицах такое поле сохраняется в виде неограниченного текстового типа (например, NVARCHAR(MAX) или TEXT).

СУБД не может выполнять операции сравнения (знаки =, <>, <, >) и соединять таблицы по полям с неограниченной длиной. Именно поэтому запрос завершается ошибкой при попытке сопоставить поля артикул или размер.

Решение 1. Явное ограничение длины строки

Выясним, как исправить ограничение типов на этапе создания таблицы значений или непосредственно в самом запросе.

Вариант А. Задание квалификаторов строки при создании колонок

Рассмотрим правильный способ создания колонок таблицы значений. При формировании ОписаниеТипов необходимо явно передавать объект КвалификаторыСтроки с указанием конкретной длины:


КвалификаторСтроки = Новый КвалификаторыСтроки(50); // Указываем длину строки
ОписаниеСтрока50 = Новый ОписаниеТипов("Строка", , КвалификаторСтроки);

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

Вариант Б. Использование функции ВЫРАЗИТЬ в запросе

Если таблица значений уже сформирована сторонней функцией и изменить её колонки невозможно, приведите тип поля к ограниченной строке прямо во временной таблице запроса с помощью конструкции ВЫРАЗИТЬ():


ВЫБРАТЬ
    ВЫРАЗИТЬ(таблица2вт.артикул КАК Строка(150)) КАК артикул,
    ВЫРАЗИТЬ(таблица2вт.размер КАК Строка(50)) КАК размер
ПОМЕСТИТЬ таблица2вт
ИЗ
    &таблица2вт КАК таблица2вт

Причина ошибки «Недостаточно памяти» и правильная логика сравнения

Разберем вторую проблему. После устранения ошибки неограниченной длины запрос может вызвать падение сеанса или ошибку переполнения памяти. Причина заключается в ошибочном условии соединения по знаку «не равно» (<>):


// НЕПРАВИЛЬНО: Декартово произведение (перемножение всех строк на все)
ЛЕВОЕ СОЕДИНЕНИЕ таблицаВТ КАК таблицаВТ
ПО таблица2вт.артикул <> таблицаВТ.артикул
    И таблица2вт.размер <> таблицаВТ.размер

При соединении по условию <> система пытается сопоставить каждую строку первой таблицы со всеми несовпадающими строками второй таблицы. Если в обеих таблицах содержится по несколько тысяч строк, результат соединения увеличивается до миллионов записей. Это вызывает исчерпание оперативной памяти, особенно в файловом режиме работы платформы.

Для поиска различий между двумя таблицами значений необходимо использовать равенство ключей и тип соединения ПОЛНОЕ СОЕДИНЕНИЕ с отбором по пустой ссылке (ЕСТЬ NULL).

Решение 2. Корректный запрос для сравнения двух таблиц

Рассмотрим правильную структуру запроса для поиска расхождений между двумя коллекциями:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВЫРАЗИТЬ(Т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. Всегда явно задавайте типы полей при создании колонок в ТаблицаЗначений или используйте функцию ВЫРАЗИТЬ() в запросе.
  2. Соединяйте временные таблицы исключительно по равенству ключей (знак =).
  3. Для ускорения работы больших таблиц добавляйте индексацию по сопоставляемым полям: ПОМЕСТИТЬ ВТ_Таблица1 ИНДЕКСИРОВАТЬ ПО артикул, размер.
  4. Используйте ПОЛНОЕ СОЕДИНЕНИЕ с отбором по ЕСТЬ NULL для нахождения строк, которые присутствуют только в одной из таблиц.
← На главную