Partager via


Représentation de tables (tabulaire)

Dans les modèles tabulaires, une table est la représentation de base des données ; les données sont présentées sous une forme rectangulaire avec des lignes et des colonnes. Toutes les lignes ont la même forme ou les mêmes colonnes.

Représentation de tables

Dans les modèles objets tabulaires, la table est un objet logique simple : une collection d'enregistrements (appelés lignes) du même type. Chaque ligne est composée d'un nombre constant d'attributs (appelés colonnes) où chaque attribut est de type de données simple

Tables dans AMO

Lorsque vous utilisez AMO pour gérer une table de modèle tabulaire, il n'existe aucune correspondance d'objet un-à-un pour une table dans AMO ; une table est représentée dans AMO par Dimension et MeasureGroup. Toutefois, pour qu'un groupe de mesures existe, Cube doit être défini pour héberger le groupe de mesures ; en outre, pour une dimension, pour qu'un groupe de mesures et un cube existent, un objet de vue de source de données doit être défini pour contenir les définitions de liaison à la source de données. En résumé, pour que toutes les tables existent, une vue de source de données et un cube courants sont requis ; la vue de source de données fournit les liaisons (ou mappages) aux éléments de données dans la dimension et le groupe de mesures qui forment une table logique, le cube est requis pour héberger le côté groupe de mesures de la représentation de la table.

D'un point de vue procédural, une vue de source de données doit être créée avant de définir tout autre objet. L'objet de vue de source de données contient le mappage de tous les objets appropriés au modèle tabulaire, dans la source de données ; le mappage du modèle relationnel est incorporé dans la vue de source de données comme un objet DataSet .NET et est stocké dans la propriété de schéma de la vue de source de données.

L'extrait de code suivant suppose que vous disposez d'une chaîne de connexion du client SQL, d'un dictionnaire d'instructions SELECT qui mappent à toutes les tables dans le modèle relationnel que vous envisagez de représenter dans votre modèle tabulaire, et d'une variable newDataSourceViewName avec le nom de la vue de source de données (généralement le nom de votre base de données relationnelle).

  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();

Une fois la vue de source créée et mise à jour, l'objet de cube doit être créé mais ne peut être mis à jour dans le serveur qu'après la création de la première table. L'objet de cube ne peut pas être vide. L'extrait de code suivant montre comment créer un cube ; il suppose que vous avez une chaîne non vide newCubeName avec le nom du cube déjà validé pour les doublons.

            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()));

Une fois que le cube est défini localement, les tables peuvent être créées et mises à jour. La procédure suivante présente les étapes nécessaires pour créer une table :

  1. Créez la dimension qui représente la table mais ne mettez pas encore à jour le serveur.

  2. Créez l'attribut RowNumber, puis attribuez-le comme attribut clé de la dimension.

  3. Créez les attributs de dimension et marquez-les avec une relation un-à-plusieurs sur RowNumber.

  4. Ajoutez la dimension aux dimensions de cube.

  5. Créez le groupe de mesures qui représente également la table.

  6. Ajoutez la dimension au groupe de mesures.

  7. Créez l'objet de mesure AMO par défaut dans le groupe de mesures. Remarquez qu'un objet de mesure AMO est utilisé pour la première fois ; les mesures calculées et les modèles tabulaires sont définis dans l'objet AMO MdxScripts["MdxScript"].

  8. Créez la partition par défaut.

  9. Mettez à jour la base de données.

L'exemple de code suivant montre comment créer une table :

        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;
        }
AttentionAttention

L'extrait de code ci-dessus ne comporte aucune vérification des erreurs ou nettoyage des procédures en cas d'échec.

Exemple AMO2Tabular

Pour savoir comment utiliser AMO pour créer et manipuler des représentations de base de données, consultez le code source de l'exemple AMO to Tabular (en anglais) ; plus précisément, archivez le fichier source suivant : CreateTable.cs. L'exemple est disponible sur Codeplex. Remarque importante à propos du code : le code est fourni uniquement comme un support aux concepts logiques expliqués ici et ne doit pas être utilisé dans un environnement de production, ni à des fins autres que pédagogiques.