Share via


Représentation (tabulaire) d'une relation

Une relation est une connexion entre deux tables de données. La relation établit la façon dont les données des deux tables doivent être mises en corrélation.

Consultez Représentation (tabulaire) d'une relation pour une explication détaillée sur la façon de créer et manipuler la représentation d'une relation.

Représentation d'une relation

Dans les modèles tabulaires, plusieurs relations peuvent être définies entre deux tables. Lorsque plusieurs relations entre deux tables sont définies, une seule peut être définie en tant que relation par défaut pour le modèle et est nommée comme relation active ; toutes les autres relations sont nommées comme inactives.

Relation dans AMO

En termes d'objets AMO, toutes les relations inactives ont une représentation de relation de mappage un-à-un avec Relationship et aucun autre objet AMO principal n'est requis ; pour la relation active, d'autres exigences existent et un mappage à ReferenceMeasureGroupDimension est également requis.

Les extraits de code suivants montrent comment créer une relation dans les modèles tabulaires, comment activer une relation et comment définir une clé primaire dans une table (autre que « RowNumber »). Pour créer une relation active, une clé primaire doit être définie dans la table de clé primaire (PKTableName) de la relation (le côté « un » de la relation). L'exemple ci-après crée la clé primaire sur PKColumnName si aucune clé primaire n'est définie dans cette colonne. Les relations inactives peuvent être créées sans clé primaire sur la colonne de clé primaire.

        private Boolean createRelationship(string PKTableName, string PKColumnName, string MVTableName, string MVColumnName, AMO.Database tabularDb, string cubeName, Boolean forceActive)
        {
            //verify input parameters
            if(     string.IsNullOrEmpty(PKTableName) || string.IsNullOrWhiteSpace(PKTableName)
                ||  string.IsNullOrEmpty(PKColumnName) || string.IsNullOrWhiteSpace(PKColumnName)
                ||  string.IsNullOrEmpty(MVTableName) || string.IsNullOrWhiteSpace(MVTableName)
                ||  string.IsNullOrEmpty(MVColumnName) || string.IsNullOrWhiteSpace(MVColumnName)
                ||  (tabularDb == null)
                ) return false;
            if(!tabularDb.Dimensions.ContainsName(PKTableName) || !tabularDb.Dimensions.ContainsName(MVTableName)) return false;
            if(!tabularDb.Dimensions[PKTableName].Attributes.ContainsName(PKColumnName) || !tabularDb.Dimensions[MVTableName].Attributes.ContainsName(MVColumnName)) return false;

            //Verify underlying cube structure
            if (!tabularDb.Cubes[cubeName].Dimensions.ContainsName(PKTableName) || !tabularDb.Cubes[cubeName].Dimensions.ContainsName(MVTableName)) return false; //Should return an exception!!
            if (!tabularDb.Cubes[cubeName].MeasureGroups.ContainsName(PKTableName) || !tabularDb.Cubes[cubeName].MeasureGroups.ContainsName(MVTableName)) return false; //Should return an exception!!
            
            //Make sure PKTableName.PKColumnName  is set as PK ==> <attribute>.usage == AMO.AttributeUsage.Key
            if (tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].Usage != AMO.AttributeUsage.Key)
            {
                //... here we are 'fixing', if there is an issue with PKTableName.PKColumnName not beeing the PK of the table
                setPKColumn(tabularDb, PKTableName, PKColumnName);
            }



            //Terminology note: 
            // -   the many side of the relationship is named the From end in AMO
            // -   the PK side of the relationship in named the To end in AMO
            //
            //It seems relationships flow FROM the many side of the relationship in TO the primary key side of the relationship in AMO
            //            
            //Verify the relationship we are creating does not exist, regardless of name.
            //if it exists, return true (no need to recreate it)
            //if it doesn't exists it will be created after this validation

            
            
            // 
            foreach (AMO.Relationship currentRelationship in tabularDb.Dimensions[MVTableName].Relationships)
            {
                if ((currentRelationship.FromRelationshipEnd.Attributes[0].AttributeID == MVColumnName)
                    && (currentRelationship.ToRelationshipEnd.DimensionID == PKTableName)
                    && (currentRelationship.ToRelationshipEnd.Attributes[0].AttributeID == PKColumnName))
                {
                    if (forceActive)
                    {
                        //Activate the relationship 
                        setActiveRelationship(tabularDb.Cubes[cubeName], MVTableName, MVColumnName, PKTableName, currentRelationship.ID);
                        //Update server with changes made here
                        tabularDb.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.CreateOrReplace);
                        tabularDb.Cubes[cubeName].MeasureGroups[MVTableName].Process(AMO.ProcessType.ProcessFull);
                    }
                    return true;
                }
            }

            //A relationship like the one to be created does not exist; ergo, let's create it:

            //First, create the INACTIVE relationship definitions in the MultipleValues end of the relationship
            #region define unique name for relationship
            string newRelationshipID = string.Format("Relationship _{0}_{1}_ to _{2}_{3}_", MVTableName, MVColumnName, PKTableName, PKColumnName);
            int rootLen = newRelationshipID.Length;
            for (int i = 0; tabularDb.Dimensions[MVTableName].Relationships.Contains(newRelationshipID); )
            {
                newRelationshipID = string.Format("{0}_{1,8:0}", newRelationshipID.Substring(0, rootLen), i);
            }
            #endregion
            AMO.Relationship newRelationship = tabularDb.Dimensions[MVTableName].Relationships.Add(newRelationshipID);

            newRelationship.FromRelationshipEnd.DimensionID = MVTableName;
            newRelationship.FromRelationshipEnd.Attributes.Add(MVColumnName);
            newRelationship.FromRelationshipEnd.Multiplicity = AMO.Multiplicity.Many;
            newRelationship.FromRelationshipEnd.Role = string.Empty;
            newRelationship.ToRelationshipEnd.DimensionID = PKTableName;
            newRelationship.ToRelationshipEnd.Attributes.Add(PKColumnName);
            newRelationship.ToRelationshipEnd.Multiplicity = AMO.Multiplicity.One;
            newRelationship.ToRelationshipEnd.Role = string.Empty;

            //Update server to create relationship
            tabularDb.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.UpdateOrCreate);
            tabularDb.Dimensions[MVTableName].Process(AMO.ProcessType.ProcessDefault);
            tabularDb.Dimensions[PKTableName].Process(AMO.ProcessType.ProcessDefault);



            //Second, activate the relationship if relationship is to be set as the active relationship: 'forceActive==true'
            //... an inactive relationship needs only to be created on the dimensions object
            if (forceActive)
            {
                //Activate the relationship 
                setActiveRelationship(tabularDb.Cubes[cubeName], MVTableName, MVColumnName, PKTableName, newRelationshipID);                
            }           
            return true;
        }

        private void setActiveRelationship(AMO.Cube currentCube, string MVTableName, string MVColumnName, string PKTableName, string relationshipID)
        {
            if (!currentCube.MeasureGroups.Contains(MVTableName))
            {
                throw new AMO.AmoException(string.Format("Cube [{0}] does not contain Measure Group [{1}]\nError activating relationship [{2}]: [{4}] <--- [{1}].[{3}]"
                                                        , currentCube.Name, MVTableName, relationshipID, MVColumnName, PKTableName));
            }
            AMO.MeasureGroup currentMG = currentCube.MeasureGroups[MVTableName];

            if (!currentMG.Dimensions.Contains(PKTableName))
            {
                AMO.ReferenceMeasureGroupDimension newReferenceMGDim = new AMO.ReferenceMeasureGroupDimension();
                newReferenceMGDim.CubeDimensionID = PKTableName;
                newReferenceMGDim.IntermediateCubeDimensionID = MVTableName;
                newReferenceMGDim.IntermediateGranularityAttributeID = MVColumnName;
                newReferenceMGDim.Materialization = AMO.ReferenceDimensionMaterialization.Regular;
                newReferenceMGDim.RelationshipID = relationshipID;
                foreach (AMO.CubeAttribute PKAttribute in currentCube.Dimensions[PKTableName].Attributes)
                {
                    AMO.MeasureGroupAttribute PKMGAttribute = newReferenceMGDim.Attributes.Add(PKAttribute.AttributeID);
                    OleDbType PKMGAttributeType = PKAttribute.Attribute.KeyColumns[0].DataType;
                    PKMGAttribute.KeyColumns.Add(new AMO.DataItem(PKTableName, PKAttribute.AttributeID, PKMGAttributeType));
                    PKMGAttribute.KeyColumns[0].Source = new AMO.ColumnBinding(PKTableName, PKAttribute.AttributeID);
                }
                currentMG.Dimensions.Add(newReferenceMGDim);

                AMO.ValidationErrorCollection errors = new AMO.ValidationErrorCollection();

                newReferenceMGDim.Validate(errors, true);
                if (errors.Count > 0)
                {
                    StringBuilder errorMessages = new StringBuilder();
                    foreach (AMO.ValidationError err in errors)
                    {
                        errorMessages.AppendLine(string.Format("At {2}: # {0} : {1}", err.ErrorCode, err.FullErrorText, err.Source));
                    }
                    throw new AMO.AmoException(errorMessages.ToString());
                }
                //Update changes in the server
                currentMG.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.CreateOrReplace);
            }
            else
            {
                AMO.ReferenceMeasureGroupDimension currentReferenceMGDim = (AMO.ReferenceMeasureGroupDimension)currentMG.Dimensions[PKTableName];
                currentReferenceMGDim.RelationshipID = relationshipID;
                currentReferenceMGDim.IntermediateGranularityAttributeID = MVColumnName;
                //Update changes in the server
                currentMG.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.UpdateOrCreate);
            }
            //process MG to activate relationship
            currentMG.Process(AMO.ProcessType.ProcessFull);

        }

        private void setPKColumn(AMO.Database tabularDb, string PKTableName, string PKColumnName)
        {
                //Find all 'unwanted' Key attributes, remove their Key definitions and include the attributes in the ["RowNumber"].AttributeRelationships
                foreach (AMO.DimensionAttribute currentDimAttribute in tabularDb.Dimensions[PKTableName].Attributes)
                {
                    if ((currentDimAttribute.Usage == AMO.AttributeUsage.Key) && (currentDimAttribute.ID != PKColumnName))
                    {
                        currentDimAttribute.Usage = AMO.AttributeUsage.Regular;
                        if (currentDimAttribute.ID != "RowNumber")
                        {
                            currentDimAttribute.KeyColumns[0].NullProcessing = AMO.NullProcessing.Preserve;
                            currentDimAttribute.AttributeRelationships.Clear();
                            if (!tabularDb.Dimensions[PKTableName].Attributes["RowNumber"].AttributeRelationships.ContainsName(currentDimAttribute.ID))
                            {
                                AMO.DimensionAttribute currentAttribute = tabularDb.Dimensions[PKTableName].Attributes[currentDimAttribute.ID];
                                AMO.AttributeRelationship currentAttributeRelationship = tabularDb.Dimensions[PKTableName].Attributes["RowNumber"].AttributeRelationships.Add(currentAttribute.ID);
                                currentAttributeRelationship.OverrideBehavior = AMO.OverrideBehavior.None;
                            }
                            tabularDb.Dimensions[PKTableName].Attributes["RowNumber"].AttributeRelationships[currentDimAttribute.ID].Cardinality = AMO.Cardinality.Many;
                        }
                    }
                }

                //Remove PKColumnName from ["RowNumber"].AttributeRelationships
                int PKAtribRelationshipPosition = tabularDb.Dimensions[PKTableName].Attributes["RowNumber"].AttributeRelationships.IndexOf(PKColumnName);
                if (PKAtribRelationshipPosition != -1) tabularDb.Dimensions[PKTableName].Attributes["RowNumber"].AttributeRelationships.RemoveAt(PKAtribRelationshipPosition, true);

                //Define PKColumnName as Key and add ["RowNumber"] to PKColumnName.AttributeRelationships with cardinality of One
                tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].Usage = AMO.AttributeUsage.Key;
                tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].KeyColumns[0].NullProcessing = AMO.NullProcessing.Error;
                if (!tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].AttributeRelationships.ContainsName("RowNumber"))
                {
                    AMO.DimensionAttribute currentAttribute = tabularDb.Dimensions[PKTableName].Attributes["RowNumber"];
                    AMO.AttributeRelationship currentAttributeRelationship = tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].AttributeRelationships.Add(currentAttribute.ID);
                    currentAttributeRelationship.OverrideBehavior = AMO.OverrideBehavior.None;
                }
                tabularDb.Dimensions[PKTableName].Attributes[PKColumnName].AttributeRelationships["RowNumber"].Cardinality = AMO.Cardinality.One;

                //Update Table before going creating the relationship
                tabularDb.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.UpdateOrCreate);
                tabularDb.Dimensions[PKTableName].Process(AMO.ProcessType.ProcessDefault);              

        }

Exemple AMO2Tabular

Toutefois, pour connaître la façon d'utiliser AMO pour créer et manipuler des représentations de relation consultez le code source de l'exemple Objets AMO vers objets tabulaires. 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.