表格模型定义语言 (TMDL)

适用于:SQL Server 2016 及更高版本的 Analysis Services Azure Analysis Services Fabric/Power BI Premium

重要

表格模型定义语言 (TMDL) 目前处于预览状态。 在预览版期间,功能和文档可能会更改。

表格模型定义语言 (TMDL) 是兼容级别 1200 或更高级别的表格数据模型的对象模型定义语法。

TMDL 的关键元素包括:

  • 与整个 表格对象模型完全兼容, (TOM) 。 每个 TMDL 对象都公开与 TOM 相同的属性。
  • 基于文本且针对人工交互和可读性进行优化。 TMDL 使用类似于 YAML 的语法。 每个 TMDL 对象都以带最小分隔符的文本表示,并使用缩进来取消标记父子关系。
  • 更好的编辑体验,尤其是在具有嵌入不同内容类型的表达式的属性上,例如数据分析表达式 (DAX) 和 M。
  • 更适合协作,因为它的文件夹表示形式,其中每个模型对象都有单独的文件表示形式,使其更便于源代码管理。

TMDL 的一个重要方面是使用空格缩进来表示 TOM 对象结构。 以下示例演示了使用 TMDL 时表示表格模型是多么容易:

database Sales
	compatibilityLevel: 1567	

model Model    
    culture: en-US    

table Sales
    
    partition 'Sales-Partition' = m
        mode: import
        source = 
            let
                Source = Sql.Database(Server, Database)
                …
    
    measure 'Sales Amount' = SUMX('Sales', 'Sales'[Quantity] * 'Sales'[Net Price])
        formatString: $ #,##0
   
    column 'Product Key'
        dataType: int64
        isHidden
        sourceColumn: ProductKey
        summarizeBy: None
 
    column Quantity
        dataType: int64
        isHidden
        sourceColumn: Quantity
        summarizeBy: None

    column 'Net Price'
        dataType: int64
        isHidden
        sourceColumn: "Net Price"
        summarizeBy: none

table Product
    
    partition 'Product-Partition' = m
        mode: import
        source = 
            let
                Source = Sql.Database(Server, Database),
                …

    column 'Product Key'
        dataType: int64
        isKey
        sourceColumn: ProductKey
        summarizeBy: none

relationship cdb6e6a9-c9d1-42b9-b9e0-484a1bc7e123
    fromColumn: Sales.'Product Key'
    toColumn: Product.'Product Key'

role Role_Store1
    modelPermission: read

    tablePermission Store = 'Store'[Store Code] IN {1,10,20,30}

expression Server = "localhost" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true]

expression Database = "Contoso" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true]

TMDL 文件夹结构

与 TMSL 不同,TMDL 使用文件夹结构。 默认文件夹结构只有一个级别的 子文件夹,所有子文件夹都包含 .tmdl 文件:

  • cultures
  • 透视图
  • 角色

以及用于

  • database
  • 模型
  • 关系
  • 表达式
  • datasources

下面是 TMDL 文件夹的示例:

具有模型的 TMDL 表示形式的文件夹

定义包括:

  • 一个用于数据库定义的文件。
  • 一个用于模型定义的文件。
  • 一个文件用于模型 中的所有 数据源。
  • 一个文件用于模型 中的所有 表达式。
  • 一个文件用于模型 中的所有 关系。
  • 每个区域性语言架构 一个文件。
  • 每个透视一个文件。
  • 每个角色的一个文件。
  • 每个表一个文件。
  • 表的所有内部元数据属性 (列、层次结构、分区,...) 元数据都位于父表 TMDL 文件中。

TMDL API

TMSL) 的表格模型脚本语言 (类似,有一个用于处理 TMDL 序列化的类。 对于 TMDL,类是 Microsoft.AnalysisServices.Tabular 命名空间下的 TmdlSerializer

TmdlSerializer 类公开用于序列化和反序列化 TMDL 文档的方法:

文件夹序列化

public static void SerializeDatabaseToFolder (Database database, string path)

  • 接收 TOM 数据库对象和 TMDL 输出路径。
  • 将 TOM 数据库序列化为 TMDL 文件夹表示形式。

详细了解 如何序列化到文件夹

public static Database DeserializeDatabaseFromFolder (string path)

  • 接收 TMDL 文件夹的完整路径。
  • 返回 TMDL 文件夹的 TOM 数据库对象表示形式。

详细了解 如何从文件夹反序列化

字符串序列化

public static string SerializeObject (MetadataObject object, bool qualifyObject = true)

  • 接收 TOM 对象并返回其 TMDL 文本表示形式。

详细了解 如何将对象序列化为字符串

流序列化

可以对 TMDL 进行序列化/反序列化,以便将 TOM 对象转换为字节流,以便跨平台进行存储、传输和互操作性。 流 API 还允许你控制加载哪些 TMDL 文档和输出哪些 TMDL 文档。

TMDL 流序列化由 MetadataSerializationContext 类处理。

详细了解 如何使用流向/从 TMDL 序列化

TMDL 语言

对象声明

除服务器对象外,TMDL 在 Microsoft.AnalysisServices.Tabular 命名空间中公开整个 TOM 数据库对象树。

TMDL 对象是通过指定 TOM 对象类型后跟其名称来声明的。 在下面的代码示例中,每个对象类型、modeltablecolumn 后跟一个对象名称。

model Model    
    culture: en-US    

table Sales
    
    measure Sales = SUM(…)
        formatString: $ #,##0

    column 'Customer Key'
        datatype: int64
        sourceColumn: CustomerKey

对象(如 partitionmeasure )具有 默认属性 ,这些属性可以在等于 之后分配 (=) 分隔符,该分隔符位于对象声明的同一行或多行 表达式的以下行中:

table Sales

    partition Sales-Part1 = m
        mode: import
        ...        
    
    measure Sales = SUM(…)
        formatString: $ #,##0

    measure 'Sales (ly)' = 
            var ly = ...
            return ly
        formatString: $ #,##0

如果 TMDL 对象名称包含以下任何字符,则必须用单引号 (') 引起来:

  • 点 (.)
  • 等于 (=)
  • 冒号 ()
  • 单引号 (')
  • 空格 ( )

如果对象名称包含单引号 (') ,请使用两个单引号来转义它。

对象属性

对象属性是在对象声明或对象默认属性多行表达式之后指定的。 对象属性值在冒号 (后面指定:) 分隔符。 例如:

table Sales
    lineageTag: e9374b9a-faee-4f9e-b2e7-d9aafb9d6a91    

    column Quantity
        dataType: int64
        isHidden
        isAvailableInMdx: false
        sourceColumn: Quantity

    measure 'Sales Amount' = 
            var result = SUMX(...)
            return result
  formatString: $ #,##0
  displayFolder: " My ""Amazing"" Measures"

以下规则适用于属性值:

  • 值必须位于冒号后面的同一行中,不能有多个行。

  • 文本属性值

    • 前导和尾随双引号是可选的,在序列化过程中会自动去除。
    • 如果文本包含尾随或前导空格,则必须用双引号 () 括起来。
    • 当用双引号括起来时,如果值包含双引号,请使用两个双引号来转义它们 (请参阅 displayFolder 上面的代码示例中的属性) 。
  • 可以使用标准键/值对语法(如'isAvailableInMdx'上一示例中的 属性)设置布尔属性。 还可以使用快捷方式语法来设置它们,其中仅声明属性名称,并且 true 是隐含的。 例如,请参阅上一示例中的“isHidden”属性。

命名对象引用

某些对象属性保存对其他模型对象的引用,例如:

  • 层次结构级别的列引用。
  • 每个表列中的 sortByColumn 引用。
  • 透视中的表/列/度量值引用。

在 TMDL 中,引用使用对象名称进行,并遵循相同的转义和单引号 (') 对象声明的封闭要求。 在下面的代码示例中,可以看到保存对另一个对象的引用的对象属性:column.sortByColumnlevel.columnperspectiveMeasure.measureperspectiveTable.table


table Product

    column Category
        sortByColumn: 'Category Order'    

 hierarchy 'Product Hierarchy'

  level Category   
   column: Category  
 

perspective Product

 perspectiveTable Product

        perspectiveMeasure '# Products'

如果需要引用完全限定的名称,TMDL 使用 表示法来引用对象,例如: 'Table 1'.'Column 1'

子对象

TOM 对象树包含许多位置不同级别的子对象。 例如:

  • 模型对象包含表、角色和表达式对象。
  • 表对象包含列、度量值和层次结构对象。

TMDL 不会显式声明子集合。 相反,其各自父级范围内的所有适用的子元素都隐式构成相应集合的元素。 例如,特定表范围内的所有列元素都将成为 TOM 中该表的列集合的元素,如下所示:

table Sales

    measure 'Sales Amount' = SUMX('Sales', [Quantity] * [Net Price])

    measure 'Total Quantity' = SUM('Sales'[Quantity])

    measure 'Sales Amount YTD' = TOTALYTD([Sales Amount], 'Calendar'[Date])    

子对象不一定是连续的。 例如,可以按任意顺序和混杂来声明列和度量值。

默认属性

某些对象类型具有默认属性,大多数情况下,该属性被视为 表达式。 默认属性特定于对象类型。 如果适用,属性值或表达式在节 = 声明后指定 (等于) 分隔符。

支持的语法:

  • 该值在节标题所在的同一行上指定。
  • 该值指定为节标头后面的多行表达式。

在以下代码示例中,度量值 Sales Amount 和分区 Sales-Partition1 是单行,度量值 Quantity 是多行:

table Sales

    measure 'Sales Amount' = SUM(...)
        formatString: $ #,##0

    measure Quantity = 
            var result = SUMX (...)
            return result
        formatString: #,##0

    partition Sales-Partition1 = m
  mode: import
  source =
   let
       ...
   in
       finalStep

表达式

在作为 TOM 中的文本属性时,有一些对象属性在 TMDL 中获取特殊分析。 整个文本是逐字读取的,因为它可以在 M 或 DAX 表达式中包含特殊字符,如引号或方括号。 表达式可以是多行或单行。 如果是多行,则它们必须位于紧跟属性或对象声明之后的行中。

TMDL 中的表达式值在等于 (=) 分隔符后指定,如以下示例所示:

table Table1

    partition 'partition 1' = m
        mode: import
        source =
            let
            ...
            in
                finalStep
    
    measure Measure1 = SUM(...)

    measure Measure2 =
            var result = SUMX ( 
                ...
            )
            return result
        formatString: $ #,##0

以下特殊规则适用于表达式:

  • 多行表达式必须缩进到父对象属性的一级,并且整个表达式必须在该缩进级别内。
  • 除父对象的缩进级别外,将去除所有外部缩进空格。
  • 允许) (空白行的垂直空格,这些空格被视为表达式的一部分。
  • 删除尾随的空白行和空格。
  • 若要强制实施不同的缩进或保留尾随空白行或空格,请使用三个反引号 (```) 封闭。
  • 默认情况下,如果表达式值包含任何可能导致往返 (修改的内容(例如尾随空格、带空格的空白行) ),TMDL 序列化程序将用反引号引起来。

用三个反引号括起来的表达式 (```) 是逐字读取的,包括缩进、空白行和空格。 分隔符应紧接在等号 (=) 和表达式后面的行之后应用,且其后不能有任何内容,如以下示例所示:

table Table1

    partition partition1 = m
        mode: import
        source = ```
            let
            ...
            in
                finalStep

            ```

    measure Measure1 = ```
                var myVar = Today()
                …
                return result
            ```

使用三个反引号 (```) 分隔符是可选的,仅在特殊情况下需要。 在大多数情况下,使用正确的缩进和对象声明可确保正确分析添加到属性的任何表达式。

当表达式包含在反引号内时,以下规则适用:

  • 三个反引号 (```) 之间的所有内容都被视为多块表达式的一部分,并且不应用 TMDL 缩进规则。 结束分隔符确定表达式中的缩进。
  • 保留表达式中的相对缩进。 结束分隔符 (```) 确定表达式左边界 (请参阅上一示例中的“Measure1”) 。

以下属性被视为表达式:

对象类型 属性 表达式语言
度量值 表达式 DAX
MPartitionSource Expression M
CalculatedPartitionSource 表达式 DAX
QueryPartitionSource 查询 NativeQuery
CalculationItem 表达式 DAX
BasicRefreshPolicy SourceExpression、PollingExpression M
KPI StatusExpression、TargetExpression、TrendExpression DAX
LinguisticMetadata Content XML 或 Json
JsonExtendedProperty Json
FormatStringDefintion 表达式 DAX
DataCoverageDefinition 表达式 DAX
CalculationGroupExpression 表达式 DAX
NamedExpression 表达式 DAX
DetailRowsDefinition 表达式 DAX
TablePermission FilterExpression DAX
CalculatedColumn 表达式 DAX

按对象类型排序的默认属性

下表按对象类型显示了默认属性和表达式语言:

对象类型 默认属性 表达式语言
度量值 表达式 DAX
CalculatedColumn 表达式 DAX
CalculationItem 表达式 DAX
FormatStringDefinition 表达式 DAX
DetailRowsDefinition 表达式 DAX
CalculationExpression 表达式 DAX
DataCoverageDefinition 表达式 DAX
TablePermission FilterExpression DAX
ColumnPermission MetadataPermission MetadataPermission 枚举
NamedExpression Expression M
MPartitionSource Expression M
CalculatedPartitionSource 表达式 DAX
JsonExtendedProperty Json
Annotation Value 文本
StringExtendedProperty 文本
数据源 类型 DataSourceType 枚举
分区 SourceType PartitionSourceType 枚举
ChangedProperty 属性 属性文本
ExternalModelRoleMember MemberType RoleMemberType 枚举
任何自定义 JSON 属性 (例如 DataAccessOptions) JSON 文档 Json
LinguisticMetadata Content Json

说明

TMDL 为说明提供第一类支持。 出于模型文档目的,最佳做法是为每个 TOM 对象提供说明。 TMDL 将说明视为具有显式语法支持的特殊属性。 按照许多其他语言的示例,使用三斜杠 (///) 语法在每个对象声明的顶部指定说明。

在说明块末尾和对象类型标记之间不允许有空格。

描述可以拆分为多行。 TMDL 序列化程序将对象说明拆分为多行,以将发出的文档行保持在最大长度下。 默认的最大长度为 80 个字符。

/// Table Description
table Sales

    /// This is the Measure Description
    /// One more line
    measure 'Sales Amount'' = SUM(...)
        formatString: #,##0

分部声明

TMDL 不强制在同一文档中声明对象。 但是,它类似于 C# 分部类 ,其中可以在多个文件之间拆分对象定义。 例如,可以在 [table].tmdl 文件中声明表定义,然后在单个 [measures].tmdl 文件中定义所有表的所有度量值,如下所示:

table Sales

    measure 'Sales Amount' = SUM(…)
        formatString: $ #,##0

table Product

    measure CountOfProduct = COUNTROWS(…)

为了避免分析错误,无法声明同一属性两次。 例如,为两个不同的 TMDL 文档中的同一个表声明两个具有相同名称的度量值会导致错误。

对象引用

可以使用 ref 关键字 (keyword) 后跟对象类型和名称来引用另一个 TMDL 对象。

例如,如果使用字符串序列化 API 序列化 Column 对象,则结果为:

ref table Table1
	column Column1
		datatype: int64
		sourceColumn: Column1

确定性集合排序

ref 关键字 (keyword) 还用于定义和保留 TOM <> TMDL 往返的集合顺序。 尤其重要的是,避免 TMDL 对象上的源代码管理差异被序列化为单个文件:表、角色、区域性和透视。 ref 关键字 (keyword) 用于父对象 TMDL 文件,以声明从 TOM 排序的项:


model Model

ref table Calendar
ref table Sales
ref table Product
ref table Customer
ref table About

ref culture en-US
ref culture pt-PT

ref role 'Stores Cluster 1'
ref role 'Stores Cluster 2'

应用以下规则:

  • 在 TMDL 反序列化期间:
    • 在 TMDL 中引用但缺少 TMDL 文件的对象将被忽略。
    • 未引用但具有现有 TMDL 文件的对象将追加到集合的末尾。
  • 在 TMDL 序列化期间:
    • TOM 中的所有集合对象都使用 ref 关键字 (keyword) 进行引用。
    • 仅包含一项的集合不会发出引用。
    • 如果对象类型相同,则不会在 ref 之间发出空行。

属性值分隔符

只有两个分隔符/符号用于分配属性值:

  • 等于 (=)

  • 冒号 ()

    • 用于每个非表达式 属性值。 包括保存模型引用的属性。

缩进

TMDL 使用严格的空格缩进规则来表示 TOM 层次结构的结构。 TMDL 文档使用默认的单 制表符 缩进规则。

每个对象可以有三个缩进级别:

  • 级别 1 - 对象声明
    • 级别 2 - 对象属性
      • 级别 3 - 对象属性多行表达式

在 TMDL 文档中,缩进在以下情况下应用:

  • 在对象节标题和对象的属性之间 (表 -> 属性) 。

    table Sales
        isHidden
        lineageTag: 9a48bea0-e5fb-40fa-9e81-f61288e31a02
    
  • 在对象及其子对象 (表之间 -> 度量值) 。

    table Sales
    
        measure 'Sales Amount' = SUMX(...)
    
        measure 'Total Quantity' = SUM(...)
    
  • 对象与其多行表达式 (表 -> 度量值 -> 表达式) 。

    table Sales
    
        measure 'Sales Amount' = 
                var result = SUMX(...)
                return result
            formatString: $ #,##0
    
  • 多行表达式必须缩进比对象属性深一个级别,并且整个表达式必须在该缩进级别内, (查看 ) 表达式

模型的数据库和直接子对象不需要缩进,因为它们隐式假定嵌套在根模型或数据库下:

  • 模型
  • 共享表达式
  • 角色
  • cultures
  • 透视图
  • 关系
  • 数据源
  • 查询组
  • 模型级注释
  • 模型级扩展属性

不遵循这些缩进规则将生成分析错误。

空格

默认情况下,TMDL 将以下规则应用于属性和表达式值中的空格,如果未包含在反引号 () ``` 或双引号 () :

  • 在属性值上,将剪裁前导空格和尾随空格。
  • 在表达式上,删除表达式末尾的空格行。
  • 空格行被剪裁为空行, (没有空格/制表符) 。

大小写

默认情况下,序列化/写入上的 TMDL API 使用 camelCase,应用于:

  • 对象类型
  • 关键字
  • 枚举值

在反序列化/读取时,TMDL API 不区分大小写。

注意事项和限制

了解 TMDL 后,请务必参阅 TMDL 入门 ,了解如何获取和部署 Power BI 语义模型的 TMDL 模型表示形式。