Borland Delphi и расширения ADO

Использование ADO MultiDimensional

Инструменты для анализа данных и поддержки принятия решений в настоящее время считаются одним из важнейших типов приложений. Реализация подобного анализа нередко базируется на построении многомерных хранилищ данных (data warehouses) и на аналитической обработке данных (On-Line Analytical Processing, OLAP)  — популярной технологии многомерного бизнес-анализа. Концепция OLAP была описана в 1993 году Е.Ф.Коддом, известным исследователем баз данных и автором реляционной модели данных. В настоящее время поддержка OLAP реализована в различных СУБД и инструментах. Если вы знакомы с компонентами Decision Cube, то вы уже видели простейшую реализацию OLAP — приложения с такими компонентами представляют собой примитивные OLAP-инструменты.

Подробности об OLAP можно найти на сайте www.olap.ru, сопровождаемом компанией «Интерфейс», поставщиком многих OLAP-продуктов на российском рынке.

В нашей статье мы можем себе позволить лишь краткое введение в OLAP и создание хранилищ данных, ибо это потребуется для понимания того, что представляют собой объекты ADO MD. Более подробно тема OLAP будет раскрыта в одной из статей цикла «Введение в СУБД».

в начало

в начало

OLAP и создание хранилищ данных: краткое введение

Рассмотрим, из чего обычно состоит многомерное хранилище данных. Представим себе торговую компанию, которая хранит все сведения о торговых операциях в какой-либо базе данных, содержащей среди прочих представление Invoices с подробными сведениями о заказах клиентов. Предположим, что обращение к этому представлению приводит к появлению набора данных, показанного в табл. 3.

Таблица 3

Date ProductCategory ProductSubcategory Product Name Country City SalesPerson Payment
01.01.99 Vegetables Canned Vegetables Canned Tomatos Germany Berlin Nicolas Wilson $1280
01.01.99 Vegetables Fresh Vegetables Dried Mushrooms UK London Daniel Adams $514
01.02.99 Dairy Cheese Cheddar Cheese Germany Frankfurt Nicolas Wilson $723
01.02.99 Dairy Cheese Gorilla Cheese Spread Austria Vienna Nicolas Wilson $330
01.03.99 Vegetables Canned Vegetables Canned Tomatos UK London Daniel Adams $439
. . . . . . . . . . . . . . . . . . . . . . . .

Допустим, нам требуется узнать суммарную стоимость всех заказов, сделанных клиентами из Германии. Очевидно, что для получения такого суммарного значения (summary) нужно выполнить следующий запрос:

  SELECT SUM(Payments) FROM Sales WHERE Country='Germany' 

В этом запросе можно заменить ‘Germany’ на ‘Austria’, или на ‘UK’, или на название другой страны. В результате мы получим одномерный массив значений — по одному числу для каждой страны, как показано в табл. 4:

Таблица 4

Germany

Austria

UK

USA

...

$2003

$330

$953

$5321

 

Теперь немного усложним задачу. Например, спросим, какова суммарная стоимость заказов овощей, сделанных клиентами из Германии:

   SELECT SUM (Payment) FROM Invoices WHERE Country=' Germany' AND ProductCategory='Vegetables'

Если мы рассмотрим все возможные комбинации категорий продуктов и названий стран, мы получим двухмерный массив суммарных значений (табл. 5):

Таблица 5

 

Vegetables

Dairy

Drinks

Germany

$1280

$723

$239

UK

$514

$0

$732

Austria

$0

$330

$0

Подобная таблица часто именуется перекрестной таблицей, кросс-таблицей, а также сводной таблицей (Cross table, Pivot table). Первым измерением (dimension) в ней является измерение Country, вторым — ProductCategory.

Снова модифицируем наш запрос. Предположим, теперь нам нужно узнать, какова суммарная стоимость заказов овощей, сделанных клиентами из Германии и обработанных сотрудником по имени Nicolas Wilson.

    SELECT SUM(Payments) FROM Sales WHERE Country='Germany' AND ProductCategory='Vegetables'    AND SalesPerson='Nicolas Wilson'

Если мы рассмотрим все возможные комбинации категорий продуктов, названий стран, имен сотрудников, то получим трехмерный массив суммарных значений, который можно представить в виде куба данных (на самом деле это, конечно, параллелепипед, но к таким наборам данных, тем не менее, принято применять термин «куб»). Размерностями этого набора данных являются Country, ProductCategory и SalesPerson. Мы можем продолжать добавлять параметры к нашему запросу (и, следовательно, добавлять к нашему кубу все новые и новые размерности) и получать соответствующие суммарные значения. Именно это и делает приложение, использующее компоненты Decision Cube, — оно вычисляет такие суммы и хранит их в оперативной памяти.

Следует отметить, что наборов таких суммарных значений может быть несколько (в частности, это могут быть суммы платежей, средняя стоимость, количество заказов и т.п.). Подобные суммарные значения в русской терминологии именуются агрегатными данными, а в англоязычной наиболее применимы термины summaries и measures.

Следующий важный аспект, часто используемый при создании OLAP-хранилищ, — это иерархическая структура размерностей. Например, если одна из размерностей основана на поле типа «дата/время», то суммарные значения можно получить для различных годов, а также для кварталов, месяцев, дней (это единственный тип иерархии, поддерживаемый компонентами Decision Cube). Мы также можем сравнить, допустим, суммарные значения для сходных периодов времени (например, для всех сред и всех четвергов или для января 1999 года и января 2000 года). Нам также может понадобиться и другой тип иерархии (например, Country/State/City или Product Category/Product Subcategory/Product Name).

Отметим, что хранение агрегатных данных в оперативной памяти — далеко не самый эффективный способ их получения и использования — как минимум потому, что их каждый раз требуется вычислять, а также потому, что их суммарный объем может оказаться в общем случае непредсказуемым. Более разумным является однократное вычисление агрегатных данных и хранение их в какой-нибудь базе данных. Это и есть ключевая идея построения хранилищ данных (data warehousing) — процесса сбора, отсеивания и предварительной обработки данных с целью предоставления результирующей информации пользователям для статистического анализа и создания отчетов на основании OLAP. Отметим, что с точки зрения логики подобное хранилище в общем случае имеет нереляционную структуру. Большинство производителей серверных СУБД выпускают специализированные серверные OLAP-инструменты и OLAP-серверы для создания и использования многомерных хранилищ данных. Этот способ анализа данных более предпочтителен, чем клиентские инструменты типа приложения с компонентами Decision Cube, так как он не требует переноса большого количества данных в клиентское приложение и повторного вычисления суммарных значений в тот момент, когда они понадобятся.

В нашей статье для иллюстрации применения объектов ADO MultiDimensional в качестве OLAP-сервера мы будем использовать Microsoft SQL Server 7.0 OLAP Extensions (или Microsoft SQL Server 2000 Analysis Services). На данный момент OLE DB Provider for OLAP Services, входящий в состав Microsoft SQL Server OLAP Extensions (Microsoft SQL Server 2000 Analysis Services) и в состав Microsoft Office, является единственным OLE DB-провайдером, позволяющим обращаться к многомерным данным. С его помощью можно обращаться к данным, хранящимся в многомерных базах данных Microsoft SQL Server 7.0/2000 или в локальных файлах *.cub, которые можно создать с помощью Microsoft Excel 2000. Однако мы полагаем, что в ближайшее время следует ожидать выпуска других OLE DB-провайдеров для других OLAP-серверов.

В данной публикации мы не описываем, как программно создать многомерное хранилище данных — чтобы сделать это в приложении, следует использовать библиотеку SQL DSO (Decision Support Objects), описание применения которой выходит за рамки данной статьи.

Для иллюстрации работы ADO MD мы используем многомерную базу данных FoodMart, входящую в комплект Microsoft SQL Server 7.0 OLAP Extensions. Однако при необходимости можно использовать любой локальный файл *.cub, созданный с помощью Microsoft Excel 2000, — в этом случае на компьютере, где будут создаваться примеры, должен быть установлен Microsoft Excel 2000.

Начнем изучение ADO MD с объектной модели, доступной в приложениях Delphi.

в начало

в начало

Объекты ADO MD

Объектная модель ADO MD, представленная на рис. 3, состоит из двух, так сказать, ветвей объектов. Первая из них используется для доступа к метаданным многомерной базы данных, вторая применяется тогда, когда нам нужно извлечь данные с помощью запросов к OLAP-кубам.

в начало

в начало

Объекты для доступа к метаданным

Первый из объектов ADO MD для доступа к метаданным — объект Catalog — представляет собой многомерное хранилище данных. Такое хранилище может содержать ноль, один или много кубов, а следовательно, одним из свойств объекта Catalog является коллекция CubeDefs. Каждый из элементов этой коллекции — CubeDef, представляющий конкретный куб в хранилище. Имя куба является значением свойства Name соответствующего объекта CubeDef. В базе данных FoodMart имеется три таких куба — Sales, Warehouse и Warehouse and Sales.

Каждый из многомерных кубов содержит несколько размерностей. Соответственно каждый из объектов CubeDef может содержать коллекцию Dimensions объектов Dimension. Каждый объект Dimension представляет конкретную размерность куба, а имя размерности содержится в свойстве Name соответствующего объекта Dimension. Например, куб Sales в базе данных FoodMart содержит несколько размерностей — Store, Time, Product, Promotion Media, Promotions, Customers и др.

Как уже было сказано, данные, из которых состоят размерности, могут быть иерархическими. Соответственно объект Dimension содержит коллекцию Hierarchies, которая теоретически может содержать один или более объектов Hierarchy. Однако на практике для каждой размерности существует только одна иерархия (по крайней мере для Microsoft SQL Server OLAP Extensions), поэтому данная коллекция содержит один-единственный элемент.

Иерархия размерности может содержать один или несколько уровней, поэтому объект Hierarchy содержит коллекцию Levels объектов Level. Например, иерархия размерности Store куба Sales содержит четыре уровня — Store Country, Store State, Store City и Store Name.

Каждый из уровней иерархии содержит один или более членов, которые представляют собой либо значения соответствующих полей в исходной базе данных, либо значения, полученные посредством группировки. Следовательно, объект Level содержит коллекцию Members объектов Member. Например, уровень Store Country единственной иерархии размерности Store содержит три члена  — Canada, Mexico и USA, уровень Store State этой же самой иерархии содержит десять членов — CA, OR, WA и несколько штатов Канады и Мексики, а уровень Store City содержит еще больше членов, соответствующих городам в этих штатах.

Отметим, что поскольку содержимое коллекции Members зависит от исходных данных, на основе которых построен куб, то метаданными это можно назвать весьма условно. Значения членов уровня иерархии являются метаданными в том случае, когда речь идет о раз и навсегда созданном кубе, который далее не пересчитывается на основе изменившихся исходных данных. В действительности ADO MD соответствует именно такой точке зрения — эта технология предназначена для чтения многомерных данных, но не для их записи. С точки зрения SQL DSO или иных технологий создания многомерных хранилищ — значения членов уровня иерархии являются уже не метаданными, а данными. Впрочем, обсуждение создания многомерных хранилищ — предмет отдельной статьи.

Каждый из этих объектов обладает внушительным списком свойств и методов. Их подробное описание можно найти в MSDN Library (http://www.msdn.microsoft.com/data/).

в начало

в начало

Объекты для доступа к данным

Вторая ветвь объектной модели ADO MD применяется для получения сечений кубов (как правило, это двухмерные кросс-таблицы, подобные табл. 4, приведенной в начале этого раздела). Для получения подобных сечений требуется сформулировать к кубу соответствующий запрос. Поскольку многомерная база данных не является реляционной (как минимум, с логической точки зрения), то использовать язык SQL для запроса к ней мы не можем. Вместо этого нам следует описать, из чего состоят строки и столбцы и какие из доступных агрегатных данных следует отобразить. Для этого мы можем использовать так называемые multidimensional expressions (MDX) — расширения SQL для формулирования запросов к OLAP-кубам. Типичный MDX-запрос выглядит следующим образом:

   SELECT axis_specification ON COLUMNS,
   axis_specification ON ROWS
   FROM cube_name
   WHERE slicer_specification

Здесь axis_specification — описание того, из чего состоит горизонтальная или вертикальная ось, в частности [Store].[Store Country].MEMBERS представляет собой список стран, [Store].[Mexico].CHILDREN — список штатов в Мексике. Slicer specification представляет собой имя агрегатных данных, используемых для создания кросс-таблицы. Например, следующий MDX-запрос:

   SELECT
   [Time].[Quarter].MEMBERS ON COLUMNS,
   {[Store].[USA].CHILDREN, [Store].[Canada].CHILDREN} ON ROWS 
   FROM Sales 
   WHERE [Measures].[Profit]

приведет к получению кросс-таблицы, содержащей суммарный ежеквартальный доход для всех штатов США и Канады.

Подробности о синтаксисе и ключевых словах MDX можно найти в Microsoft Platform SDK.

В объектной модели ADO MD кросс-таблица, являющаяся результатом MDX-запроса, представлена объектом CellSet. Этот объект предоставляет доступ к объектам Cells, представляющим конкретные ячейки в кросс-таблице. Помимо этого объект CellSet содержит коллекцию Axes объектов Axis (обычно в этой коллекции два объекта, соответствующие строкам и столбцам). Как объект Cell, так и объект Axis обладают коллекцией Positions объектов Position, представляющих позицию вдоль оси. Объект Position обладает коллекцией Members, представляющей конкретное значение данных на оси.

Рассмотрев объекты ADO MD, мы можем создать приложение, их использующее. Это будет сделано в следующем разделе.

в начало

в начало

Создание утилиты просмотра OLAP-кубов

Выясним, как объекты ADO MD можно использовать в Delphi. В этих целях создадим приложение, с помощью которого пользователь сможет:

  • просматривать метаданные многомерной базы данных в виде иерархической структуры;
  • копировать имена объектов многомерной базы данных и ключевые слова MDX из списка, заранее заданного в редактор MDX-запросов;
  • выполнять MDX-запрос и копировать результаты в набор данных (компонент TClientDataSet) с целью представления их в компоненте TDBGrid.

Для этого создадим новый проект и поместим его главную форму будущего приложения компонента TToolBar с несколькими кнопками, TTreeView, TListBox, TDBGrid, TClientDataSet, TDataSource и TMemo. Затем установим значение свойства DataSource компонента DBGrid1 равным DataSource1, а значение свойства DataSet компонента DataSource1 равным ClientDataSet1. Компонент ListBox1 следует заполнить ключевыми словами MDX, такими как CHILDREN, MEMBERS, DESCENDANTS, и др.

Далее следует сослаться в нашем приложении на библиотеку типов ADO MD, содержащуюся в файле MSADOMD.DLL, так как ADO MD не поддерживается в Delphi 5 на уровне компонентов. Для этого следует выбрать пункт Project | Import Type Library из главного меню среды разработки, а затем выбрать Microsoft ActiveX Data Objects (Multi-dimensional) 1.0 Library из списка доступных библиотек типов. Обратите внимание на то, что если вы уже импортировали библиотеку типов ADOX и не переименовали класс Delphi для объекта ADOX Catalog, то класс Delphi TCatalog окажется уже определенным. В этом случае во избежание конфликта имен можно переименовать TCatalog в TADOMDCatalog. Желательно также убедиться, что опция Generate Component Wrapper не выбрана, так как нам нужно создать только *.pas-файл для доступа к объектам ADO MD. Далее можно нажать кнопку Create Unit, что приведет к созданию файла ADOMD_TLB.PAS, представляющего собой интерфейсный модуль к библиотеке типов ADO MD. Наконец, нам нужно включить ссылку на этот файл в предложение Uses, так же как и ссылку на модули ComObj и ADODB.

Следующий шаг в нашем приложении — соединение с многомерной базой данных. Параметр ConnectionString, используемый для этой цели, должен ссылаться на OLE DB Provider for OLAP Services (стоит убедиться, что он действительно установлен), а также на имя компьютера и имя базы данных, например:

   DS := 'Provider=MSOLAP.1;Data Source=localhost; Initial Catalog=FoodMart';

Можно также соединиться с локальными кубами, созданными с помощью Microsoft Excel и сохраненными в файлах *.cub. В этом случае параметр Connection String может выглядеть так:

   DS := 'Provider=MSOLAP.1;Data Source=C:\Data\Cubes\NW1.cub';

Код, отвечающий за соединение с базой данных, можно поместить в обработчик события OnClick одной из кнопок. Этот код, а также процедура, заполняющая компонент TreeView1 именами кубов, представлены ниже:

   procedure TForm1.Button1Click(Sender: TObject);
   begin
   DS := 'Provider=MSOLAP.1;Data Source=localhost;'+
             'Initial Catalog=FoodMart';
   FillTreeView(DS);
   end;
   
   procedure TForm1.FillTreeView(DataSource: WideString);
   var
   I : Integer;
   begin
   //Создадим новый объект Catalog 
   Catalog1 := CoCatalog.Create;
   TreeView1.Items.Clear;
   RootNode := TreeView1.Items.Add(nil, 'Catalog');
   //Соединимся с многомерной базой данных
   Catalog1._Set_ActiveConnection(OleVariant(DataSource));
   //Последовательно получим имена всех кубов в базе данных
   for I := 0 to Catalog1.CubeDefs.Count-1 do
   begin
      CubeDef1 := Catalog1.CubeDefs[I] as CubeDef;
      CubeDefNode := TreeView1.Items.AddChild(RootNode, CubeDef1.Name);
   end;
   end;

Здесь мы соединяемся с базой данных, создаем объект Catalog, просматриваем по очереди все элементы его коллекции CubeDefs и извлекаем имена кубов (они содержатся в свойстве Name объектов CubeDef).

Реальная обработка метаданных куба реализована в обработчике события OnMouseDown компонента TreeView1:

   procedure TForm1.TreeView1MouseDown(Sender: TObject; Button: TMouseButton;
                                                         Shift:    TShiftState; X, Y: Integer);
   var   
          HitTest    : THitTests;
          CurrNode   : TTreeNode;
          I                : integer;
          NodeName   : string;
          AddString : String;
   begin
   HitTest := TreeView1.GetHitTestInfoAt(X,Y);
   //Если пользователь щелкнул мышью на одной из ветвей TTreeView
   if (htOnItem in HitTest) then
   begin
      CurrNode := TreeView1.GetNodeAt(X, Y);
      //Если у ветви могут быть дочерние ветви, но они еще не добавлены
      //добавим их
      if ((CurrNode.Count=0) and (CurrNode.Level<4)) then
      begin
      case CurrNode.Level of
      //Эта ветвь представляет куб
      1: begin
            CubeDef1 := Catalog1.CubeDefs.Get_Item(CurrNode.Text);
            //Получаем имена всех    размерностей куба
            for I := 0 to CubeDef1.Dimensions.Count-1    do
            begin
             Dimension1 := CubeDef1.Dimensions[I]    as Dimension;
             DimNode      :=    TreeView1.Items.AddChild(CurrNode, Dimension1.Name);
            end;
          end;
      //Эта ветвь представляет размерность
      2: begin
            CubeDef1       := Catalog1.CubeDefs.Get_Item(CurrNode.Parent.Text);
            Dimension1 := CubeDef1.Dimensions.Get_Item(CurrNode.Text);
            //Получаем имена всех    уровней иерархии данной размерности
            for I := 0 to Dimension1.Hierarchies[0].Levels.Count-1    do
            begin
             Level1      :=    Dimension1.Hierarchies[0].Levels[i] as Level;
             LevelNode := TreeView1.Items.AddChild(CurrNode,    Level1.Name);
            end;
          end;
      //Эта ветвь представляет уровень иерархии
      3: begin
            CubeDef1       := Catalog1.CubeDefs.Get_Item(CurrNode.Parent.Parent.Text);
            Dimension1 := CubeDef1.Dimensions.Get_Item(CurrNode.Parent.Text);
            Level1          := Dimension1.Hierarchies[0].Levels.Get_Item(CurrNode.Text);
            //Получаем имена всех    членов данного уровня иерархии
            for I := 0 to Level1.Members.Count-1    do
            begin
             Member1      :=    Level1.Members[I] as Member;
             MemberNode := TreeView1.Items.AddChild(CurrNode,    Member1.Name);
            end;
          end;
         end;
       end
       else
       //Если данная ветвь уже имеет дочерние ветви (или их не должно    быть), 
       //скопируем имя объекта в редактор MDX-запросов 
       begin
         //Если ветвь не корневая
         if Currnode.Level>0 then
         begin
          CurrNode := TreeView1.GetNodeAt(X, Y);
          NodeName := CurrNode.Text;
          //Копируем имя ветви, сформатированное    в соответствии 
          //с синтаксисом MDX, в редактор MDX-запросов
          if ((CurrNode.Level=1) or (CurrNode.Parent.Parent.Text='Measures'))
          then AddString:='['+NodeName +']'
          else AddString:='['+NodeName +'].';
          Memo1.SetSelTextBuf(PChar(AddString));
       end;
      end;
   end;
   end;
   
   procedure TForm1.ListBox1Click(Sender: TObject);
   var AddString:string;
   begin
   //Добавим ключевое слово MDX из списка в редактор MDX-запросов
   AddString := Listbox1.Items[Listbox1.ItemIndex]+' ';
   Memo1.SetSelTextBuf(PChar(AddString));
   end;

Здесь мы определяем, что именно представляет ветвь, на которой пользователь щелкнул мышью (куб, размерность, уровень иерархии, член уровня), используя ее свойство Level, а также выясняем, имеются ли уже у нее дочерние ветви. Если дочерние ветви отсутствуют (свойство Count данной ветви равно нулю), мы обращаемся к базе данных и создаем соответствующие дочерние ветви, используя свойство Name соответствующего объекта ADO MD. Если же из базы данных уже нечего загружать, мы копируем имя объекта, представленного данной ветвью, в компонент Memo1, в то место, где находится курсор.

Код для копирования ключевых слов MDX в компонент Memo1 приведен в этом же фрагменте кода. Таким образом, мы получили инструмент для просмотра метаданных куба и создания текста MDX-запросов с помощью щелчков мыши на ветвях дерева объектов ADO MD на элементах списка ключевых слов.

Следующий шаг в создании OLAP-клиента заключается в выполнении MDX-запроса, содержащегося в компоненте Memo1, и в заполнении компонента TClientDataSet его результатами. Эта функциональность реализована в процедуре CDSFill, приведенной ниже:

  procedure TForm1.Button2Click(Sender: TObject);
   begin
   CDSFill(DS);
   end;
   
   procedure TForm1.CDSFill(DataSource: WideString);
   var    
   I,J : Integer;
   V    : OleVariant;
   begin
   //Создадим новый объект CellSet
   CellSet1 := CoCellSet.Create;
   try
   //Выполним MDX-запрос, содержащийся в компоненте Memo1, 
   //и откроем объект CellSet
   CellSet1.Open(Memo1.Text,DataSource);
   with ClientDataSet1 do
   begin
      Close;
      with FieldDefs do
      begin
       //Уничтожим все определения полей в ClientDataset
       Clear;
       //Добавим новые определения полей
       //Первое поле нужно для ранения имен строк
       with AddFieldDef do
       begin
         Name := 'Rows';
         DataType := ftString;
       end;
       //Перебираем коллекцию Positions первой оси
       for I := 1 to CellSet1.Axes[0].Positions.Count do
       begin
         with AddFieldDef do
         begin
         //Значение поля исходной базы данных станет    именем колонки
          Name :=CellSet1.Axes[0].Positions[I-1].Members[0].Caption+
         //Имена колонок в наборах данных должны    быть уникальны, поэтому 
         //добавим уникальное число, содержащееся    в свойстве Ordinal 
         //объекта Position, к значению поля
         ' ('+IntToStr(CellSet1.Axes[0].Positions[I-1].Ordinal)    +')';
         DataType := ftFloat;
         end;
       end;
      end;
      //Создаем и открываем ClientDataSet
      CreateDataSet;
      Open;
      //Добавляем к нему записи
      for J:=1 to CellSet1.Axes[1].Positions.Count   do
      begin
       //Добавляем запись
       Append;
       //Добавляем имя строки, используя коллекцию Position второй    оси
       Fields[0].Value :=   CellSet1.Axes[1].Positions[J-1].Members[0].Caption;
       //Перебираем ячейки в строке, извлекая из них данные
       for I := 1 to CellSet1.Axes[0].Positions.Count do
       begin
         //Создаем массив координат ячеек
         V:=VarArrayCreate([0,1], varVariant);
         V[0] := I-1;
         V[1] := J-1;
         //Если соответствующая ячейка в CellSet    не пуста,
         if CellSet1.Item[PSafeArray(TVarData(V).VArray)].FormattedValue    <> ''
         then
          //Значение поля будет равно значению в    ячейке
          Fields[I].Value := Cellset1.Item[PSafeArray(TVarData(V).VArray)].Value
         else
          //иначе поместим в поле нулевое значение
          ClientDataSet1.Fields[I].Value:=0;
       end;
      end;
      //Закрываем Cellset и высвобождаем ресурсы
      CellSet1.Close;
      CellSet1 := nil;
   end;
   except
      ShowMessage('Invalid MDX Query');
   end;
   end;

В данном фрагменте кода мы создаем объект CellSet и используем его метод Open. Если MDX-запрос корректен, будет создан пустой набор данных типа TClientDataset с именами полей, равными свойству Caption первых элементов коллекций Members, являющихся свойствами коллекции Positions первой оси (Axis[0]).

Обратите внимание на то, что имена полей в наборах данных должны быть уникальны. Однако в реальных многомерных базах данных свойство Caption членов коллекции Members таковым не является. Например, в иерархии Year/Month свойство Caption для членов коллекции Members January 1999 и January 2000 будет равно одному и тому же значению January. Существует много способов избежать дублирования имен полей, и в данном примере мы использовали самый простой — добавление уникального числа, содержащегося в свойстве Ordinal объекта Member.

После того как имена полей компонента ClientDataset1 определены, выполняется цикл перебора строк объекта CellSet, и для каждого ряда мы устанавливаем значение первого поля равным свойству Caption первого элемента коллекции Members соответствующего члена коллекции Positions второй оси (Axis[1]), а затем помещаем значения из соответствующих объектов Cell (доступных с помощью коллекции Item объекта CellSet) в оставшиеся поля. В результате получается набор данных, заполненный двухмерным сечением куба и отображенный в компоненте DBGrid1, как это показано на рис. 4.

Итак, мы создали простейшее приложение для просмотра OLAP-кубов и выполнения MDX-запросов, используя ADO MD. Это лишь элементарный пример для иллюстрации возможностей ADO MD, который можно расширить, например путем добавления бизнес-графики или более «интеллектуального» генератора запросов.

в начало

в начало

Заключение

В данной статье мы рассмотрели расширения ADO — ADO Extension for DDL and Security (ADOX), Jet and Replication Objects (JRO) и ADO Multidimensional (ADO MD). Мы узнали, как использовать объекты ADOX для получения метаданных и создания баз данных «из ничего». Кроме того, мы изучили, как использовать объекты JRO для сжатия баз данных Access и кратко рассказали о процессе репликации баз данных. И, наконец, мы рассмотрели, как можно использовать объекты ADO MD для получения метаданных и данных из многомерных OLAP-хранилищ. Все эти расширения позволяют добавить к Delphi-приложениям многие полезные функции, недоступные с помощью входящих в комплект поставки Delphi компонентов ADO Express.

Отметим, что в настоящее время единственным полноценным источником информации по использованию ADO в Delphi является книга «Advanced Delphi Developer’s Guide to ADO», выпущенная в июне этого года издательством Wordware Publishing (http://www.wordware.com/data/).

Предыдущая страница

КомпьютерПресс 10'2000

Наш канал на Youtube

1999 1 2 3 4 5 6 7 8 9 10 11 12
2000 1 2 3 4 5 6 7 8 9 10 11 12
2001 1 2 3 4 5 6 7 8 9 10 11 12
2002 1 2 3 4 5 6 7 8 9 10 11 12
2003 1 2 3 4 5 6 7 8 9 10 11 12
2004 1 2 3 4 5 6 7 8 9 10 11 12
2005 1 2 3 4 5 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
Популярные статьи
КомпьютерПресс использует