Программирование базовых объектов AMO OLAP
Применимо к: SQL Server Analysis Services Azure Analysis Services Fabric/Power BI Premium
Создание сложных объектов служб Analysis Services является простым и простым, но требует внимания к деталям. В этом разделе подробно описывается программирование базовых объектов OLAP. Этот раздел состоит из следующих подразделов.
Объекты класса Dimension
Для администрирования или обработки измерения необходимо запрограммировать Dimension объект .
Создание, удаление и поиск измерения
Создание Dimension объекта выполняется в четыре этапа:
Создайте объект измерения и заполните основные атрибуты.
Основными атрибутами являются: «Имя», «Тип измерения», «Режим хранения», «Привязка к источнику данных», «Имя элемента "Все" атрибута» и другие атрибуты измерения.
Перед созданием измерения необходимо удостоверится, что такое измерение не существует. Если измерение существует, то оно будет удалено и создано повторно.
Создайте атрибуты, определяющие измерение.
Перед его использованием каждый атрибут необходимо добавить в схему отдельно (метод CreateDataItem приведен в конце образца кода), а затем его можно будет добавить в коллекцию атрибутов измерения.
Столбцы «Key» и «Name» должны быть определены во всех атрибутах.
Первичный ключевой атрибут измерения следует определять как AttributeUsage.Key, чтобы было ясно, что этот атрибут является ключом доступа к измерению.
Создайте иерархии, доступ к которым будет получать пользователь для обзора измерения.
При создании иерархий порядок уровней определяется порядком, в котором сверху вниз создаются уровни. Верхним уровнем является добавленный первым в коллекцию уровней иерархии.
Обновите сервер при помощи метода Update текущего измерения.
В следующем примере кода создается измерение Product из таблицы Продуктов Adventure Works в образце базы данных.
static void CreateProductDimension(Database db, string datasourceName)
{
// Create the Product dimension
Dimension dim = db.Dimensions.FindByName("Product");
if ( dim != null)
dim.Drop();
dim = db.Dimensions.Add("Product");
dim.Type = DimensionType.Products;
dim.UnknownMember = UnknownMemberBehavior.Hidden;
dim.AttributeAllMemberName = "All Products";
dim.Source = new DataSourceViewBinding(datasourceName);
dim.StorageMode = DimensionStorageMode.Molap;
#region Create attributes
DimensionAttribute attr;
attr = dim.Attributes.Add("Product Name");
attr.Usage = AttributeUsage.Key;
attr.Type = AttributeType.Product;
attr.OrderBy = OrderBy.Name;
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "ProductKey"));
attr.NameColumn = CreateDataItem(db.DataSourceViews[0], "DimProduct", "EnglishProductName");
attr = dim.Attributes.Add("Product Line");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "ProductLine"));
attr.NameColumn = CreateDataItem(db.DataSourceViews[0], "DimProduct", "ProductLineName");
attr = dim.Attributes.Add("Model Name");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "ModelName"));
attr.AttributeRelationships.Add(new AttributeRelationship("Product Line"));
attr.AttributeRelationships.Add(new AttributeRelationship("Subcategory"));
attr = dim.Attributes.Add("Subcategory");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProductSubcategory", "ProductSubcategoryKey"));
attr.KeyColumns[0].NullProcessing = NullProcessing.UnknownMember;
attr.NameColumn = CreateDataItem(db.DataSourceViews[0], "DimProductSubcategory", "EnglishProductSubcategoryName");
attr.AttributeRelationships.Add(new AttributeRelationship("Category"));
attr = dim.Attributes.Add("Category");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProductCategory", "ProductCategoryKey"));
attr.NameColumn = CreateDataItem(db.DataSourceViews[0], "DimProductCategory", "EnglishProductCategoryName");
attr = dim.Attributes.Add("List Price");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "ListPrice"));
attr.AttributeHierarchyEnabled = false;
attr = dim.Attributes.Add("Size");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "Size"));
attr.AttributeHierarchyEnabled = false;
attr = dim.Attributes.Add("Weight");
attr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "DimProduct", "Weight"));
attr.AttributeHierarchyEnabled = false;
#endregion
#region Create hierarchies
Hierarchy hier;
hier = dim.Hierarchies.Add("Product Model Categories");
hier.AllMemberName = "All Products";
hier.Levels.Add("Category").SourceAttributeID = "Category";
hier.Levels.Add("Subcategory").SourceAttributeID = "Subcategory";
hier.Levels.Add("Model Name").SourceAttributeID = "Model Name";
hier = dim.Hierarchies.Add("Product Categories");
hier.AllMemberName = "All Products";
hier.Levels.Add("Category").SourceAttributeID = "Category";
hier.Levels.Add("Subcategory").SourceAttributeID = "Subcategory";
hier.Levels.Add("Model Name").SourceAttributeID = "Product Name";
hier = dim.Hierarchies.Add("Product Model Lines");
hier.AllMemberName = "All Products";
hier.Levels.Add("Subcategory").SourceAttributeID = "Product Line";
hier.Levels.Add("Model Name").SourceAttributeID = "Model Name";
#endregion
dim.Update();
}
static DataItem CreateDataItem(DataSourceView dsv, string tableName, string columnName)
{
DataTable dataTable = ((DataSourceView)dsv).Schema.Tables[tableName];
DataColumn dataColumn = dataTable.Columns[columnName];
return new DataItem(tableName, columnName,
OleDbTypeConverter.GetRestrictedOleDbType(dataColumn.DataType));
}
Обработка измерения
Обработка измерения выполняется так же просто, как и с помощью метода Dimension Process объекта .
Обработка измерения может затронуть все кубы, использующие это измерение.
В следующем коде выполняется добавочное обновление во всех измерениях указанной базы данных:
static void UpdateAllDimensions(Database db)
{
foreach (Dimension dim in db.Dimensions)
dim.Process(ProcessType.ProcessUpdate);
}
Объекты класса Cube
Для администрирования или обработки куба необходимо запрограммировать Cube объект .
Создание, удаление и поиск куба
Управление кубами осуществляется так же, как управление измерениями. Создание Cube объекта выполняется в четыре этапа:
Создайте объект куба и заполните основные атрибуты.
Основными атрибутами являются «Имя», «Режим хранения», «Привязка к источнику данных», «Мера по умолчанию» и другие атрибуты куба.
Перед созданием куба следует удостовериться, что такой куб не существует. В данном образце, если куб существует, то будет удален, а затем снова создан.
Добавление измерений куба.
Измерения добавляются в текущую коллекцию измерений куба из базы данных; измерения в кубе представляют собой ссылки на коллекцию измерений в базе данных. Каждое измерение необходимо сопоставлять с кубом отдельно. В данном образце измерения сопоставляются с указанием внутреннего идентификатора измерения в базе данных, имени измерения в кубе и идентификатора для этого именованного измерения в кубе.
Обратите внимание, что в образце кода измерение «Date» добавляется три раза и при каждом добавлении используется другое имя измерения куба: «Date», «Ship Date», «Delivery Date». Эти измерения называются "ролевой игрой". Базовое измерение совпадает (Дата), но в таблице фактов это измерение используется в разных "ролях" (Дата заказа, Дата отправки, Дата доставки). См. раздел "Создание, удаление и поиск Группы мер" далее в этом документе, чтобы понять, как определяются "ролевые" измерения.
Создайте группы мер, доступ к которым будет получать пользователь, чтобы просматривать данные куба.
Создание группы мер описывается в разделе «Создание, удаление и поиск группы мер» ниже в этом документе. В этом образце в качестве оболочки для процесса создания группы мер применяются разные методы, по одному на каждую группу мер.
Обновите сервер при помощи метода Update текущего куба.
Метод обновления используется с параметром обновления ExpandFull, что позволяет гарантировать полное обновление всех объектов на сервере.
В следующем образце кода создаются части куба Adventure Works. В этом образце кода не создаются все измерения или группы мер, включенные в образец проекта служб Adventure Works Analysis Services.
static void CreateAdventureWorksCube(Database db, string datasourceName)
{
// Create the Adventure Works cube
Cube cube = db.Cubes.FindByName("Adventure Works");
if ( cube != null)
cube.Drop();
db.Cubes.Add("Adventure Works");
cube.DefaultMeasure = "[Reseller Sales Amount]";
cube.Source = new DataSourceViewBinding(datasourceName);
cube.StorageMode = StorageMode.Molap;
#region Create cube dimensions
Dimension dim;
dim = db.Dimensions.GetByName("Date");
cube.Dimensions.Add(dim.ID, "Date", "Order Date Key - Dim Time");
cube.Dimensions.Add(dim.ID, "Ship Date",
"Ship Date Key - Dim Time");
cube.Dimensions.Add(dim.ID, "Delivery Date",
"Delivery Date Key - Dim Time");
dim = db.Dimensions.GetByName("Customer");
cube.Dimensions.Add(dim.ID);
dim = db.Dimensions.GetByName("Reseller");
cube.Dimensions.Add(dim.ID);
#endregion
#region Create measure groups
CreateSalesReasonsMeasureGroup(cube);
CreateInternetSalesMeasureGroup(cube);
CreateResellerSalesMeasureGroup(cube);
CreateCustomersMeasureGroup(cube);
CreateCurrencyRatesMeasureGroup(cube);
#endregion
cube.Update(UpdateOptions.ExpandFull);
}
Обработка куба
Обработка куба выполняется так же просто, как и с помощью метода Cube Process объекта . При обработке куба обрабатываются все группы мер в этом кубе, а также все секции в группах мер. В кубе единственными объектами, которые можно обработать, являются секции; группы мер с точки зрения обработки представляют собой всего лишь контейнеры секций. Выбранный для куба тип обработки распространяется и на его секции. Обработка куба и группы мер по существу сводится к обработке измерений и секций.
В следующем коде выполняется полная обработка всех кубов в указанной базе данных:
foreach (Cube cube in db.Cubes)
cube.Process(ProcessType.ProcessFull);
}
Объекты MeasureGroup
Для администрирования или обработки группы мер необходимо запрограммировать MeasureGroup объект .
Создание, удаление и поиск MeasureGroup
Управление группами мер осуществляется так же, как управление измерениями и кубами. Создание MeasureGroup объекта выполняется в следующих шагах:
Создайте объект группы мер и заполните основные атрибуты.
Основными атрибутами являются «Имя», «Режим хранения», «Режим обработки», «Мера по умолчанию» и другие атрибуты группы мер.
Перед созданием группы мер проверьте, что такая группа мер не существует. В приведенном ниже образце кода предусмотрено, что если группа мер существует, то будет удалена и создана повторно.
Создайте меры группы мер. Каждой создаваемой группе мер назначаются следующие атрибуты: имя, статистическая функция, исходный столбец, строка форматирования. Могут быть назначены и другие атрибуты. Обратите внимание, что в приведенном ниже образце кода метод CreateDataItem добавляет столбец в схему.
Добавление измерений группы мер.
Измерения добавляются в текущую коллекцию измерений группы мер из коллекции измерений родительского куба. Как только измерение будет включено в коллекцию измерений группы мер, ключевой столбец из таблицы фактов можно будет сопоставить с этим измерением, чтобы данную группу мер можно было просматривать через это измерение.
В приведенном далее образце кода см. строки под заголовком «Mapping dimension and key column from fact table». Ролевые измерения реализуются путем связывания разных суррогатных ключей с одним измерением под разными именами. С каждым из ролевых измерений («Date», «Ship Date», «Delivery Date») связывается отдельный суррогатный ключ (OrderDateKey, ShipDateKey, DueDateKey). Все ключи берутся из таблицы фактов FactInternetSales.
Добавление спроектированных секций группы мер.
В приведенном далее образце кода в качестве оболочки для процесса создания секции применяется один метод.
Обновите сервер при помощи метода Update текущей группы мер.
В приведенном далее образце кода все группы мер обновляются при обновлении куба.
В следующем образце кода создастся группа мер InternetSales образца проекта служб Adventure Works Analysis Services.
static void CreateInternetSalesMeasureGroup(Cube cube)
{
// Create the Internet Sales measure group
Database db = cube.Parent;
MeasureGroup mg = cube.MeasureGroups.FindByName("Internet Sales");
if ( mg != null)
mg.Drop();
mg = cube.MeasureGroups.Add("Internet Sales");
mg.StorageMode = StorageMode.Molap;
mg.ProcessingMode = ProcessingMode.LazyAggregations;
mg.Type = MeasureGroupType.Sales;
#region Create measures
Measure meas;
meas = mg.Measures.Add("Internet Sales Amount");
meas.AggregateFunction = AggregationFunction.Sum;
meas.FormatString = "Currency";
meas.Source = CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "SalesAmount");
meas = mg.Measures.Add("Internet Order Quantity");
meas.AggregateFunction = AggregationFunction.Sum;
meas.FormatString = "#,#";
meas.Source = CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "OrderQuantity");
meas = mg.Measures.Add("Internet Unit Price");
meas.AggregateFunction = AggregationFunction.Sum;
meas.FormatString = "Currency";
meas.Visible = false;
meas.Source = CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "UnitPrice");
meas = mg.Measures.Add("Internet Total Product Cost");
meas.AggregateFunction = AggregationFunction.Sum;
//meas.MeasureExpression = "[Internet Total Product Cost] * [Average Rate]";
meas.FormatString = "Currency";
meas.Source = CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "TotalProductCost");
meas = mg.Measures.Add("Internet Order Count");
meas.AggregateFunction = AggregationFunction.Count;
meas.FormatString = "#,#";
meas.Source = CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "ProductKey");
#endregion
#region Create measure group dimensions
CubeDimension cubeDim;
RegularMeasureGroupDimension regMgDim;
ManyToManyMeasureGroupDimension mmMgDim;
MeasureGroupAttribute mgAttr;
// Mapping dimension and key column from fact table
// > select dimension and add it to the measure group
cubeDim = cube.Dimensions.GetByName("Date");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
// > add key column from dimension and map it with
// the surrogate key in the fact table
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Date").ID); // this is dimension key column
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "OrderDateKey")); // this surrogate key in fact table
cubeDim = cube.Dimensions.GetByName("Ship Date");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Date").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "ShipDateKey"));
cubeDim = cube.Dimensions.GetByName("Delivery Date");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Date").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "DueDateKey"));
cubeDim = cube.Dimensions.GetByName("Customer");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Full Name").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "CustomerKey"));
cubeDim = cube.Dimensions.GetByName("Product");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Product Name").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "ProductKey"));
cubeDim = cube.Dimensions.GetByName("Source Currency");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Currency").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "CurrencyKey"));
cubeDim = cube.Dimensions.GetByName("Sales Reason");
mmMgDim = new ManyToManyMeasureGroupDimension();
mmMgDim.CubeDimensionID = cubeDim.ID;
mmMgDim.MeasureGroupID = cube.MeasureGroups.GetByName("Sales Reasons").ID;
mg.Dimensions.Add(mmMgDim);
cubeDim = cube.Dimensions.GetByName("Internet Sales Order Details");
regMgDim = new RegularMeasureGroupDimension(cubeDim.ID);
mg.Dimensions.Add(regMgDim);
mgAttr = regMgDim.Attributes.Add(cubeDim.Dimension.Attributes.GetByName("Sales Order Key").ID);
mgAttr.Type = MeasureGroupAttributeType.Granularity;
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "SalesOrderNumber"));
mgAttr.KeyColumns.Add(CreateDataItem(db.DataSourceViews[0], "FactInternetSales", "SalesOrderLineNumber"));
#endregion
#region Create partitions
CreateInternetSalesMeasureGroupPartitions( mg)
#endregion
}
Обработка группы мер
Обработка группы мер выполняется так же просто, как и с помощью метода MeasureGroup Process объекта . При отработке группы мер будут также обработаны и все секции, принадлежащие к ней. Внутри группы мер ее обработка сводится к обработке измерений и секций. См. раздел Обработка секции в этом документе.
В следующем коде выполняется полная обработка всех групп мер указанного куба.
static void FullProcessAllMeasureGroups(Cube cube)
{
foreach (MeasureGroup mg in cube.MeasureGroups)
mg.Process(ProcessType.ProcessFull);
}
Объекты секционирования
Для администрирования или обработки секции необходимо запрограммировать Partition объект .
Создание, удаление и поиск секции
Секции — это простые объекты, которые можно создать за два шага.
Создайте объект секции и заполните его основные атрибуты.
Основными атрибутами являются «Имя», «Режим хранения», «Источник секции», «Срез», а также другие атрибуты группы мер. Атрибут «Источник секции» определяет инструкцию выборки SQL для текущей секции. Срез — это многомерное выражение, указывающее кортеж или набор, который разграничивает часть измерений, которые содержатся в текущей секции, от родительской группы мер. Для секций MOLAP срезы определяются автоматически при каждой обработке секции.
Перед созданием секции следует удостовериться, что такая секция не существует. В приведенном ниже образце кода предусмотрено, что если секция существует, то будет удалена и создана повторно.
Обновите сервер при помощи метода Update текущей секции.
В приведенном далее образце кода все секции обновляются при обновлении куба.
В приведенном ниже образце кода создаются секции для группы мер «InternetSales».
static void CreateInternetSalesMeasureGroupPartitions(MeasureGroup mg)
{
Partition part;
part = mg.Partitions.FindByName("Internet_Sales_184");
if ( part != null)
part.Drop();
part = mg.Partitions.Add("Internet_Sales_184");
part.StorageMode = StorageMode.Molap;
part.Source = new QueryBinding(db.DataSources[0].ID, "SELECT * FROM [dbo].[FactInternetSales] WHERE OrderDateKey <= '184'");
part.Slice = "[Date].[Calendar Year].&[2001]";
part.Annotations.Add("LastOrderDateKey", "184");
part = mg.Partitions.FindByName("Internet_Sales_549");
if ( part != null)
part.Drop();
part = mg.Partitions.Add("Internet_Sales_549");
part.StorageMode = StorageMode.Molap;
part.Source = new QueryBinding(db.DataSources[0].ID, "SELECT * FROM [dbo].[FactInternetSales] WHERE OrderDateKey > '184' AND OrderDateKey <= '549'");
part.Slice = "[Date].[Calendar Year].&[2002]";
part.Annotations.Add("LastOrderDateKey", "549");
part = mg.Partitions.FindByName("Internet_Sales_914");
if ( part != null)
part.Drop();
part = mg.Partitions.Add("Internet_Sales_914");
part.StorageMode = StorageMode.Molap;
part.Source = new QueryBinding(db.DataSources[0].ID, "SELECT * FROM [dbo].[FactInternetSales] WHERE OrderDateKey > '549' AND OrderDateKey <= '914'");
part.Slice = "[Date].[Calendar Year].&[2003]";
part.Annotations.Add("LastOrderDateKey", "914");
}
Обработка секции
Обработка секции так же проста, как и с помощью метода Partition Process объекта .
В следующем образце кода выполняется полная обработка во всех секциях указанной группы мер.
static void FullProcessAllPartitions(MeasureGroup mg)
{
foreach (Partition part in mg.Partitions)
part.Process(ProcessType.ProcessFull);
}
Слияние секций
Слияние секций означает выполнение любых операций, в результате которых две или большее количество секций становятся одной.
Объединение секций является методом Partition объекта . Эта команда выполняет слияние данных из одной или нескольких исходных секций в целевую секцию, а затем удаляет исходные секции.
Можно выполнять слияние секций только в том случае, если они удовлетворяют всем перечисленным далее условиям.
Секции находятся в одной и той же группе мер.
Секции хранятся в одном и том же режиме (MOLAP, HOLAP или ROLAP).
Секции находятся на одном сервере; слияние дистанционно расположенных секций можно выполнить, если они находятся на одной сервере.
В отличие от предыдущих версий, в службах Analysis Services необязательно, чтобы все исходные секции имели одинаковую структуру агрегатов.
Итоговым набором агрегатов для целевой секции становится тот же набор агрегатов, который имелся перед выполнением команды слияния.
В следующем образце кода выполняется слияние всех секций указанной группы мер. Секции сливаются в первую секцию группы мер.
static void MergeAllPartitions(MeasureGroup mg)
{
if (mg.Partitions.Count > 1)
{
Partition[] partArray = new Partition[mg.Partitions.Count - 1];
for (int i = 1; i < mg.Partitions.Count; i++)
partArray[i - 1] = mg.Partitions[i];
mg.Partitions[0].Merge(partArray);
//To have last changes in the server reflected in AMO
mg.Refresh();
}
Объекты Aggregation
Чтобы спроектировать агрегат и применить его к одной или нескольким секциям, необходимо запрограммировать Aggregation объект .
Создание и удаление агрегатов
Агрегаты можно легко создавать и назначать группам мер или секциям с помощью метода DesignAggregations из AggregationDesign объекта . Объект AggregationDesign является отдельным от секции объектом, который AggregationDesign содержится в объекте MeasureGroup . Агрегаты можно проектировать с учетом достижения указанного уровня оптимизации (числа от 0 до 100) либо до указанного уровня хранения (байты). Одну и ту же статистическую схему можно использовать в нескольких секциях.
В следующем образце кода создаются агрегаты для всех секций указанной группы мер. Все существующие в секциях агрегаты удаляются.
static public String DesignAggregationsOnPartitions(MeasureGroup mg, double optimizationWanted, double maxStorageBytes)
{
double optimization = 0;
double storage = 0;
long aggCount = 0;
bool finished = false;
AggregationDesign ad = null;
String aggDesignName;
String AggregationsDesigned = "";
aggDesignName = mg.AggregationPrefix + "_" + mg.Name;
ad = mg.AggregationDesigns.Add();
ad.Name = aggDesignName;
ad.InitializeDesign();
while ((!finished) && (optimization < optimizationWanted) && (storage < maxStorageBytes))
{
ad.DesignAggregations(out optimization, out storage, out aggCount, out finished);
}
ad.FinalizeDesign();
foreach (Partition part in mg.Partitions)
{
part.AggregationDesignID = ad.ID;
AggregationsDesigned += aggDesignName + " = " + aggCount.ToString() + " aggregations designed\r\n\tOptimization: " + optimization.ToString() + "/" + optimizationWanted.ToString() + "\n\r\tStorage: " + storage.ToString() + "/" + maxStorageBytes.ToString() + " ]\n\r";
}
return AggregationsDesigned;
}