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.