Получение SQL данных

Originally published in Clarion Magazine (www.clarionmag.com). Translated and reprinted with permission.
Впервые опубликовано в журнале Clarion Magazine (www.clarionmag.com). Переведено и перепечатано с разрешения.

  • Автор: Том Руби
  • Уровень знаний: начальный/advanced
  • Подразделы: нет
  • Оригинальная дата публикации: 31 августа 2005г.
  • Дата перевода: сентябрь 2005г.

Нет сомнений, что было бы удобным если бы в Кларионе можно было бы выполнить SQL запрос и получить результат. Например, я хочу получить сумму нескольких записей. На SQL я бы просто написал запрос типа:

SELECT Sum(Amount) FROM tblSomething WHERE SomeField = SomeValue

Проблема в том, что в Кларионе мне нужно создать View, включая в него поле Amount и … очень долгий путь вообщем.

Но есть другой метод! Ничего не говоря нам, SoftVelocity сделала нечто действительно пригодное для Клариона. Они дали нам класс, который они назвали cCwADO, который великолепно обрабатывает такой тип ситуаций и его использование едва ли труднее чем сесть в кресло.

Предварительные условия
Если вы попытаетесь использовать cCwADO в любых старых ABC программах, вы всегда получите GPF. Есть три простые вещи, которые необходимо сделать, чтобы класс заработал.

• Подключить класс. Подключать класс необходимо в модуле. Для этого переключите ваше главное окно приложения в «Module view» и откройте модуль, который содержит процедуру, в которой вы хотите использовать cCwADO. Откройте embeds-ы и добавьте одну строку кода в начале модуля:

 INCLUDE('cwado.inc'),ONCE

• Инициализировать COM систему. Для того, чтобы сделать это, SoftVelocity включило маленький класс, названный cCOMIniter, а необходимый код заключен в методе-констукторе. Этот класс автоматически подключается при подключении класса cCwADO, так что все что вам нужно это правильно определить экземпляр класса. Я поместил код внутри данных процедуры, который выглядит так:

ComIniter cCOMIniter

Теперь, как я говорил немного выше, это нужно сделать только один раз для всего приложения. Я не думаю, что это заденет что-то, если выполнится еще раз (не цитируйте меня в этом месте). Вы можете проверить, что инициализация уже была выполнена:

IF ComIniter.IsInitialized()

• Добавить несколько определений (Defines) в ваш проект: выберите Project/Properties для вызова редактора проекта. Нажмите кнопку Properties и выберите закладку Defines. Вы увидите две директивы для ABCDllMode и ABCLinkMode. Добавьте еще несколько:

_COMLinkMode_
_COMDLLMode_=>off
_ADOLinkMode_
_ADODLLMode_=>off
_svLinkMode_
_svDLLMode_=>off
_ADOMPRLinkMode_
_ADOMPRDLLMode_=>off
_SVDllMode_=>0
_SVLinkMode_=>1

Я не уверен полностью, что это все необходимые директивы, или то, что эти настройки будут работать для всех моделей сборки, но это работает прекрасно для приложений, использующих CxxRUN.DLL или Local Mode.

Класс
Теперь вы готовы к тому, чтобы использовать класс. Вы можете определить экземпляр cCwADO класса в точке вставки для определения данных процедуры или даже в секцию данных метода. Просто наберите следующее:

DataSet cCwADO

Также вы можете определить класс следующим способом:

DataSet   CLASS(cCwADO)
          END

Пока вы все еще в той точке вставки, определите также переменную для строки соединения с сервером:

ConnectionString   CSTRING(256)

Для того, чтобы использовать класс, для начала вы должны соединиться с вашей базой данных. К счастью, существует простой метод, для того чтобы сделать это:

DataSet.SETConnection(ConnectionString)

Строка соединения, это не совсем то что написано в поле OWNER в словаре, она формируется примерно так:

Provider=SQLOLEDB.1; Persist Security Info=False; User ID=sa; PASSWORD=sa; |
Initial Catalog=book; Data Source=MAXS2000

Имя сервера это параметр Data Source. Также вы можете использовать DSN имя здесь, если оно у вас есть. В этом примере, Initial Catalog это база данных, Password и User — говорят сами за себя. Вы можете построить эту CSTRING строку любым методом, который вам нравится, но для простоты понимания, я просто присвоил эту строку целиком переменной ConnectionString. Конечно, теперь вы знаете как хакнуть мою тестовую базу данных 🙂

Следующим шагом вы должны сказать объекту куда вы хотите поместить данные. Любая переменная в памяти будет работать:

DataSet.AddFieldsInfo('', 'Counter', RecordCount, 0 )

Здесь два строковых параметра, которые указывают какую колонку от результирующей выборки вы хотите. Первый параметр — имя таблицы, второй — колонка таблицы. Вы видите, что я оставил имя таблицы пустым, потому что я намереваюсь использовать выражение типа:

select count(*) as counter from tblDetail

т.е. результат (count *) не связан с таблицей.
Третий параметр метода, это переменная в которую вы хотите поместить результат. В этом случае я использую переменную, названную RecordCount и определенную как LONG.
Последний параметр почти всегда ноль. Если колонка которую вы запрашиваете является датой, то вам необходимо использовать константу adDBDate. Эта величина определена в файле adoint.inc и становится доступной после подключения класса. Там же определены еще константы для различных типов данных.

Вы вызываете метод AddFieldsInfo единожды для каждой колонки, которую вы хотите получить, или использовать этот метод следующим образом:

DataSet.addFieldsInfo('tablename', DisplayQueue)

Внутри этот удобный метод использует WHO и WHAT для добавления информации о полях для всех полей группы, очереди или даже записи файла (record). Он сравнивает имена полей в очереди с именами полей набора данных (dataset) за вас.
Теперь вы готовы выполнить запрос:

B = DataSet.ExecuteQuery('select count(*) as counter from tblDetail')

Метод ExecuteQuery возвращает 1 если возвращен ряд и 0 в противном случае. В этом примере, метод помещает счетчик в переменную счетчика записей.
Если вы ожидаете получить набор данных больше чем из одного ряда, вы можете просто прокрутиться внутри набора следующим способом:

LOOP
   IF DataSet.Next() = 0 THEN BREAK END
END

А вот кусочек кода, показывающий как закачать набор записей в очередь:

B = DataSet.ExecuteQuery('SELECT CustomerID, CompanyName FROM Customers')
LOOP
   ADD(DisplayQueue)
    IF NOT DataSet.Next() THEN BREAK END
END

Закрытие набора данных
Когда вы закончите работать с набором данных, его необходимо закрыть.

DataSet.Close()

Это фактически подготавливает набор к следующему запросу, если вы хотите конечно.

Заключение
Получение данных напрямую от SQL базы данных это всегда борьба с Кларионом. Или это так кажется. Ничего не говоря нам, SoftVelocity дали нам инструмент, который нужен для выполнения любых SQL запросов и получения запросов в группу, очередь или даже в отдельные переменные.
Вы должны помнить:
• Подключайте класс в module-секции
• Создайте экземпляр cCOMIniter
• Добавьте текст директив компилятора
• Создайте экземпляр cCwADO класса
• Используйте метод(ы) AddFieldsInfo для того, чтобы сказать куда положит данные
• Выполните ваш запрос
• Получите в цикле набор данных