Статьи

Подключение к базе данных 1С и выполнение SQL запросов на примере работы со справочниками. Часть 2.

В прошлой статье мы заполняли известные поля таблиц известными значениями. Теперь рассмотрим обратную задачу. Предположим, что нам требуется выбрать значения из таблиц. Написать и выполнить соответствующий SQL запрос мы уже сможем. В "Rainbow" есть методы позволяющие получить данные из выборки. Основная проблема заключается в том, чтобы узнать, что же мы получили. Согласитесь, что от строки ' STU ' толку мало. Тот факт, что она находится в колонке справочника контрагента под названием "Ответственный менеджер" дает нам основание предполагать, что это идентификатор элемента справочника "Сотрудники", однако и этого пытливому уму недостаточно. Конечно, можно сделать запрос к справочнику "Сотрудники" по этому идентификатору и получить нужные сведения (например фамилию), но в контексте работы в 1С удобнее было бы получить соответствующий агрегатный объект. Этим мы и займемся.
К сожалению "Rainbow" не содержит методов, позволяющих осуществить преобразование ID объекта в объект 1С. Придется позаботиться об этом самим. Идея заключается в использовании функции ЗначениеИзСтрокиВнутр. Параметром ей служит особым образом форматированная строка, которая однозначно идентифицирует любой агрегатный объект. Если Вы поэкспериментируете с обратной функцией ЗначениеВСтрокуВнутр, применив ее к различным объектам и внимательно присмотревшись к полученным результатам, то у Вас есть шанс совершить деяние достойное Шампольона. Мы этот путь уже прошли и в результате предлагаем Вашему вниманию три функции восстанавливающие по виду объекта, по типу объекта и по идентификатору объекта собственно агрегатный объект.
//Вспомогательная функция для представления строки, представляющей
//идентификатор объекта в десятичном виде, 
//в нужном формате
Функция глСтрокаФикс(Знач текстСтрока,Знач ФиксДлина,Назад = 1) Экспорт
Перем Инд, ДлинаТекстСтрока;
   ФиксДлина = Число(ФиксДлина);
   текстСтрока = СокрЛП(Строка(текстСтрока));
   ДлинаТекстСтрока = СтрДлина(текстСтрока);
   ДобавитьПробелов = ФиксДлина - ДлинаТекстСтрока;
   Если ДобавитьПробелов = 0 Тогда
      Возврат текстСтрока;
   ИначеЕсли ДобавитьПробелов < 0 Тогда
      текстСтрока = Сред(текстСтрока,1,ДлинаТекстСтрока - ДобавитьПробелов);
   Иначе
      Для Инд = 1 по ДобавитьПробелов Цикл
         Если Назад = 1 Тогда
            текстСтрока = текстСтрока + " ";
         Иначе
            текстСтрока = " " + текстСтрока;
         КонецЕсли;
      КонецЦикла;
   КонецЕсли;
   Возврат текстСтрока;
КонецФункции

//Функция преобразующая идентификатор объект 
//из тридцатишестиричного формата в десятичный
Функция глПреобразоватьID(Знач ID) Экспорт
   ID = Прав(ID,9);
   Стр1 = СокрЛП(Строка(objRainbowService.BaseToInt(НРег(Сред(ID,1,6)),36)));
   Стр2 = ВРег(Сред(ID,7,3));
   Стр3 = Сред(глСтрокаФикс(Стр1 + Стр2 + "'",14,0),1,13);
   Возврат Стр3;
КонецФункции

//Самая главная функция.
//Принимает три параметра
//ТипОбъекта - Строка
//ВидОбъекта - Строка или готовый идентификатор вида объекта в десятичном
//формате
//IDОбъекта - идентификатор объекта
Функция глПолучитьОбъектПоID(ТипОбъекта,Знач ВидОбъекта,IDОбъекта) Экспорт
   Если ТипОбъекта = "Справочник" Тогда
      Если ТипЗначенияСтр(ВидОбъекта) = "Строка" Тогда
         ВидОбъекта = Число(глПолучитьМетаIDОбъекта(Метаданные.Справочник(ВидОбъекта), 0));
      КонецЕсли;
      Если ТипЗначенияСтр(ВидОбъекта) = "Число" Тогда
         стрНомер = СокрЛП(Строка(ВидОбъекта));
      КонецЕсли;
      стрКод = "B";
      стрID  = глПреобразоватьID(IDОбъекта);
   ИначеЕсли ТипОбъекта = "Документ" Тогда
      Если ТипЗначенияСтр(ВидОбъекта) = "Число" Тогда
         стрНомер = СокрЛП(Строка(ВидОбъекта));
      КонецЕсли;
      стрКод = "O";
      стрID  = глSQLПреобразоватьID(IDОбъекта);
   ИначеЕсли ТипОбъекта = "Перечисление" Тогда
      Если ТипЗначенияСтр(ВидОбъекта) = "Строка" Тогда
         ВидОбъекта = Число(глПолучитьМетаIDОбъекта(Метаданные.Перечисление(ВидОбъекта), 0));
      КонецЕсли;
      Если ТипЗначенияСтр(ВидОбъекта) = "Число" Тогда
         стрНомер = СокрЛП(Строка(ВидОбъекта));
      КонецЕсли;
      стрКод = "E";
      стрID  = глПреобразоватьID(IDОбъекта);
   КонецЕсли;
   
   Результат = "{""" + стрКод + """,""0"",""0"",""" + стрНомер + """,""0"",""0"",""" + стрID + """}";
   
   Попытка
      Объект = ЗначениеИзСтрокиВнутр(Результат);
   Исключение
      Сообщить(ОписаниеОшибки(),"!!!");
      Сообщить(">> Тип = '" + ТипОбъекта + "'","!");
      Сообщить(">> Вид = '" + ВидОбъекта + "'","!");
      Сообщить(">> ID  = '" + IDОбъекта  + "'","!");
      Сообщить(">> Знач = " + Результат       ,"!");
      Возврат 0;
   КонецПопытки;

   Возврат Объект;
КонецФункции
Соберем все вместе в маленьком примере. Предположим нам надо выбрать из справочника контрагентов только тех у который определенный ответственный менеджер и которые находятся в определенном регионе.
Функция ПолучитьТЗКонтрагентовПоУсловию(ВыбМенеджер, ВыбРегион)
   тзРезультат = СоздатьОбъект("ТаблицаЗначений");
   тзРезультат.НоваяКолонка("Контрагенты", "Справочник.Контрагенты");
   метаIDКонтрагента = Число(глПолучитьМетаIDОбъекта(Метаданные.Справочник("Контрагенты"), 0));
   РеквизитОтвМенеждер = Метаданные.Справочник("Контрагенты").Реквизит("ОтветственныйМенеджер");
   метаIDОтветственныйМенеджер = Число(глПолучитьМетаIDОбъекта(РеквизитОтвМенеджер, 0));
   РеквизитРегион = Метаданные.Справочник("Контрагенты").Реквизит("Регион");
   метаIDРегион = Число(глПолучитьМетаIDОбъекта(РеквизитРегион, 0));
   IDМенеджера = глПолучитьIDОбъекта(ВыбМенеджер.ТекущийЭлемент(), 1);
   IDРегиона = глПолучитьIDОбъекта(ВыбРегион.ТекущийЭлемент(), 1);
   стрЗапрос =
   "SELECT ID
   |FROM SC" + метаIDКонтрагента + "
   |WHERE
   |	SP" + метаIDОтветственныйМенеджер + " = " + IDМенеджера + "
   |	AND SP" + метаIDРегион + " = " + IDРегиона;
   objSQLЗапрос = "";
   глВыполнитьЗапрос(objSQLЗапрос, стрЗапрос);
   //GotoNext - переход к следующей записи
   objSQLЗапрос.GotoNext();
   //IsOk - Возвращает 1, если есть выбранный элемент
   Пока objSQLЗапрос.IsOK() <> 0 Цикл
      тзРезультат.НоваяСтрока();
      //GetString - получить строковое значение из выборки
      текЗначение = objSQLЗапрос.GetString(0);
      тзРезультат.Контрагенты = глПолучитьОбъектПоID("Справочник", метаIDКонтрагента, текЗначение);
      objSQLЗапрос.GotoNext();
   КонецЦикла;
   //Close - закрытие выборки
   objSQLЗапрос.Close();
   objSQLЗапрос = "";
   Возврат тзРезультат;
КонецФункции
Но несмотря на все достоинства у "Rainbow" присутствует ряд недостатков:
  • Так как "Rainbow" работает с базой данных в контексте с 1С, то ошибка в SQL запросе приводит к системной ошибке самой 1С, и как следствие, закрытию программы.
  • По этой же причине попытка обратится к SQL серверу при открытом объекте ODBCQuery (например, создать новый элемент справочника) вызовет системную ошибку 1С и ее закрытие.
  • "Rainbow" не умеет корректно возвращать значения типа NUMERIC(n, m). Поэтому приходится в запросе выполнять преобразование к типу CHAR, а затем в 1С выполнять обратное преобразование.
  • "Rainbow" не умеет обрабатывать ошибки в пакетном запросе. Т.е. если Вы выполняете пакет команд SQL, то вполне вероятна ситуация, при которой одна из первых команд не выполнится из-за ошибки (например, ошибка в вызываемой из пакета хранимой процедуре), а остальные будут выполняться далее. В этом случае никакой ошибки "Rainbow" не сгенерит, и чтобы узнать об успешности выполнения всех команд пакета Вам придется приложить ряд усилий.
Но перечисленные недостатки не отменяют основных преимуществ "Rainbow":
  • Способность работать в монопольном режиме.
  • Наличие средств работы с метаданными.
На сегодня все. Далее мы рассмотрим способы подключения к базе данных с использованием технологии ADODB.
Hosted by uCoz