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

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

Понимание принципов работы с Временными таблицами является ключевым навыком для любого программиста 1С. Правильное использование операторов ПОМЕСТИТЬ и ВЫБРАТЬ позволяет избежать дублирования кода и ускорить выполнение отчетов в десятки раз. Мы рассмотрим не только базовый синтаксис, но и нюансы сравнения по составу колонок, а также работу с неиндексируемыми полями.

Подготовка структур данных для анализа

Прежде чем писать сам запрос, необходимо убедиться, что обе сравниваемые таблицы имеют идентичную структуру колонок. Если в одной таблице есть поле "Артикул", а в другой его нет, или типы данных не совпадают (например, Число против Строки), выполнение запроса завершится ошибкой. Программа не сможет неявно привести типы в контексте временной таблицы без явного указания.

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

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

ТЗ_Старая = Новый ТаблицаЗначений();

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

ТЗ_Старая.Колонки.Добавить("Количество", Тип("Число(15,3)"));

ТЗ_Новая = Новый ТаблицаЗначений();

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

ТЗ_Новая.Колонки.Добавить("Количество", Тип("Число(15,3)"));

После заполнения данных необходимо передать эти переменные в объект Запрос. Это делается через метод УстановитьПараметр, где имя параметра в тексте запроса должно совпадать с ключом в коллекции параметров. Без этого шага движок 1С просто не увидит ваши данные внутри запроса.

💡

Всегда явно указывайте длину строк и точность чисел при создании колонок Таблицы Значений, чтобы избежать проблем с сравнением "10" и "10.000".

Использование временных таблиц в запросе

Основной механизм сравнения двух наборов данных в 1С строится на создании временных таблиц в памяти (или на стороне сервера СУБД, если используется файловая или клиент-серверная версия с соответствующими настройками). Оператор ПОМЕСТИТЬ позволяет скопировать содержимое параметра-таблицы значений во временное хранилище с уникальным именем.

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

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

Запрос = Новый Запрос;

Запрос.Текст =

"ПОМЕСТИТЬ ТЗ_Источник

|ВЫБРАТЬ *

|ИЗ &Таблица1 КАК Таблица1

|;

|

|ПОМЕСТИТЬ ТЗ_Сравнение

|ВЫБРАТЬ *

|ИЗ &Таблица2 КАК Таблица2

|;

|

|ВЫБРАТЬ *

|ИЗ ТЗ_Источник КАК Ист

| ЛЕВОЕ СОЕДИНЕНИЕ ТЗ_Сравнение КАК Сравни

| ПО Ист.Номенклатура = Сравни.Номенклатура";

Запрос.УстановитьПараметр("Таблица1", ТЗ_Старая);

Запрос.УстановитьПараметр("Таблица2", ТЗ_Новая);

Обратите внимание на использование символа | для разделения строк в многострочном тексте запроса. Это стандартная практика в 1С, позволяющая делать код читаемым. Игнорирование этого правила приведет к синтаксической ошибке при компиляции текста запроса.

☑️ Алгоритм работы с временными таблицами

Выполнено: 0 / 4

Оператор ИСКЛЮЧЕНИЕ для поиска различий

Самый эффективный способ найти строки, которые присутствуют в одной таблице, но отсутствуют в другой — использование оператора ИСКЛЮЧЕНИЕ (EXCEPT в терминологии SQL). Этот оператор автоматически сравнивает все поля выбранных записей и возвращает только те строки из первого набора, которых нет во втором.

При использовании ИСКЛЮЧЕНИЕ необходимо следить за тем, чтобы списки выбранных полей в обеих частях запроса были идентичны по количеству, порядку и типам данных. Если вы выберете в первой части три поля, а во второй четыре, система выдаст ошибку несоответствия структуры результата.

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

⚠️ Внимание: Оператор ИСКЛЮЧЕНИЕ чувствителен к значениям NULL (Неопределено). Если в сравниваемых колонках есть пустые значения, они могут быть обработаны некорректно в зависимости от версии платформы и настроек СУБД.

Пример запроса с использованием исключения для поиска новых позиций:

ТекстЗапроса = 

"ВЫБРАТЬ

| Новая.Номенклатура,

| Новая.Количество

|ИЗ ТЗ_Новая КАК Новая

|ИСКЛЮЧЕНИЕ

|ВЫБРАТЬ

| Старая.Номенклатура,

| Старая.Количество

|ИЗ ТЗ_Старая КАК Старая";

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

📊 Какой метод сравнения вы используете чаще?
Циклы в коде
Оператор ИСКЛЮЧЕНИЕ
ЛЕВОЕ СОЕДИНЕНИЕ
Внешние обработки

Сравнение через ЛЕВОЕ СОЕДИНЕНИЕ и анализ NULL

В случаях, когда оператор ИСКЛЮЧЕНИЕ не подходит (например, нужно сравнить только по ключевому полю, игнорируя остальные, или нужно получить детализацию изменений), используется классическое ЛЕВОЕ СОЕДИНЕНИЕ. Этот метод позволяет сопоставить строки двух таблиц по условию и увидеть, какие из них не нашли пару.

Суть метода заключается в том, что мы соединяем первую таблицу со второй по ключу (например, по ссылке на номенклатуру). Если для строки из левой таблицы не нашлось соответствия в правой, то поля правой таблицы в результате будут заполнены значением NULL (или Неопределено в 1С).

Фильтрация результата по условию ГДЕ Сравни.Номенклатура ЕСТЬ NULL позволяет выделить записи, которые есть в источнике, но отсутствуют в сравнении. Это аналог поиска удаленных записей, но с возможностью захватить дополнительные поля из левой таблицы.

Тип соединения Описание поведения Результат при отсутствии пары
ЛЕВОЕ СОЕДИНЕНИЕ Все строки из левой таблицы + совпадения из правой NULL в полях правой таблицы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Только строки, где есть совпадение в обеих таблицах Строка исключается из выборки
ПОЛНОЕ СОЕДИНЕНИЕ Все строки из обеих таблиц NULL в полях той таблицы, где нет совпадения

Использование соединения дает большую гибкость. Вы можете сравнивать таблицы не по полному совпадению всех полей, а только по идентификатору, а затем в условии ГДЕ проверять различия в конкретных числовых полях, например, Ист.Количество <> Сравни.Количество.

Нюансы работы с NULL в соединениях

При использовании ЛЕВОЕ СОЕДИНЕНИЕ помните, что условие ГДЕ Сравни.Поле ЕСТЬ NULL сработает только если в правой таблице действительно нет совпадений. Если там есть запись со значением NULL, логика может нарушиться.

Оптимизация производительности при больших объемах

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

Для создания индекса используется модификатор ИНДЕКСИРОВАТЬ ПО сразу после имени временной таблицы в операторе ПОМЕСТИТЬ. Это instructs движок 1С построить структуру поиска по указанным полям перед выполнением основных операций выборки.

Не стоит индексировать все поля подряд. Индексы имеют стоимость создания. Оптимально создавать их только по тем полям, которые участвуют в условиях ПО (для соединений) или в ГДЕ. Поля, используемые только для вывода в результат, индексировать не нужно.

Запрос.Текст = 

"ПОМЕСТИТЬ ТЗ_Данные

|ИНДЕКСИРОВАТЬ ПО Номенклатура, Склад

|ВЫБРАТЬ *

|ИЗ &ТаблицаИсточник";

Также стоит избегать использования функций в условиях соединения, таких как ВЫБОР или преобразование типов прямо в тексте запроса ГДЕ. Это может привести к полному сканированию таблицы (Table Scan) вместо быстрого поиска по индексу (Index Seek).

💡

Индексация временных таблиц по полям соединения может ускорить выполнение запроса в 10-100 раз на больших выборках.

Обработка результатов и типовые ошибки

После выполнения запроса результат помещается в объект ТаблицаЗначений через метод Выгрузить(). На этом этапе важно проверить, не потерялись ли типы данных. Иногда, особенно при использовании сложных выражений ВЫБОР, платформа может привести тип поля к более общему, что потребует дополнительной обработки в коде.

Типичной ошибкой является попытка сравнить таблицы с разным набором колонок без явного указания псевдонимов или списка полей. Запрос требует строгой дисциплины: если вы делаете ВЫБРАТЬ *, убедитесь, что порядок колонок в источниках одинаков, иначе данные попадут не в те ячейки.

Еще одна распространенная проблема — работа с неуникальными ключами. Если в таблице значений есть дубли строк, операторы множества (как ИСКЛЮЧЕНИЕ) могут вести себя непредсказуемо с точки зрения бизнес-логики, удаляя все дубликаты. В таких случаях лучше использовать группировку СГРУППИРОВАТЬ ПО перед сравнением.

⚠️ Внимание: При сравнении строк учитывайте регистр! В некоторых конфигурациях и СУБД строки "Товар" и "товар" считаются разными. Используйте функцию СТРОКА() или настройки сравнения, если регистр не важен.

Для отладки сложных запросов удобно выводить полученную таблицу значений в форму или консоль отладки, чтобы визуально убедиться в корректности отфильтрованных данных. Не полагайтесь слепо на логику, всегда проверяйте результат на тестовых данных.

Сравнение дат и времени

Помните, что тип Дата включает в себя время. Дата "01.01.2026 00:00:00" не равна дате "01.01.2026 12:30:00". Используйте функцию НАЧАЛОДНЯ() для игнорирования времени при сравнении.

Часто задаваемые вопросы (FAQ)

Можно ли сравнивать таблицы значений без использования запросов?

Да, это можно сделать через циклы Для каждого и поиск строк методом Найти(). Однако такой подход имеет квадратичную сложность O(N*M) и будет крайне медленным на больших объемах данных (тысячи строк). Запрос работает линейно или логарифмически благодаря индексации.

Что делать, если структуры таблиц не совпадают?

В тексте запроса необходимо явно перечислить поля для выборки, приводя их к общему виду. Например, если в одной таблице нет поля "Комментарий", можно выбрать псевдо-поле "" КАК Комментарий, чтобы выровнять структуру перед операцией сравнения.

Как найти строки, которые изменились (были и там, и там, но с разными значениями)?

Используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ по ключевому полю, а в условии ГДЕ укажите неравенство для остальных полей: ГДЕ ТЗ1.Количество <> ТЗ2.Количество. Это отберет только те записи, где ключ совпал, но данные изменились.

Работает ли этот метод в управляемых формах?

Да, полностью. Объекты Запрос и ТаблицаЗначений являются общими для обычного и управляемого приложения. Единственное отличие — в управляемых формах нельзя напрямую выводить таблицу значений в поле формы без использования ТаблицыЗначений как источника данных для Таблицы или Дерева.

Как очистить временные таблицы?

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