public static void Check(MeasureGroup mg, IDesignerHost designer) { if (mg.Measures.Count == 0) { throw new Exception(mg.Name + " has no measures."); } if (mg.IsLinked) { throw new Exception(mg.Name + " is a linked measure group. Run this Measure Group Health Check on the source measure group."); } DataSource oDataSource = mg.Parent.DataSource; DsvTableBinding oTblBinding = new DsvTableBinding(mg.Parent.DataSourceView.ID, GetTableIdForDataItem(mg.Measures[0].Source)); DataTable dtTable = mg.ParentDatabase.DataSourceViews[oTblBinding.DataSourceViewID].Schema.Tables[oTblBinding.TableID]; //check whether this fact table uses an alternate datasource if (dtTable.ExtendedProperties.ContainsKey("DataSourceID")) { oDataSource = mg.ParentDatabase.DataSources[dtTable.ExtendedProperties["DataSourceID"].ToString()]; } Microsoft.DataWarehouse.Design.DataSourceConnection openedDataSourceConnection = Microsoft.DataWarehouse.DataWarehouseUtilities.GetOpenedDataSourceConnection((object)null, oDataSource.ID, oDataSource.Name, oDataSource.ManagedProvider, oDataSource.ConnectionString, oDataSource.Site, false); try { if (openedDataSourceConnection != null) { openedDataSourceConnection.QueryTimeOut = (int)oDataSource.Timeout.TotalSeconds; } } catch { } if (openedDataSourceConnection == null) { MessageBox.Show("Unable to connect to data source [" + oDataSource.Name + "]"); return; } sq = openedDataSourceConnection.Cartridge.IdentStartQuote; fq = openedDataSourceConnection.Cartridge.IdentEndQuote; string sBitSqlDatatype = "bit"; string sCountBig = "count_big("; string sCountBigEnd = ")"; string sFloorFunctionBegin = "floor("; string sFloorFunctionEnd = ")"; if (openedDataSourceConnection.DBServerName == "Oracle") { sBitSqlDatatype = "number(1,0)"; sCountBig = "count("; } else if (openedDataSourceConnection.DBServerName == "Teradata") { sBitSqlDatatype = "numeric(1,0)"; sCountBig = "cast(count("; sCountBigEnd = ") as bigint)"; sFloorFunctionBegin = "cast("; sFloorFunctionEnd = " as bigint)"; } string sFactQuery = GetQueryDefinition(mg.ParentDatabase, mg, oTblBinding, null); StringBuilder sOuterQuery = new StringBuilder(); foreach (Measure m in mg.Measures) { //TODO: measure expressions if ((m.AggregateFunction == AggregationFunction.Sum && !(m.Source.Source is RowBinding)) || m.AggregateFunction == AggregationFunction.AverageOfChildren || m.AggregateFunction == AggregationFunction.ByAccount || m.AggregateFunction == AggregationFunction.FirstChild || m.AggregateFunction == AggregationFunction.FirstNonEmpty || m.AggregateFunction == AggregationFunction.LastChild || m.AggregateFunction == AggregationFunction.LastNonEmpty || m.AggregateFunction == AggregationFunction.Min || m.AggregateFunction == AggregationFunction.Max || m.AggregateFunction == AggregationFunction.None) { ColumnBinding cb = GetColumnBindingForDataItem(m.Source); DataColumn col = mg.Parent.DataSourceView.Schema.Tables[cb.TableID].Columns[cb.ColumnID]; if (col.DataType == typeof(DateTime)) { continue; //DateTime not supported by BIDS Helper except for count and distinct count aggregates } if (sOuterQuery.Length > 0) { sOuterQuery.Append(","); } else { sOuterQuery.Append("select "); } string sNegativeSign = string.Empty; if (col.DataType == typeof(bool)) { sNegativeSign = "-"; //true in sql is 1, but SSAS treats true as -1 } if (m.AggregateFunction == AggregationFunction.Min || m.AggregateFunction == AggregationFunction.Max || m.AggregateFunction == AggregationFunction.None) { sOuterQuery.Append("max(").Append(sNegativeSign).Append("cast(").Append(sq).Append(cb.ColumnID).Append(fq).Append(" as float))").AppendLine(); } else { sOuterQuery.Append("sum(").Append(sNegativeSign).Append("cast(").Append(sq).Append(cb.ColumnID).Append(fq).Append(" as float))").AppendLine(); } sOuterQuery.Append(",min(").Append(sNegativeSign).Append("cast(").Append(sq).Append(cb.ColumnID).Append(fq).Append(" as float))").AppendLine(); sOuterQuery.Append(",max(").Append(sNegativeSign).Append("cast(").Append(sq).Append(cb.ColumnID).Append(fq).Append(" as float))").AppendLine(); sOuterQuery.Append(",cast(max(case when ").Append(sFloorFunctionBegin).Append(sq).Append(cb.ColumnID).Append(fq).Append(sFloorFunctionEnd).Append(" <> ").Append(sq).Append(cb.ColumnID).Append(fq).Append(" then 1 else 0 end) as ").Append(sBitSqlDatatype).Append(")").AppendLine(); sOuterQuery.Append(",cast(max(case when ").Append(sFloorFunctionBegin).Append(sq).Append(cb.ColumnID).Append(fq).Append("*10000.0").Append(sFloorFunctionEnd).Append(" <> cast(").Append(sq).Append(cb.ColumnID).Append(fq).Append("*10000.0 as float) then 1 else 0 end) as ").Append(sBitSqlDatatype).Append(")").AppendLine(); } else if (m.AggregateFunction == AggregationFunction.Count || m.AggregateFunction == AggregationFunction.DistinctCount || (m.AggregateFunction == AggregationFunction.Sum && m.Source.Source is RowBinding)) { if (sOuterQuery.Length > 0) { sOuterQuery.Append(","); } else { sOuterQuery.Append("select "); } if (m.Source.Source is RowBinding) { if (m.AggregateFunction == AggregationFunction.DistinctCount) { throw new Exception("RowBinding on a distinct count not allowed by Analysis Services"); } else { sOuterQuery.Append(sCountBig).Append("*").Append(sCountBigEnd).AppendLine(); } sOuterQuery.Append(",0").AppendLine(); sOuterQuery.Append(",1").AppendLine(); sOuterQuery.Append(",cast(0 as ").Append(sBitSqlDatatype).Append(")").AppendLine(); sOuterQuery.Append(",cast(0 as ").Append(sBitSqlDatatype).Append(")").AppendLine(); } else { ColumnBinding cb = GetColumnBindingForDataItem(m.Source); DataColumn col = mg.Parent.DataSourceView.Schema.Tables[cb.TableID].Columns[cb.ColumnID]; if (m.AggregateFunction == AggregationFunction.DistinctCount) { sOuterQuery.Append(sCountBig).Append("distinct ").Append(sq).Append(cb.ColumnID).Append(fq).Append(sCountBigEnd).AppendLine(); } else if (col.DataType == typeof(Byte[])) { sOuterQuery.Append("sum(cast(case when ").Append(sq).Append(cb.ColumnID).Append(fq).Append(" is not null then 1 else 0 end as float))").AppendLine(); } else { sOuterQuery.Append(sCountBig).Append(sq).Append(cb.ColumnID).Append(fq).Append(sCountBigEnd).AppendLine(); } sOuterQuery.Append(",0").AppendLine(); sOuterQuery.Append(",1").AppendLine(); sOuterQuery.Append(",cast(0 as ").Append(sBitSqlDatatype).Append(")").AppendLine(); sOuterQuery.Append(",cast(0 as ").Append(sBitSqlDatatype).Append(")").AppendLine(); } } else { throw new Exception("Aggregation function " + m.AggregateFunction.ToString() + " not supported!"); } } if (sOuterQuery.Length == 0) { return; } sOuterQuery.AppendLine("from (").Append(sFactQuery).AppendLine(") fact"); DataSet ds = new DataSet(); //openedDataSourceConnection.QueryTimeOut = 0; //just inherit from the datasource openedDataSourceConnection.Fill(ds, sOuterQuery.ToString()); DataRow row = ds.Tables[0].Rows[0]; openedDataSourceConnection.Close(); List <MeasureHealthCheckResult> measureResults = new List <MeasureHealthCheckResult>(); int i = 0; foreach (Measure m in mg.Measures) { if (m.AggregateFunction == AggregationFunction.Sum || m.AggregateFunction == AggregationFunction.AverageOfChildren || m.AggregateFunction == AggregationFunction.ByAccount || m.AggregateFunction == AggregationFunction.FirstChild || m.AggregateFunction == AggregationFunction.FirstNonEmpty || m.AggregateFunction == AggregationFunction.LastChild || m.AggregateFunction == AggregationFunction.LastNonEmpty || m.AggregateFunction == AggregationFunction.Count || m.AggregateFunction == AggregationFunction.DistinctCount || m.AggregateFunction == AggregationFunction.Min || m.AggregateFunction == AggregationFunction.Max || m.AggregateFunction == AggregationFunction.None) { double dsvColMaxValue = 0; bool dsvColAllowsDecimals = false; if (m.Source.Source is ColumnBinding && m.AggregateFunction != AggregationFunction.Count && m.AggregateFunction != AggregationFunction.DistinctCount) { ColumnBinding cb = GetColumnBindingForDataItem(m.Source); DataColumn col = mg.Parent.DataSourceView.Schema.Tables[cb.TableID].Columns[cb.ColumnID]; if (col.DataType == typeof(DateTime)) { continue; //DateTime not supported by BIDS Helper except for count and distinct count aggregates } MeasureDataTypeOption dsvOption = GetMeasureDataTypeOptionForType(col.DataType); if (dsvOption != null) { dsvColMaxValue = dsvOption.max; dsvColAllowsDecimals = dsvOption.allowsDecimals; } } double?total = (!Convert.IsDBNull(row[i * 5]) ? Convert.ToDouble(row[i * 5]) : (double?)null); double?min = (!Convert.IsDBNull(row[i * 5 + 1]) ? Convert.ToDouble(row[i * 5 + 1]) : (double?)null); double?max = (!Convert.IsDBNull(row[i * 5 + 2]) ? Convert.ToDouble(row[i * 5 + 2]) : (double?)null); bool hasDecimals = (!Convert.IsDBNull(row[i * 5 + 3]) ? Convert.ToBoolean(row[i * 5 + 3]) : false); bool hasMoreThan4Decimals = (!Convert.IsDBNull(row[i * 5 + 4]) ? Convert.ToBoolean(row[i * 5 + 4]) : false); MeasureDataTypeOption oldDataTypeOption = GetMeasureDataTypeOptionForMeasure(m); double recommendedMaxValue = double.MaxValue; List <MeasureDataTypeOption> possible = new List <MeasureDataTypeOption>(); foreach (MeasureDataTypeOption option in dataTypeOptions) { if ( (total == null || (option.max >= total && option.min <= total)) && (max == null || option.max >= max) && (min == null || option.min <= min) && (!hasDecimals || option.allowsDecimals) && (!hasMoreThan4Decimals || option.allowsMoreThan4Decimals) ) { possible.Add(option); if ( (total == null || (total * FreeSpaceFactor < option.max && total * FreeSpaceFactor > option.min)) && option.max < recommendedMaxValue && option.max >= dsvColMaxValue && (dsvColAllowsDecimals == option.allowsDecimals) && (option.oleDbType != OleDbType.Single) //never recommend Single ) { recommendedMaxValue = option.max; } } } foreach (MeasureDataTypeOption option in dataTypeOptions) { if (option.max == recommendedMaxValue) { Type dsvDataType = null; //don't bother getting the DSV datatype for count or DistinctCount measures if (m.Source.Source is ColumnBinding && m.AggregateFunction != AggregationFunction.Count && m.AggregateFunction != AggregationFunction.DistinctCount) { ColumnBinding cb = GetColumnBindingForDataItem(m.Source); DataColumn col = mg.Parent.DataSourceView.Schema.Tables[cb.TableID].Columns[cb.ColumnID]; dsvDataType = col.DataType; } MeasureHealthCheckResult result = new MeasureHealthCheckResult(m, FormatDouble(total), FormatDouble(min), FormatDouble(max), hasDecimals, hasMoreThan4Decimals, possible.ToArray(), option, oldDataTypeOption, dsvDataType); measureResults.Add(result); break; } } i++; } else { throw new Exception("Aggregation function " + m.AggregateFunction.ToString() + " not supported"); } } BIDSHelper.SSAS.MeasureGroupHealthCheckForm form = new BIDSHelper.SSAS.MeasureGroupHealthCheckForm(); form.measureDataTypeOptionBindingSource.DataSource = dataTypeOptions; form.measureHealthCheckResultBindingSource.DataSource = measureResults; form.Text = "Measure Group Health Check: " + mg.Name; DialogResult dialogResult = form.ShowDialog(); if (dialogResult == DialogResult.OK) { foreach (MeasureHealthCheckResult r in measureResults) { if (r.CurrentDataType != r.DataType) { //save change if (r.Measure.AggregateFunction == AggregationFunction.Count || r.Measure.AggregateFunction == AggregationFunction.DistinctCount) { r.Measure.DataType = r.DataType.dataType; } else { r.Measure.Source.DataType = r.DataType.oleDbType; r.Measure.DataType = MeasureDataType.Inherited; } //mark cube object as dirty IComponentChangeService changesvc = (IComponentChangeService)designer.GetService(typeof(IComponentChangeService)); changesvc.OnComponentChanging(mg.Parent, null); changesvc.OnComponentChanged(mg.Parent, null, null, null); //marks the cube designer as dirty } } } }
void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { try { MeasureGroup mg = currentStat.IntermediateMeasureGroup; DataSource oDataSource = mg.Parent.DataSource; DsvTableBinding oTblBinding = new DsvTableBinding(mg.Parent.DataSourceView.ID, MeasureGroupHealthCheckPlugin.GetTableIdForDataItem(mg.Measures[0].Source)); DataTable dtTable = mg.ParentDatabase.DataSourceViews[oTblBinding.DataSourceViewID].Schema.Tables[oTblBinding.TableID]; //check whether this fact table uses an alternate datasource if (dtTable.ExtendedProperties.ContainsKey("DataSourceID")) { oDataSource = mg.ParentDatabase.DataSources[dtTable.ExtendedProperties["DataSourceID"].ToString()]; } Microsoft.DataWarehouse.Design.DataSourceConnection openedDataSourceConnection = Microsoft.DataWarehouse.DataWarehouseUtilities.GetOpenedDataSourceConnection((object)null, oDataSource.ID, oDataSource.Name, oDataSource.ManagedProvider, oDataSource.ConnectionString, oDataSource.Site, false); try { if (openedDataSourceConnection != null) { openedDataSourceConnection.QueryTimeOut = 0; } else { throw new Exception("Couldn't open connection from data source " + oDataSource.Name); } command = openedDataSourceConnection.CreateCommand(); command.CommandText = currentStat.SQL; if (backgroundWorker.CancellationPending) { return; } System.Data.Common.DbDataReader reader = null; try { try { reader = command.ExecuteReader(); } catch (Exception innerEx) { if (backgroundWorker.CancellationPending) { return; } else { throw innerEx; } } if (!backgroundWorker.CancellationPending && reader.Read()) { lock (command) { if (Convert.IsDBNull(reader["OriginalRecordCount"])) { currentStat.OriginalRecordCount = null; } else { currentStat.OriginalRecordCount = Convert.ToInt64(reader["OriginalRecordCount"]); } if (Convert.IsDBNull(reader["CompressedRecordCount"])) { currentStat.CompressedRecordCount = null; } else { currentStat.CompressedRecordCount = Convert.ToInt64(reader["CompressedRecordCount"]); } if (Convert.IsDBNull(reader["MatrixDimensionRecordCount"])) { currentStat.MatrixDimensionRecordCount = null; } else { currentStat.MatrixDimensionRecordCount = Convert.ToInt64(reader["MatrixDimensionRecordCount"]); } currentStat.Status = M2MMatrixCompressionPlugin.M2MMatrixCompressionStat.M2MMatrixCompressionStatStatus.Complete; } foreach (M2MMatrixCompressionPlugin.M2MMatrixCompressionStat stat in _list) { if (stat != currentStat && currentStat.IntermediateMeasureGroupName == stat.IntermediateMeasureGroupName && stat.SQL == currentStat.SQL) { stat.OriginalRecordCount = currentStat.OriginalRecordCount; stat.CompressedRecordCount = currentStat.CompressedRecordCount; stat.MatrixDimensionRecordCount = currentStat.MatrixDimensionRecordCount; stat.Status = M2MMatrixCompressionPlugin.M2MMatrixCompressionStat.M2MMatrixCompressionStatStatus.Complete; } } } } finally { try { if ((reader != null) && !reader.IsClosed) { reader.Close(); } } catch { } command = null; } } finally { try { openedDataSourceConnection.Close(); } catch { } } } catch (Exception ex) { currentStat.Error = ex.Message + "\r\n" + ex.StackTrace; foreach (M2MMatrixCompressionPlugin.M2MMatrixCompressionStat stat in _list) { if (stat != currentStat && currentStat.IntermediateMeasureGroupName == stat.IntermediateMeasureGroupName && stat.SQL == currentStat.SQL) { stat.Error = currentStat.Error; } } } }