NAV+SQL: две подружки-хохотушки

Давным-давно, когда я и не подозревал о существовании Navision, занимался разработкой корпоративной системы одного логистического холдинга. По всем понятной терминологии – «самописки». Самописка была клиент-серверной, на связке Access 2000 + MS SQL 2000. Тогда же мне пришлось очень плотно подсесть на SQL, хранимые процедуры, функции, триггеры и т.д. и т.п. Фактически, SQL стал (да и остается по сей день) моим любимым языком. Я мог писать на нем что угодно, реализовывать любые алгоритмы, строить отчетность. И все написанное, что характерно, летало. А если вдруг переставало летать, то небольшая настройка индексов, плана исполнения ХП, обновленная статистика снова выводила базу в полет :)

По этой причине мое знакомство с Navision было очень болезненным –  там совершенно не было SQL :-( Некоторые наши сотрудники не смогли выдержать такого расклада, и ушли туда, где был сиквел, .NET и полная свобода творчества ;-)

А я остался… И какое-то время чуть ли не физически мучался. Мне просто не хватало кислороду! :-) И его, наверное, не хватило бы, но мысль о том, как подружить Navision с сиквелом, зрела (сервер БД-то по-прежнему был – SQL Server 2000). К сожалению, работать приходилось почти в полной изоляции, без общения с коллегами по цеху (в 2003 году было не очень много ресурсов на тему навижна). Это тоже сыграло свою роль – я просто тормозил.

Решение пришло само собой. Просто понадобилось сделать отчет с несколькими группировками, условиями – т.е. достаточно сложную вещь. На навижн – малореально и медленно. Выход один — хранимая процедура.

А теперь собственно, перейдем от лирики к физике. Вот функции кодеюнита, который отвечает за работу с SQL Server. Функции нужны следующие – создание подключения к сиквелу, выполнение произвольного SQL-запроса на сервере с получением ADODB.Recordset, далее функция по заполнению временной таблицы данными из полученного рекордсета.

OBJECT Codeunit 50001 SQL Management
{
  VAR
    adoCon1 : Automation "'Microsoft ActiveX Data Objects 2.8 Library'.Connection";
    Text001 : TextConst 'RUS=Не могу определить сервер, к которому подключена система!';
    Text002 : TextConst 'RUS=Не могу определить текущую базу данных!';
    Text003: TextConst 'RUS="Provider=sqloledb;data source=%1;database=%2;uid=%3;pwd=%4;"';
}

Для подключения к базе нам понадобятся виртуальные таблицы (2000000…), о которых можно почитать в рубрике «Уголок пытливого юзера».

PROCEDURE CreateNewConnection( VAR adoConnection: "Automation :
              'Microsoft ActiveX Data Objects 2.8 Library'.Connection");
VAR
  recServer : Record 2000000047; // Сервер
  recSession : Record 2000000009; // Сессия БД
  txtConnectionString : Text[250];
BEGIN

  WITH recServer DO BEGIN
    SETRANGE("My Server", TRUE);
    IF NOT FIND('-') THEN
      ERROR(Text001);
  END;

  WITH recSession DO BEGIN
    SETRANGE("Моя сессия", TRUE);
    IF NOT FIND('-') THEN
      ERROR(Text002);
  END;

  txtConnectionString := STRSUBSTNO(Text003,
                         recServer."Server Name",
                         recSession."Имя БД",
                         'user', 'password); // Эти вещи лучше вынести в настройки

  IF NOT ISCLEAR(adoConnection) THEN
    CLEAR(adoConnection);

  CREATE(adoConnection);
  adoConnection.ConnectionString(txtConnectionString);
  adoConnection.ConnectionTimeout(15);
  adoConnection.Open;
  adoConnection.CommandTimeout(60);
END;

Следующая функция выполняет SQL-запрос и возвращает результаты выполнения в рекордсете padoRec.
Не забудьте использовать SET NOCOUNT ON в начале хранимой процедуры :-)

Если запрос – это выполнение хранимой процедуры, и она возвращает несколько рекордсетов, то к каждому следующему можно получить доступ через свойство padoRec.NextRecordset.

PROCEDURE CreateQuery(ptxtSQL : Text[1024]; VAR padoRec : Automation
                      "'Microsoft ActiveX Data Objects 2.8 Library'.Recordset");
BEGIN
  //Получение ADODB.Recordset с результатами выполнения запроса txtSQL
  IF ISCLEAR(adoCon1) THEN
    CreateNewConnection(adoCon1);

  IF NOT ISCLEAR(padoRec) THEN
    CLEAR(padoRec);

  CREATE(padoRec);
  padoRec.Open(ptxtSQL, adoCon1, 3, 3, -1);
END;

Если ХП не возвращает результатов, либо они не важны, можно использовать следующую функцию (долгое время работы с ADP-проектами на Access дает о себе знать, там ведь тоже есть CurrentProject.Connection.Execute :-)

PROCEDURE ExecuteSQL(VAR ptxtQuery : Text[1024]);
BEGIN
  //Выполняет SQL-запрос (результаты не возвращаются)
  IF ISCLEAR(adoCon1) THEN
    CreateNewConnection(adoCon1);

  adoCon1.Execute(ltxtQuery);
END;

И последний шаг – заполнение временной таблицы данными из рекордсета. Ну тут все просто :-)

 ...
unitMSSQLMgt.CreateQuery(txtSQL, adoRec);

WITH recTmpCustomer DO BEGIN
  RESET;
  DELETEALL;

  IF adoRec.EOF THEN
    EXIT;

  WHILE NOT adoRec.EOF DO BEGIN
    INIT;
    "No." := adoRec.Fields.Item('Customer_No').Value;
    ...
     // Заполняем поля временной таблицы
     ...
    INSERT;
    adoRec.MoveNext;
  END;
END;

Далее, таблицу можно указать как источник данных для формы, и поехали :-) Код служит верой и правдой уже долго. Правда, на этом тема работы связки Navision+SQL не заканчивается, а только начинается. Нюансы…

На сегодня все. В следующей статье я расскажу вам о том, какие открытия ждали меня дальше.

Автор:

В области Navision - с 2003 года. Профессиональные интересы: NAV, MS SQL, .NET, BPMN, IT-менеджмент. Предметная область: логистика, финансы, склады, 3PL.

Количество статей, опубликованных автором: 86.

Добавить комментарий