При разработке механизмов заполнения таблиц данных программисты часто сталкиваются с ситуацией, когда после выполнения кода во всех строках таблицы оказываются идентичные значения. Эта проблема особенно актуальна при работе с регистрами сведений и внешними источниками данных. В данной статье мы подробно разберем, почему это происходит, как исправить логику работы запроса и как оптимизировать код для повышения производительности системы.
Рассмотрим типичную ситуацию, представленную в вопросе автора. В исходном коде запрос выполнялся внутри цикла Для каждого СтрокаТаблицы Из Таблица Цикл. Однако внутри самого текста запроса отсутствовало условие связи с конкретной строкой (отсутствовал параметр ГДЕ). В результате на каждой итерации цикла запрос выбирал все записи из регистра, группировал их по ИНН, а последующий цикл Пока Выборка.Следующий() просто перезаписывал данные в текущей строке таблицы значениями из последней записи выборки.
Проанализируем основные причины возникновения этой ошибки:
Пока Выборка.Следующий() внутри цикла по строкам таблицы без четкого соответствия приводит к тому, что в строку записывается результат последнего шага итерации выборки.Если необходимо быстро исправить код, сохраняя структуру автора (хотя это и не оптимально с точки зрения скорости), нужно добавить в запрос условие ГДЕ и передать в него параметр из текущей строки таблицы. Разберем исправленный пример:
Для каждого СтрокаТаблицы Из Таблица Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(СводкаОбращений.ИНН) КАК ИННКоличество,
| МИНИМУМ(СводкаОбращений.Дата) КАК ДатаМинимум,
| МАКСИМУМ(СводкаОбращений.Дата) КАК ДатаМаксимум
|ИЗ
| РегистрСведений.СводкаОбращений КАК СводкаОбращений
|ГДЕ
| СводкаОбращений.ИНН = &ИНН";
Запрос.УстановитьПараметр("ИНН", СтрокаТаблицы.ИННучреждения);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Используем Если вместо Пока, так как ожидаем одну сгруппированную строку
Если Выборка.Следующий() Тогда
СтрокаТаблицы.ДатаПервогоОбращенияГЛ = Выборка.ДатаМинимум;
СтрокаТаблицы.ДатаПоследнегоОбращенияГЛ = Выборка.ДатаМаксимум;
СтрокаТаблицы.ОбщееКоличествоОбращений = Выборка.ИННКоличество;
КонецЕсли;
КонецЦикла;
В данном случае мы добавили Запрос.УстановитьПараметр("ИНН", СтрокаТаблицы.ИННучреждения). Теперь на каждом шаге цикла запрос будет искать данные только для конкретной организации. Проблема "одинаковых данных" исчезнет, но проблема производительности останется.
Правильный подход в 1С — это принцип "один запрос для всех данных". Вместо того чтобы мучить базу данных сотнями мелких запросов, мы соберем все нужные ИНН в массив и получим результат одним махом. Рассмотрим, как это сделать правильно.
Выполним следующие шаги:
В (&МассивИНН).Соответствие для быстрого поиска.
// 1. Собираем уникальные ИНН
МассивИНН = Таблица.ВыгрузитьКолонку("ИННучреждения");
// 2. Создаем один запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СводкаОбращений.ИНН КАК ИНН,
| КОЛИЧЕСТВО(СводкаОбращений.ИНН) КАК ИННКоличество,
| МИНИМУМ(СводкаОбращений.Дата) КАК ДатаМинимум,
| МАКСИМУМ(СводкаОбращений.Дата) КАК ДатаМаксимум
|ИЗ
| РегистрСведений.СводкаОбращений КАК СводкаОбращений
|ГДЕ
| СводкаОбращений.ИНН В (&МассивИНН)
|СГРУППИРОВАТЬ ПО
| СводкаОбращений.ИНН";
Запрос.УстановитьПараметр("МассивИНН", МассивИНН);
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
// 3. Индексируем для быстрого поиска (или используем Соответствие)
ТаблицаРезультатов.Индексы.Добавить("ИНН");
// 4. Заполняем исходную таблицу
Для каждого СтрокаТаблицы Из Таблица Цикл
ПараметрыПоиска = Новый Структура("ИНН", СтрокаТаблицы.ИННучреждения);
НайденныеСтроки = ТаблицаРезультатов.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
ТекРезультат = НайденныеСтроки[0];
СтрокаТаблицы.ДатаПервогоОбращенияГЛ = ТекРезультат.ДатаМинимум;
СтрокаТаблицы.ДатаПоследнегоОбращенияГЛ = ТекРезультат.ДатаМаксимум;
СтрокаТаблицы.ОбщееКоличествоОбращений = ТекРезультат.ИННКоличество;
КонецЕсли;
КонецЦикла;
Часто бывает так: параметр установлен, данные в регистре точно есть, но запрос возвращает пустую выборку. Проанализируем причины, связанные с особенностями хранения строк в 1С:
1. Лишние пробелы и фиксированная длина.
Если поле ИНН в конфигурации имеет тип Строка с фиксированной длиной (например, 12), то ИНН длиной 10 символов ("7701010101") будет храниться как "7701010101 " (с двумя пробелами в конце). При обычном сравнении = СУБД может посчитать их разными. Рекомендуется использовать очистку пробелов перед передачей в запрос: СокрЛП(СтрокаТаблицы.ИННучреждения).
2. Использование оператора ПОДОБНО.
Если вы подозреваете наличие мусорных символов в конце строки, можно использовать конструкцию с маской:
Запрос.Текст = "... ГДЕ СводкаОбращений.ИНН ПОДОБНО &ИНН";
Запрос.УстановитьПараметр("ИНН", СокрЛП(СтрокаТаблицы.ИННучреждения) + "%");
3. Неразрывные пробелы.
При копировании данных из Excel или PDF в строку часто попадает "неразрывный пробел" (код символа 160). Визуально он не отличается от обычного, но СокрЛП его не удаляет. В таких случаях нужно использовать СтрЗаменить(СтрокаИНН, Символы.НПП, "").
4. Обработка NULL.
При использовании соединений (особенно ЛЕВОЕ СОЕДИНЕНИЕ) агрегатные функции могут возвращать NULL, если данных не найдено. Обязательно проверяйте результаты функцией ЕСТЬNULL в самом тексте запроса, чтобы избежать пустых ячеек в интерфейсе:
ЕСТЬNULL(КОЛИЧЕСТВО(СводкаОбращений.ИНН), 0) КАК ИННКоличество
Для корректного заполнения таблиц в 1С важно не только правильно передавать параметры в запрос, но и учитывать специфику строковых типов данных. Всегда стремитесь выносить запросы за пределы циклов — это залог стабильной и быстрой работы программы. Если данные все равно не находятся, проверьте типы реквизитов в конфигураторе и наличие скрытых символов в значениях ИНН.