答应过的全盘分享,也必承诺到底,此篇PowerBI功能分享的最后一篇,讲述如何导出数据模型的元数据,笔者定义其为模型的数据字典。
此篇对应功能实现出自:第6波-导出PowerbiDesktop模型数据字典https://www.jianshu.com/p/bc26a8dcdfce
关系型数据库有数据字典,分析型数据库更加要有数据字典,DAX建模过程中,若模型的关系复杂,引用的表过多,生成的度量值、计算列过多,单单从PowerBIDeskTop去查看模型关系非常低效。
当然最好的查看方式,在全宇宙里,只能找到Excel了,没有其他敢跟它叫板,所以理所当然地将PowerBIDeskTop模型的元数据,存放到Excel单元格里,结合Excel的排序、筛选、条件格式、单元格格式等友好的操作方式辅助加工查阅模型元数据。
先上代码,同样地此代码也包含了许多Excel的VSTO开发的精髓-使用ListObject对象返回数据。
public static void OutputDataDic(string cnnString, string dbName, string fileName)
{
string shtName = "数据字典_" + fileName;
shtName = shtName.Length > 31 ? shtName.Substring(0, 31) : shtName;
Excel.Worksheet wht;
try
{
wht = Common.ExcelApp.ActiveWorkbook.Worksheets[shtName];
Common.ExcelApp.DisplayAlerts = false;
wht.Delete();
Common.ExcelApp.DisplayAlerts = true;
}
catch (Exception)
{
}
wht = Common.ExcelApp.Worksheets.Add();
wht.Name = shtName;
var vstoSht = Globals.Factory.GetVstoObject(wht);
ListObject listMeasures = vstoSht.Controls.AddListObject(vstoSht.Range["A1"], "度量值表_" + fileName);
ListObject listDims = vstoSht.Controls.AddListObject(vstoSht.Range["C1"], "维度表_" + fileName);
ListObject listRelationShips = vstoSht.Controls.AddListObject(vstoSht.Range["E1"], "关系表_" + fileName);
using (AMO.Server server = new AMO.Server())
{
server.Connect(cnnString);
AMO.Database pbidDb = server.Databases[dbName];
////获取度量值
GetMeasuresTable(pbidDb, listMeasures);
//获取列字段
GetDimsTable(pbidDb, listDims);
////获取关系
GetRelationsTable(pbidDb, listRelationShips);
};
vstoSht.Activate();
}
获取元数据,主要有度量值、维度字段、及表间关系三类元数据。核心代码如下(上方的方法中,最开始是为了访问Sqlserver的SSAS元数据,有区分1200及1100的不同兼容模型版本写了两套代码,因PowerBIDeskTop只会存在1200及以上的兼容模型,故1100的版本已经清除了):
private static DataSet.DataSet1.MeasuresOfLevel1200DataTable GetMeasuresTableOfLevel1200(AMO.Database tabularDb)
{
TOM.Model model = tabularDb.Model;
DataSet.DataSet1.MeasuresOfLevel1200DataTable measuresTable = new DataSet.DataSet1.MeasuresOfLevel1200DataTable();
foreach (TOM.Table table in model.Tables)
{
foreach (TOM.Measure measure in table.Measures)
{
DataSet.DataSet1.MeasuresOfLevel1200Row dr = measuresTable.NewMeasuresOfLevel1200Row();
dr.Measure = $"[{measure.Name}]";
dr.TableName = table.Name;
dr.DisplayFolder = measure.DisplayFolder;
dr.Description = measure.Description;
dr.FormatString = measure.FormatString;
dr.MeasureExpression = measure.Expression;
dr.Visible = !measure.IsHidden;
dr.DataType = measure.DataType.ToString();
measuresTable.AddMeasuresOfLevel1200Row(dr);
}
}
return measuresTable;
}
private static DataTable GetDimsTableOfLevel1200(AMO.Database tabularDb)
{
TOM.Model model = tabularDb.Model;
DataSet.DataSet1.ColumnNamesDataTable dtColumns = new DataSet.DataSet1.ColumnNamesDataTable();
foreach (TOM.Table table in model.Tables)
{
foreach (TOM.Column col in table.Columns)
{
if (!col.Name.Contains("RowNumber"))
{
TOM.CalculatedColumn calCol = col as TOM.CalculatedColumn;
DataSet.DataSet1.ColumnNamesRow dr = dtColumns.NewColumnNamesRow();
dr.TableName = table.Name;
dr.FieldName = col.Name;
dr.FieldType = col.DataType.ToString();
if (calCol != null)
{
dr.DaxExpression = calCol.Expression;
}
dr.Description = col.Description;
dr.Visible = !col.IsHidden;
dtColumns.AddColumnNamesRow(dr);
}
}
}
return dtColumns.Cast<DataSet.DataSet1.ColumnNamesRow>().Where(s => !s.TableName.StartsWith("LocalDateTable"))
.Where(s => !s.TableName.StartsWith("DateTableTemplate"))
.CopyToDataTable<DataSet.DataSet1.ColumnNamesRow>();
}
private static DataTable GetRelationsTableOfLevel1200(AMO.Database tabularDb)
{
TOM.Model model = tabularDb.Model;
DataSet.DataSet1.关系表DataTable dt = new DataSet.DataSet1.关系表DataTable();
foreach (TOM.Relationship relationship in model.Relationships)
{
TOM.SingleColumnRelationship singleRelationShip = relationship as TOM.SingleColumnRelationship;
DataSet.DataSet1.关系表Row dr = dt.New关系表Row();
dr.ToTable = relationship.ToTable.Name;
dr.FromTable = relationship.FromTable.Name;
dr.IsActive = relationship.IsActive;
if (singleRelationShip != null)
{
dr.FromColumn = singleRelationShip.FromColumn.Name;
dr.ToColumn = singleRelationShip.ToColumn.Name;
dr.FromCardinality = singleRelationShip.FromCardinality.ToString();
dr.ToCardinality = singleRelationShip.ToCardinality.ToString();
}
dt.Add关系表Row(dr);
}
return dt.Cast<DataSet.DataSet1.关系表Row>().Where(s => !s.ToTable.StartsWith("LocalDateTable")).CopyToDataTable<DataSet.DataSet1.关系表Row>();
}
连续四篇高质量开源文章,带大家进走Excel催化剂深水区,国际领先的Excel与PowerBIDeskTop互通互联的高级、极高商业价值的核心代码全放送。
最后再次祝愿PowerBI中国社区越来越好,繁荣发展,更多乐于分享的后来者再接再厉。