Как получить результат хранимой процедуры с параметрами через Внешний источник данных

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

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

Анализ проблемы: почему EXEC не работает в ВИД

Рассмотрим стандартную ситуацию: в сторонней базе данных (например, MS SQL Server) есть процедура dbo.report_on, которая принимает идентификатор и диапазон дат, выполняет сложную логику с использованием временных таблиц и возвращает итоговый набор записей. Попытка прописать в свойстве Выражение таблицы ВИД конструкцию вида EXEC dbo.report_on &1, &2, &3 неизбежно приведет к ошибке.

Выясним причину такого поведения. Платформа 1С при выполнении запроса к внешнему источнику не просто транслирует ваш текст «как есть». Она оборачивает содержимое поля Выражение в собственный подзапрос. На стороне SQL это выглядит примерно так:


SELECT T1._Field1, T1._Field2 FROM (EXEC dbo.report_on 31, '20260101', '20260201') AS T1

Согласно спецификации T-SQL, инструкция EXEC не может быть частью подзапроса в блоке FROM. Это фундаментальное ограничение синтаксиса SQL Server, которое делает невозможным прямой вызов процедур через стандартный механизм ВИД. Поэтому любые попытки «поиграть» со скобками или кавычками в данном контексте не принесут результата. При необходимости обойти это ограничение можно использовать примеры прямого подключения и получения данных из SQL в 1С.

Основное решение: использование табличных функций (TVF)

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

Рассмотрим подробнее процесс создания такой функции-обертки. Если логика процедуры проста, используется Inline TVF. Если же внутри присутствуют циклы и временные таблицы (как в исходном примере автора темы), необходимо использовать Multi-statement TVF.

Пример структуры функции на стороне SQL Server:


CREATE FUNCTION dbo.fn_GetReportOn (@ID INT, @DateStart DATETIME, @DateEnd DATETIME)
RETURNS @ResultTable TABLE (
    [Category] NVARCHAR(600),
    [Month] NVARCHAR(50),
    [Value] FLOAT
)
AS
BEGIN
    -- Здесь размещается логика вашей процедуры
    -- Вместо SELECT в конце делается INSERT в @ResultTable
    INSERT INTO @ResultTable
    SELECT Category, Month, Value FROM #FinalReport;
    
    RETURN;
END

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

dbo.fn_GetReportOn(&1, &2, &3)

Важно помнить: 1С рекомендует заключать всё выражение в дополнительные скобки (dbo.fn_GetReportOn(&1, &2, &3)), чтобы минимизировать конфликты при генерации итогового SQL-запроса платформой.

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

Проанализируем ситуацию, упомянутую в обсуждении: админ СУБД может заметить существенное замедление (в примере автора — с 4 минут до 36 минут) при переходе от процедуры к функции. Это происходит потому, что для Multi-statement TVF оптимизатор SQL Server часто не может заранее построить эффективный план запроса, так как не знает объема возвращаемых данных (оценка Cardinality Estimation).

Как мы можем это оптимизировать:

  1. Использование переменных таблиц вместо временных: Внутри функции нельзя использовать #temp таблицы, только табличные переменные @table.
  2. Обновление версии SQL Server: Начиная с SQL Server 2017, появилась функция Interleaved Execution, которая позволяет динамически оценивать количество строк в табличных функциях во время компиляции запроса.
  3. Оптимизация логики: Постарайтесь избавиться от циклов WHILE внутри функции в пользу рекурсивных CTE или соединений таблиц, если это возможно.

Альтернативный вариант: Использование OPENQUERY

Если администраторы БД категорически запрещают создавать функции, существует «хакерский» способ вызова процедуры через OPENQUERY. Этот метод использует механизм связанных серверов (Linked Servers). Вы можете создать связанный сервер, указывающий на самого себя (Loopback), и вызывать процедуру через него.

В поле Выражение в 1С это будет выглядеть так:

SELECT * FROM OPENQUERY(LOCALSERVER, 'EXEC db.dbo.report_on 31, ''20260101'', ''20260201''')

Однако у этого метода есть огромный минус: крайне сложно динамически передавать параметры &1, &2 внутрь строки запроса OPENQUERY. Этот способ подходит только для вызовов со статичными параметрами или при использовании очень сложных оберток на стороне SQL.

Тонкости настройки параметров в 1С

При использовании параметров &1, &2 в ВИД, платформа 1С автоматически сопоставляет их с типами данных полей, которые вы описали в таблице внешнего источника. Проанализируем типичные ошибки:

  1. Несоответствие типов: Если в SQL функция ожидает DATETIME, а 1С передает Строку (или наоборот), запрос упадет с ошибкой преобразования типов. Убедитесь, что в свойствах параметров в 1С указаны верные типы данных.
  2. Порядок параметров: Индексы &1, &2 и так далее соответствуют порядку параметров, передаваемых в запрос. При использовании СКД (Системы компоновки данных) платформа сама подставит значения из параметров отчета в эти позиции.
  3. Метаданные: Если процедура использует сложную логику, 1С может не «увидеть» структуру возвращаемых полей автоматически. В этом случае поля в таблице ВИД в конфигураторе придется описать вручную, точно соблюдая имена и типы данных SQL.

Когда стоит отказаться от ВИД в пользу ADO или HTTP

Если объем данных превышает сотни тысяч строк, а производительность табличной функции неудовлетворительна, рассмотрим альтернативные пути:

  1. Прямое соединение через ADODB: Старый, но надежный способ. Позволяет выполнить EXEC напрямую и получить Recordset. Минус — привязка к Windows и необходимость ручного разбора данных в таблицу значений 1С.
  2. Промежуточная таблица: Процедура на стороне SQL по расписанию (SQL Job) записывает данные в обычную физическую таблицу, которую 1С просто читает через ВИД без всяких «выражений». Это самый быстрый способ с точки зрения 1С.
  3. Микросервис на Go/Python: Как предлагалось в сообщении №7, можно написать небольшую утилиту, которая запрашивает данные из SQL, формирует JSON и отдает его 1С через HTTPСоединение. Платформа 1С очень быстро парсит JSON, и такой подход часто оказывается производительнее тяжелых внешних источников.

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

← На главную