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

Таблица в SQL базе данных
Текст процедуры запроса
Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 | dbo_tbl_Account.Name КАК Наименование, | dbo_tbl_Account.INN КАК ИНН, | dbo_tbl_Account.KPP КАК КПП, | dbo_tbl_Account.ID КАК ID, | dbo_tbl_Account.PrimaryContactID КАК PrimaryContactID, | dbo_tbl_Account.GlobalSourceID КАК GlobalSourceID, | dbo_tbl_Account.SourceID КАК SourceID, | dbo_tbl_Account.ManagerID КАК ManagerID, | dbo_tbl_Account.OwnerID КАК OwnerID, | dbo_tbl_Account.StateID КАК StateID, | dbo_tbl_Account.ModifiedOn КАК ПартнерДатаИзменения |ИЗ | ВнешнийИсточникДанных.sc_Terrasoft.Таблица.dbo_tbl_Account КАК dbo_tbl_Account |ГДЕ | dbo_tbl_Account.ID = &ID | |УПОРЯДОЧИТЬ ПО | dbo_tbl_Account.ModifiedOn УБЫВ"; Запрос.УстановитьПараметр("ID", ID_CRM); УстановитьПривилегированныйРежим(Истина); РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Такой способ получения данных прост для понимания, гибок, его удобно отлаживать и легко настроить под конкретную задачу, так как в запросе можно сразу выставить условия и получить только нужные строки таблицы.
Вторая задача. Запрос клиента - настроить получение данных по тендерным заявкам из системы мониторинга Headway в информационную базу 1С. После подключения базы Headway мы обнаружили одну таблицу, где хранилась информация о полях тендерных заявок и их типах, а также несколько функций внешнего источника данных для получения данных по самим заявкам.
Следуя рекомендациям разработчиков Headway, мы использовали функции внешнего источника, возвращающие значения. Но и тут возникли сложности - платформа 1С не всегда может корректно обработать результат такой функции (например, если функция возвращает в качестве результата таблицу значений). Для того, чтобы всё-таки получить необходимые данные, существует другой метод, который в итоге и был использован для решения данной задачи - подключение к SQL базе с использованием COM-объекта.
Для начала создаем таблицу значений, в которую будем загружать результат выполнения функции и сразу добавляем в неё колонки с подходящими описаниями типов значений исходя из возвращаемых параметров.
ТаблицаTender = СоздатьТаблицуTender();
Затем получаем данные для подключения к базе SQL и создаем подключение через COM-объект.
Текст процедуры подключения
НастройкиHeadway = АКАМ_HeadwayТорги.ПолучитьДанныеИзХранилищаНастроекHeadway(); Если НастройкиHeadway.Количество() = 0 Тогда Возврат Ложь; КонецЕсли; Логин = НастройкиHeadway.Логин; Пароль = НастройкиHeadway.Пароль; ИмяБД = НастройкиHeadway.ИмяБД; ИмяСервера = НастройкиHeadway.АдресСервера; UserID = НастройкиHeadway.UserID; Попытка Connection = new COMObject("ADODB.Connection"); ConnectionString = СтрШаблон("DRIVER={SQL Server}; |SERVER=%1; |DATABASE=%2; |UID=%3; |PWD=%4", ИмяСервера, ИмяБД, Логин, Пароль); Connection.Open(ConnectionString); Исключение СообщениеДействия = СтрШаблон("Не удалось создать подключение по причине: %1", ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); Connection = Неопределено; КонецПопытки; Если Connection = Неопределено Тогда Возврат Ложь; КонецЕсли;Создаем пустые соответствия, заполняя ключи названиями выходных параметров. Затем вызываем функцию, передавая в нее входные параметры, и обрабатываем результат, записывая возвращаемые значения в таблицу значений ТаблицаTender.
Текст процедуры обработки результата
СоответствиеПолейTender = ПолучитьСоответствиеПолейЗаявкиTender(); СоответствиеПолейTenderДатыСтатус = ПолучитьСоответствиеПолейЗаявкиTenderДатыСтатус(); Попытка // получение данных Tender Сmd = new COMObject("ADODB.Command"); Сmd.ActiveConnection = Connection; ТекстПроцедурыСПараметрами = "DECLARE @tmpUserID uniqueidentifier; |SET @tmpUserID = cast('%1' as uniqueidentifier); |EXEC HWC_DATA.dbo.API_TENDER |@UserID = @tmpUserID, |@Field = 'Tender_ID,TenderPrice,NotifNr,NSI_Law,TendNm,Cust_ID,RegNm,Org_ProviderNm_id,NSI_Platform,ClaimReglament,FormT_name,StatusT_name,PublDt,ClaimDtBeg,ClaimDtEnd,ClaimProcDtEnd,ClaimProcDtEnd,TendDt', |%2, |@Entity = 'Tender';"; Шаблон = СтрШаблон("@DateStart = '%1', |@DateEnd = '%2'", ДатаНачалаПолученияЗаявок, ДатаОкончанияПолученияЗаявок); ТекстПроцедурыСПараметрами = СтрШаблон(ТекстПроцедурыСПараметрами, UserID, Шаблон); Rs = new COMObject("ADODB.RecordSet"); Rs.Open(ТекстПроцедурыСПараметрами, Connection); Rs = Rs.NextRecordSet(); Пока НЕ Rs.EOF() Цикл НоваяСтрока = ТаблицаTender.Добавить(); Для Каждого Эл Из СоответствиеПолейTender Цикл НоваяСтрока[Эл.Ключ] = Rs.Fields(Эл.Ключ).Value; КонецЦикла; Rs.MoveNext(); КонецЦикла; Rs.Close();Стоит заметить, что иногда при попытке обойти результат возникает ошибка или же результат оказывается пустым, хотя так быть не должно. Справиться с такой ошибкой помогает функция «Rs = Rs.NextRecordSet();» перед началом обхода результата. Если такой ошибки не возникает, то в этой функции нет необходимости.
Также сложность этого случая заключается в том, что в возвращаемых параметрах присутствуют даты и строки неограниченной длины. Даже при таком подходе к решению задачи корректно вернуть такие значения не удается, поэтому в результирующей таблице ТаблицаTender такие колонки окажутся пустыми.
Для того чтобы вернуть такие значения нужно создать временную таблицу SQL, в которую будут помещаться данные, которые необходимы.
Текст процедуры работы с временной таблицей
Сmd = new COMObject("ADODB.Command"); Сmd.ActiveConnection = Connection; ТекстПроцедурыСПараметрами = "DECLARE @tmpUserID uniqueidentifier; |SET @tmpUserID = cast('%1' as uniqueidentifier); |IF OBJECT_ID('tempdb..#tmpTender') IS NOT NULL |BEGIN | DROP TABLE #tmpTender |END |CREATE TABLE #tmpTender ( | Tender_ID bigint, | StatusT_name varchar(300), | PublDt datetime, | ClaimDtBeg datetime, | ClaimDtEnd datetime, | ClaimProcDtEnd datetime, | TendDt datetime); |INSERT INTO #tmpTender(Tender_ID,StatusT_name,PublDt,ClaimDtBeg,ClaimDtEnd,ClaimProcDtEnd,TendDt) |EXEC HWC_DATA.dbo.API_TENDER |@UserID = @tmpUserID, |@Field = 'Tender_ID,StatusT_name,PublDt,ClaimDtBeg,ClaimDtEnd,ClaimProcDtEnd,TendDt', |%2, |@Entity = 'Tender'; |SELECT * FROM #tmpTender;"; ТекстПроцедурыСПараметрами = СтрШаблон(ТекстПроцедурыСПараметрами, UserID, Шаблон); Rs = new COMObject("ADODB.RecordSet"); Rs.Open(ТекстПроцедурыСПараметрами, Connection);
Перед созданием такой временной таблицы необходимо проверять существует ли уже таблица с таким именем, и если это так – то удалить таблицу. В создаваемой таблице так же важно обозначить типы данных, которые будут храниться, причем строки с неограниченной длиной надо ограничить по количеству символов. Процедура делает выборку необходимых данных из основной таблицы и помещает во временную, после чего можно обойти результат выборки по временной таблице.
Текст процедуры работы обработки выборки
Rs = Rs.NextRecordSet();
Rs = Rs.NextRecordSet();
Пока НЕ Rs.EOF() Цикл
НайденнаяСтрока = ТаблицаTender.Найти(Rs.Fields("Tender_ID").Value, "Tender_ID");
Если НайденнаяСтрока = Неопределено Тогда Продолжить; КонецЕсли;
Для Каждого Эл Из СоответствиеПолейTenderДатыСтатус Цикл
НайденнаяСтрока[Эл.Ключ] = Rs.Fields(Эл.Ключ).Value;
КонецЦикла;
Rs.MoveNext();
КонецЦикла;
Rs.Close();
Исключение
СообщениеДействия = СообщениеДействия + Символы.ПС + СтрШаблон(
"Не удалось получить данные таблицы Tender по причине: %1",
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Этот способ получения данных из БД SQL достаточно сложный в реализации, так как требует знаний по написанию запросов на языке SQL.
Подводя итог можно сказать, что ни один из вышеперечисленных способов нельзя назвать универсальным. Для каждой конкретной задачи необходимо использовать разные методы получения данных исходя из требований клиента и структуры базы данных, с которой предстоит работать. Важно понимать, что задачи синхронизации и обмена данными являются ключевыми для успешного функционирования современных предприятий. Правильное решение этих задач позволяет оптимизировать бизнес-процессы, повысить эффективность работы и получить доступ к более полным и актуальным данным.