Почему прямой запрос из 1С к MS SQL через ADO возвращает пустой результат и как это исправить?

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

При интеграции 1С с внешними базами данных Microsoft SQL Server разработчики нередко сталкиваются с ситуацией, когда один и тот же SQL-запрос успешно возвращает строки в среде SQL Server Management Studio (SSMS), но при выполнении через COM-объект ADODB.Connection из кода 1С возвращает пустую выборку без каких-либо ошибок. Разберем по шагам причины такого поведения и рассмотрим надежные способы получения данных.

1. Разграничение прав доступа и особенности представлений (VIEW)

Проанализируем основную причину несовпадения результатов в SSMS и 1С. В консоли управления запрос чаще всего выполняется под учетной записью администратора (например, sa) или с использованием доменной аутентификации Windows. В коде 1С подключение обычно настраивается под выделенным техническим пользователем SQL Server с ограниченными привилегиями.

Обратим внимание на следующие критические факторы безопасности СУБД:

  1. Логика фильтрации внутри представлений (RLS и контекст пользователя): если представление (VIEW) использует системные функции (такие как SUSER_SNAME(), USER_NAME()) или политики безопасности строк (Row-Level Security), SQL Server динамически фильтрует строки. Ограниченный пользователь 1С не увидит записей, тогда как администратор в SSMS получит полный список.
  2. Разрыв цепочки владения (Broken Ownership Chaining): если представление и базовые таблицы принадлежат разным схемам или пользователям, права не наследуются автоматически. Пользователю недостаточно иметь право SELECT на представление — необходимо явно выдать права на чтение всех связанных таблиц.
  3. Разрешения на уровне колонок и таблиц: проверим права созданного пользователя SQL на каждый объект, задействованный в операторах JOIN. При отсутствии прав на одну из связываемых таблиц запрос может отсекать все строки.

2. Настройка параметров сеанса и служебные сообщения (SET NOCOUNT ON)

При выполнении команд через ADO на стороне SQL Server могут генерироваться служебные сообщения о количестве обработанных строк (DONE_IN_PROC). Это происходит при наличии триггеров или пакетных инструкций. В результате объект ADODB.Recordset принимает первым не набор данных с результатом выборки, а пустой служебный маркер.

Чтобы исключить влияние служебных пакетов, всегда добавляем директиву SET NOCOUNT ON; в самое начало текста запроса перед оператором SELECT.

3. Корректная инициализация RecordSet и настройка курсора

Выясним, как правильно инициализировать набор данных. По умолчанию при выполнении через метод Execute() объекта ADODB.Command или ADODB.Connection создается однонаправленный курсор (forward-only), который в ряде конфигураций драйверов может вести себя некорректно при попытке позиционирования. Рекомендуется явно создавать ADODB.RecordSet и задавать статический курсор CursorType = 3 (adOpenStatic).

Посмотрим на пример универсальной функции для подключения к MS SQL Server, выполнения запроса и выгрузки результата в ТаблицаЗначений:


Функция ВыполнитьПрямойЗапросSQL(Сервер, База, Пользователь, Пароль, ТекстЗапроса) Экспорт

    // 1. Инициализируем соединение
    Подключение = Новый COMОбъект("ADODB.Connection");
    Подключение.Provider = "SQLOLEDB.1";
    Подключение.Properties("Data Source").Value = СокрЛП(Сервер);
    Подключение.Properties("Initial Catalog").Value = СокрЛП(База);
    Подключение.Properties("User ID").Value = СокрЛП(Пользователь);
    Подключение.Properties("Password").Value = СокрЛП(Пароль);
    
    Попытка
        Подключение.Open();
    Исключение
        ВызватьИсключение "Ошибка подключения к MS SQL: " + ОписаниеОшибки();
    КонецПопытки;

    // 2. Настраиваем и открываем выборку
    ЗаписиSQL = Новый COMОбъект("ADODB.RecordSet");
    ЗаписиSQL.CursorType = 3; // adOpenStatic
    
    // Добавляем SET NOCOUNT ON для исключения пустых служебных пакетов
    ТекстSQL = "SET NOCOUNT ON; " + ТекстЗапроса;
    
    Попытка
        ЗаписиSQL.Open(ТекстSQL, Подключение);
    Исключение
        Подключение.Close();
        ВызватьИсключение "Ошибка выполнения SQL-запроса: " + ОписаниеОшибки();
    КонецПопытки;

    // 3. Формируем структуру результирующей ТаблицыЗначений
    Таблица = Новый ТаблицаЗначений;
    КоличествоКолонок = ЗаписиSQL.Fields.Count - 1;
    
    Для НомерПоля = 0 По КоличествоКолонок Цикл
        ПолеSQL = ЗаписиSQL.Fields.Item(НомерПоля);
        Таблица.Колонки.Добавить(ПолеSQL.Name);
    КонецЦикла;

    // 4. Заполняем строки
    Пока НЕ ЗаписиSQL.EOF Цикл
        НоваяСтрока = Таблица.Добавить();
        Для НомерПоля = 0 По КоличествоКолонок Цикл
            НоваяСтрока[НомерПоля] = ЗаписиSQL.Fields.Item(НомерПоля).Value;
        КонецЦикла;
        ЗаписиSQL.MoveNext();
    КонецЦикла;

    // 5. Закрываем соединения
    ЗаписиSQL.Close();
    Подключение.Close();

    Возврат Таблица;

КонецФункции

4. Типичные ошибки при передаче дат и обходе набора записей

Рассмотрим распространенные детали реализации, приводящие к скрытым ошибкам:

  1. Проверка признака конца данных: свойство EOF объекта Recordset возвращает булево значение. В условиях циклов 1С следует писать Пока НЕ ЗаписиSQL.EOF Цикл, а не сравнивать его с числом 0.
  2. Форматирование дат: при склейке параметров дат внутри строки SQL-запроса необходимо использовать канонический неразрывный строковый формат стандарта ISO YYYYMMDD или YYYY-MM-DDTHH:MM:SS. Это предотвращает искажение условий фильтрации из-за различий в региональных настройках (Language/DateFormat) сеанса SQL-сервера.
  3. Разрядность компонентов (Bitness x86/x64): при исполнении кода на стороне сервера 1С COM-объект создается в 64-битном процессе службы ragent. Убедимся, что на сервере установлены соответствующие 64-битные OLE DB провайдеры (например, MSOLEDBSQL) или ODBC-драйверы.
← На главную