Rappresentazione delle tabelle (tabulare)

Nei modelli tabulari una tabella è la rappresentazione di base dei dati; i dati vengono presentati in un formato rettangolare di dati composto da righe e colonne. Tutte le righe hanno la stessa forma o colonne.

Rappresentazione delle tabelle

Nei modelli a oggetti tabulari la tabella è un oggetto logico semplice: è una raccolta di record (noto come righe) dello stesso tipo. Ogni riga è costituita da un numero fisso di attributi (noto come colonne) in cui ogni attributo è di tipo di dati semplice

Tabelle in AMO

Quando si utilizza AMO per gestire una tabella di modelli tabulari non c'è una corrispondenza dell'oggetto uno-a-uno per una tabella in AMO; in AMO una tabella viene rappresentata da Dimension e MeasureGroup. Tuttavia, affinché esista un gruppo di misure, deve essere definito un Cube per ospitare il gruppo di misure; inoltre, per una dimensione, affinché esistano un gruppo di misure e un cubo, deve essere definito un oggetto Vista origine dati per mantenere le definizioni di associazione all'origine dati. Riepilogando, affinché tutte le tabelle esistano, sono richiesti una Vista origine dati e il Cubo; la vista origine dati fornisce le associazioni (o mapping) agli elementi di dati nella dimensione e nel gruppo di misure che formano una tabella logica, il cubo è necessario per ospitare la parte del gruppo di misure della rappresentazione della tabella.

Da una prospettiva procedurale è necessario che una vista origine dati venga creata prima di definire qualsiasi altro oggetto. L'oggetto vista origine dati contiene il mapping per tutti gli oggetti attinenti al modello tabulare nell'origine dati; il mapping del modello relazionale è incorporato nella vista origine dati come oggetto DataSet .Net e viene archiviato nella proprietà Schema di DSV.

Nel frammento di codice seguente si presuppone che si disponga di una stringa di connessione a un client SQL, di un dizionario di istruzioni Select di cui è stato eseguito il mapping a tutte le tabelle nel modello relazionale che si desidera rappresentare nel modello tabulare e di una variabile newDataSourceViewName con il nome della vista origine dati (generalmente il nome del database relazionale).

  DataSet newDataSourceViewDataSet = new DataSet(newDataSourceViewName);

  Foreach( String tableName in listOfSqlStatements.Keys)
  {
      String sqlStmt = listOfSqlStatements[tableName];

      DataTable newTable = new DataTable(tableName);

      using (SqlConnection SqlCnx = new SqlConnection(SqlCnxStr))
      {
          SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlStmt, SqlCnx);
          dataAdapter.FillSchema(newTable, SchemaType.Source);
      }
      newDataSourceViewDataSet.Tables.Add(newTable);
  }

  AMO.DataSourceView newDatasourceView = newDatabase.DataSourceViews.AddNew(newDataSourceViewName, newDataSourceViewName);
  newDatasourceView.DataSourceID = newDatasource.ID; //This is the ID of the DataSource object 
  newDatasourceView.Schema = newDataSourceViewDataSet; //Here you are storing all the relational schema in the DSV
  newDatasourceView.Update();

Una volta creata e aggiornata la vista origine dati, è necessario creare l'oggetto cubo ma non deve essere aggiornato nel server fino a quando non sarà stata creata la prima tabella. Non è possibile creare un oggetto cubo vuoto. Nel frammento di codice seguente viene mostrato come creare un cubo; nel frammento si presuppone che si disponga di una stringa newCubeName non vuota con il nome del cubo già convalidato anche per i duplicati.

            modelCube = newDatabase.Cubes.Add(newCubeName, newCubeName);
            modelCube.Source = new AMO.DataSourceViewBinding(newDatasourceView.ID);
            modelCube.StorageMode = AMO.StorageMode.InMemory;
            modelCube.Language = newDatabase.Language;
            modelCube.Collation = newDatabase.Collation;
            //Create initial MdxScript
            AMO.MdxScript mdxScript = modelCube.MdxScripts.Add("MdxScript", "MdxScript");
            StringBuilder initialCommand = new StringBuilder();
            initialCommand.AppendLine("CALCULATE;");
            initialCommand.AppendLine("CREATE MEMBER CURRENTCUBE.Measures.[__No measures defined] AS 1;");
            initialCommand.AppendLine("ALTER CUBE CURRENTCUBE UPDATE DIMENSION Measures, Default_Member = [__No measures defined];");
            mdxScript.Commands.Add(new AMO.Command(initialCommand.ToString()));

Una volta definito in locale il cubo, sarà possibile creare e aggiornare le tabelle. Nella procedura riportata di seguito vengono illustrati i passaggi necessari per creare una tabella:

  1. Creare la dimensione che rappresenta la tabella ma non aggiornare ancora il server.

  2. Creare l'attributo RowNumber e definirlo come attributo chiave della dimensione

  3. Creare gli attributi della dimensione e contrassegnarli con una relazione uno-a-molti con RowNumber

  4. Aggiungere la dimensione alle dimensioni del cubo

  5. Creare il gruppo di misure che rappresenta anche la tabella

  6. Aggiungere la dimensione al gruppo di misure

  7. Creare l'oggetto misura AMO predefinito per il gruppo di misure. Si noti che questa è l'unica occasione in cui viene utilizzato un oggetto misura AMO; le misure calcolate, nei modelli tabulari, sono definite nell'oggetto AMO MdxScripts ["MdxScript"].

  8. Creare una partizione predefinita

  9. Aggiornare il database

Nel frammento di codice seguente viene illustrato come creare una tabella:

        private Boolean CreateTable(
                               AMO.Database db      //the AMO database object where dimension are created
                             , AMO.Cube cb          //the AMO cube where measure group is created
                             , DataTable dataTable  //the schema of the table to be created
                             )
        {
            String tableID = dataTable.TableName;

            if (db.Dimensions.Contains(tableID))
            {
                if (cb.MeasureGroups.Contains(tableID))
                {
                    cb.MeasureGroups[tableID].Measures.Clear();
                    cb.MeasureGroups[tableID].Partitions.Clear();
                    cb.MeasureGroups.Remove(tableID, true);
                }
                if (cb.Dimensions.Contains(tableID))
                {
                    cb.Dimensions.Remove(tableID, true);
                }
                db.Dimensions.Remove(tableID);
            }

            #region Create Dimension
            //Define Dimension general properties
            AMO.Dimension currentDimension = db.Dimensions.AddNew(tableID, tableID);
            currentDimension.Source = new AMO.DataSourceViewBinding(newDatasourceView.ID);
            currentDimension.StorageMode = AMO.DimensionStorageMode.InMemory;
            currentDimension.UnknownMember = AMO.UnknownMemberBehavior.AutomaticNull;
            currentDimension.UnknownMemberName = "Unknown";
            currentDimension.ErrorConfiguration = new AMO.ErrorConfiguration();
            currentDimension.ErrorConfiguration.KeyNotFound = AMO.ErrorOption.IgnoreError;
            currentDimension.ErrorConfiguration.KeyDuplicate = AMO.ErrorOption.ReportAndStop;
            currentDimension.ErrorConfiguration.NullKeyNotAllowed = AMO.ErrorOption.ReportAndStop;
            currentDimension.Language = db.Language;
            currentDimension.Collation = db.Collation;
            currentDimension.ProactiveCaching = new AMO.ProactiveCaching();
            TimeSpan defaultProactiveChachingTimeSpan = new TimeSpan(0, 0, -1);
            currentDimension.ProactiveCaching.SilenceInterval = defaultProactiveChachingTimeSpan;
            currentDimension.ProactiveCaching.Latency = defaultProactiveChachingTimeSpan;
            currentDimension.ProactiveCaching.SilenceOverrideInterval = defaultProactiveChachingTimeSpan;
            currentDimension.ProactiveCaching.ForceRebuildInterval = defaultProactiveChachingTimeSpan;
            currentDimension.ProactiveCaching.Source = new AMO.ProactiveCachingInheritedBinding();

            //Manualy add a "RowNumber" attribute as the key attribute of the dimension, until a primary key is defined
            //"RowNumber" a required column for a tabular model and has to be of type AMO.AttributeType.RowNumber and binding AMO.RowNumberBinding
            //The name of the "RowNumber" attribute can be any name, as long as type and binding are correctly set
            //By default the MS client tools set the column name and column ID of the RowNumber attribute to "RowNumber"
            //In this sample, to avoid problems, on any customer table that contains a column named 'RowNumber' 
            //the Id value of the column (in the dimension object) will be renamed to 'RowNumber_in_<TableName>' and the Name of the column will remain "RowNumber"

            AMO.DimensionAttribute currentAttribute = currentDimension.Attributes.Add("RowNumber", "RowNumber");
            currentAttribute.Type = AMO.AttributeType.RowNumber;
            currentAttribute.KeyUniquenessGuarantee = true;
            currentAttribute.Usage = AMO.AttributeUsage.Key;
            currentAttribute.KeyColumns.Add(new AMO.DataItem());
            currentAttribute.KeyColumns[0].DataType = System.Data.OleDb.OleDbType.Integer;
            currentAttribute.KeyColumns[0].DataSize = 4;
            currentAttribute.KeyColumns[0].NullProcessing = AMO.NullProcessing.Error;
            currentAttribute.KeyColumns[0].Source = new AMO.RowNumberBinding();
            currentAttribute.NameColumn = new AMO.DataItem();
            currentAttribute.NameColumn.DataType = System.Data.OleDb.OleDbType.WChar;
            currentAttribute.NameColumn.DataSize = 4;
            currentAttribute.NameColumn.NullProcessing = AMO.NullProcessing.ZeroOrBlank;
            currentAttribute.NameColumn.Source = new AMO.RowNumberBinding();
            currentAttribute.OrderBy = AMO.OrderBy.Key;
            currentAttribute.AttributeHierarchyVisible = false;
            //Deferring AttributeRelationships until after adding each other attribute
            //Add each column in the table as an attribute in the dimension
            foreach (DataColumn dataColumn in dataTable.Columns)
            {
                string attributeID, attributeName;
                if (dataColumn.ColumnName != "RowNumber")
                {
                    attributeID = dataColumn.ColumnName;
                }
                else
                {
                    attributeID = string.Format("RowNumber_in_{0}", dataTable.TableName);
                }
                attributeName = dataColumn.ColumnName;
                currentAttribute = currentDimension.Attributes.Add(attributeName, attributeID);
                currentAttribute.Usage = AMO.AttributeUsage.Regular;
                currentAttribute.KeyUniquenessGuarantee = false;
                currentAttribute.KeyColumns.Add(new AMO.DataItem(dataTable.TableName, dataColumn.ColumnName, AMO.OleDbTypeConverter.GetRestrictedOleDbType(dataColumn.DataType)));
                currentAttribute.KeyColumns[0].Source = new AMO.ColumnBinding(dataTable.TableName, dataColumn.ColumnName);
                currentAttribute.KeyColumns[0].NullProcessing = AMO.NullProcessing.Preserve;
                currentAttribute.NameColumn = new AMO.DataItem(dataTable.TableName, dataColumn.ColumnName, System.Data.OleDb.OleDbType.WChar);
                currentAttribute.NameColumn.Source = new AMO.ColumnBinding(dataTable.TableName, dataColumn.ColumnName);
                currentAttribute.NameColumn.NullProcessing = AMO.NullProcessing.ZeroOrBlank;
                currentAttribute.OrderBy = AMO.OrderBy.Key;
                AMO.AttributeRelationship currentAttributeRelationship = currentDimension.Attributes["RowNumber"].AttributeRelationships.Add(currentAttribute.ID);
                currentAttributeRelationship.Cardinality = AMO.Cardinality.Many;
                currentAttributeRelationship.OverrideBehavior = AMO.OverrideBehavior.None;
            }
            #endregion

            #region Add Dimension to Model cube
            cb.Dimensions.Add(tableID, tableID, tableID);
            #endregion

            #region Add MeasureGroup to Model cube
            AMO.MeasureGroup currentMeasureGroup = cb.MeasureGroups.Add(tableID, tableID);
            currentMeasureGroup.StorageMode = AMO.StorageMode.InMemory;
            currentMeasureGroup.ProcessingMode = AMO.ProcessingMode.Regular;

            //Adding Dimension
            AMO.DegenerateMeasureGroupDimension currentMGDim = new AMO.DegenerateMeasureGroupDimension(tableID);
            currentMeasureGroup.Dimensions.Add(currentMGDim);
            currentMGDim.ShareDimensionStorage = AMO.StorageSharingMode.Shared;
            currentMGDim.CubeDimensionID = tableID;
            foreach (AMO.CubeAttribute ca in cb.Dimensions[tableID].Attributes)
            {
                AMO.MeasureGroupAttribute mga = new AMO.MeasureGroupAttribute(ca.AttributeID);
                if (mga.AttributeID == "RowNumber")
                {
                    mga.Type = AMO.MeasureGroupAttributeType.Granularity;
                    AMO.DataItem rowNumberKeyColumn = new AMO.DataItem(new AMO.ColumnBinding(tableID, "RowNumber"));
                    rowNumberKeyColumn.DataType = System.Data.OleDb.OleDbType.Integer;
                    mga.KeyColumns.Add(rowNumberKeyColumn);
                }
                else
                {
                    foreach (AMO.DataItem di in ca.Attribute.KeyColumns)
                    {
                        AMO.DataItem keyColumn = new AMO.DataItem(new AMO.ColumnBinding(tableID, ((AMO.ColumnBinding)di.Source).ColumnID));
                        keyColumn.DataType = di.DataType;
                        keyColumn.NullProcessing = AMO.NullProcessing.Preserve;
                        keyColumn.InvalidXmlCharacters = AMO.InvalidXmlCharacters.Remove;
                        mga.KeyColumns.Add(keyColumn);
                    }
                }
                currentMGDim.Attributes.Add(mga);
            }

            //Adding default Measure
            String defaultMeasureID = string.Concat("_Count ", tableID);
            AMO.Measure currentMeasure = currentMeasureGroup.Measures.Add(defaultMeasureID, defaultMeasureID);
            currentMeasure.AggregateFunction = AMO.AggregationFunction.Count;
            currentMeasure.DataType = AMO.MeasureDataType.BigInt;
            AMO.DataItem currentMeasureSource = new AMO.DataItem(new AMO.RowBinding(tableID));
            currentMeasureSource.DataType = System.Data.OleDb.OleDbType.BigInt;
            currentMeasure.Source = currentMeasureSource;

            //Partitions
            AMO.Partition currentPartition = new AMO.Partition(tableID, tableID);
            currentPartition.StorageMode = AMO.StorageMode.InMemory;
            currentPartition.ProcessingMode = AMO.ProcessingMode.Regular;
            currentPartition.Source = new AMO.QueryBinding(newDatasource.ID, (String)dataTable.ExtendedProperties["sqlStmt"]);
            currentMeasureGroup.Partitions.Add(currentPartition);

            #endregion

            #region Update new objects in database
            db.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.UpdateOrCreate);
            #endregion

            return true;
        }
Nota di attenzioneAttenzione

Nel frammento di codice precedente non si dispone di controllo degli errori o di procedure di pulizia in caso di errore.

Esempio AMO2Tabular

Per comprendere le modalità di utilizzo di AMO (Analysis Management Objects) per creare e modificare le rappresentazioni di database vedere il codice sorgente dell'esempio AMO to Tabular; in particolare controllare nel seguente file di origine: CreateTable.cs. L'esempio è disponibile all'indirizzo Codeplex. Nota importante sul codice: il codice viene fornito solo come supporto ai concetti logici illustrati in questo argomento e non deve essere utilizzato in un ambiente di produzione né per altri scopi se non quello formativo.