Как по запросу быстро получить пустую Таблицу Значений с описанием колонок и правильно отсеять битые ссылки в 1С

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

В процессе разработки в среде «1С:Предприятие 8» программисты часто сталкиваются с задачей, когда структуру будущей таблицы значений (ТЗ) хочется получить автоматически на основе текста запроса, не описывая вручную каждую колонку и типы данных через громоздкий объект ОписаниеТипов. Ситуация усложняется, если в исходных данных присутствуют «битые ссылки» (несуществующие элементы базы данных, на которые есть ссылки в документах или регистрах). Попытка выгрузить результат запроса напрямую и отсеять «мусор» при обходе выборки программно — частая, но не самая оптимальная практика.

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

Часть 1. Как получить пустую таблицу значений с описанием типов по запросу

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

Способ 1. Использование метода Выгрузить().Очистить() или СкопироватьКолонки()

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


РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультата = РезультатЗапроса.Выгрузить();
ТаблицаРезультата.Очистить(); // Получаем пустую таблицу с колонками

Или аналогичный вариант с копированием структуры:


РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультата = РезультатЗапроса.Выгрузить().СкопироватьКолонки();

В чем проблема этого подхода? Если ваш запрос выбирает десятки или сотни тысяч строк, то метод Выполнить().Выгрузить() заставит платформу поднять все эти данные из СУБД в оперативную память сервера приложений, сформировать огромную таблицу и только после этого вы очистите её. Это колоссальная и абсолютно бессмысленная трата ресурсов сервера и сети.

Способ 2. Оптимальное решение — конструкция «ГДЕ ЛОЖЬ»

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Номенклатура,
    |	Номенклатура.СтавкаНДС КАК СтавкаНДС,
    |	ВЫРАЗИТЬ(0 КАК ЧИСЛО(15, 2)) КАК Сумма
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ ЛОЖЬ"; // Или ГДЕ 1 = 0

ПустаяТЗ = Запрос.Выполнить().Выгрузить();

В данном случае СУБД мгновенно вернет пустую выборку строк, но платформа 1С на её основе создаст полноценный объект ТаблицаЗначений. Колонки будут иметь строго определенные типы (включая составные, если они были описаны в метаданных полей запроса). Вы получите идеальный каркас для дальнейшего заполнения без какой-либо нагрузки на базу данных.

Способ 3. Конструкция «ВЫБРАТЬ ПЕРВЫЕ 0»

В качестве альтернативы условию ГДЕ ЛОЖЬ можно использовать ограничение количества выбираемых записей в самом начале пакета запроса:


Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 0
    |	Склады.Ссылка КАК Склад,
    |	Склады.Код КАК Код
    |ИЗ
    |	Справочник.Склады КАК Склады";

ПустаяТЗ = Запрос.Выполнить().Выгрузить();

Этот вариант работает точно так же быстро и возвращает пустую таблицу со структурой колонок исходной таблицы метаданных.

Способ 4. Создание копии структуры уже заполненной таблицы значений

Если у вас в коде уже есть заполненная таблица значений, и вам нужно быстро получить ее пустую копию с аналогичными колонками и типами данных, не стоит обходить её строки или использовать метод Очистить(). Достаточно использовать метод Скопировать() с передачей пустого массива строк:

ПустаяТаблица = ИсходнаяТаблица.Скопировать(Новый Массив());

Платформа мгновенно создаст новую пустую таблицу значений, унаследовав структуру колонок и их типы данных от исходного объекта.

---

Часть 2. Правильная фильтрация битых ссылок

Битые ссылки (или объектные ссылки на несуществующие записи) появляются в информационной базе вследствие некорректного удаления данных без контроля ссылочной целостности (например, при программном удалении через метод Удалить() вместо штатного интерактивного удаления с пометкой). Фильтровать их при обходе выборки в коде — не самое удачное архитектурное решение. Посмотрим, как решить эту задачу правильно.

Очистка от битых ссылок прямо в запросе

Самым эффективным способом является отсеивание невалидных данных еще на уровне СУБД, чтобы они даже не передавались в код приложения.

Вариант 1. Соединение со справочником (Классический подход)

Мы можем связать левым соединением таблицу, содержащую проверяемые поля, с таблицей-источником этих ссылок. Если ссылка битая, то в поле соединения вернется значение NULL.


ВЫБРАТЬ
    Продажи.ДокументОснование КАК ДокументОснование
ИЗ
    РегистрНакопления.Продажи КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
    ПО Продажи.ДокументОснование = Реализация.Ссылка
ГДЕ
    НЕ Продажи.ДокументОснование = ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка)
    И Реализация.Ссылка ЕСТЬ NULL

В данном примере условие Реализация.Ссылка ЕСТЬ NULL отберет исключительно «битые» ссылки. Если же нам, наоборот, нужно получить только «живые» (существующие) ссылки, условие изменится на И НЕ Реализация.Ссылка ЕСТЬ NULL.

Вариант 2. Неявное разыменование через системные поля (Рекомендуемый быстрый способ)

Чтобы не писать громоздкие левые соединения для каждого поля, в платформе 1С можно использовать механизм неявного обращения через точку к реквизитам ссылочного поля. На уровне СУБД платформа автоматически построит оптимальное левое соединение.

Для проверки существования ссылки идеально подходит системное поле ВерсияДанных. Данное поле гарантированно заполнено у любого существующего физического объекта в базе данных и всегда принимает значение NULL, если объект был физически удален.

Выясним, как выглядит запрос для выбора только существующих (живых) объектов:


ВЫБРАТЬ
    Таблица.ДокументСсылка КАК ДокументСсылка
ИЗ
    Документ.ЗаказПокупателя КАК Таблица
ГДЕ
    НЕ Таблица.ДокументСсылка.ВерсияДанных ЕСТЬ NULL

А если нам требуется найти в базе данных именно битые ссылки, то запрос будет выглядеть так:


ВЫБРАТЬ
    Таблица.ДокументСсылка КАК ДокументСсылка
ИЗ
    Документ.ЗаказПокупателя КАК Таблица
ГДЕ
    Таблица.ДокументСсылка.ВерсияДанных ЕСТЬ NULL
    И НЕ Таблица.ДокументСсылка = ЗНАЧЕНИЕ(Документ.ИмяДокумента.ПустаяСсылка)

Этот способ лаконичен, легко читается и работает максимально быстро на уровне SQL-запроса.

---

Часть 3. Как правильно проверять ссылки на «битость» в коде

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

Как делать НЕЛЬЗЯ (плохой тон и потеря производительности):

Как делать ПРАВИЛЬНО:

Если в вашей конфигурации используется Библиотека Стандартных Подсистем (БСП), то самым правильным и быстрым решением будет использование встроенного метода общего модуля БСП:


// Функция возвращает Булево: Истина, если ссылка существует в базе данных
Если ОбщегоНазначения.СсылкаСуществует(ПроверяемаяСсылка) Тогда
    // Обрабатываем живой объект
Иначе
    // Ссылка является битой
КонецЕсли;

Внутри этой функции БСП выполняет сверхлегкий точечный запрос к таблице базы данных вида ВЫБРАТЬ Ссылка ИЗ ... ГДЕ Ссылка = &Ссылка. Такой запрос отрабатывает за миллисекунды и не нагружает сервер приложений.

Если же БСП в вашей системе отсутствует, вы можете написать аналогичную простую функцию самостоятельно в общем модуле проекта:


Функция СсылкаСуществует(Ссылка) Экспорт
    
    Если Не ЗначениеЗаполнено(Ссылка) Тогда
        Возврат Ложь;
    КонецЕсли;
    
    ИмяМетаданных = Ссылка.Метаданные().Имя;
    ПолноеИмяМетаданных = Ссылка.Метаданные().ПолноеИмя();
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	Т.Ссылка КАК Ссылка
        |ИЗ
        |	" + ПолноеИмяМетаданных + " КАК Т
        |ГДЕ
        |	Т.Ссылка = &Ссылка";
        
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Возврат Не Запрос.Выполнить().Пустой();
    
КонецФункции

Используйте описанные методики для проектирования надежных и быстродействующих алгоритмов в своих конфигурациях на базе платформы «1С:Предприятие 8»!

← На главную