Partilhar via


Linguagem de definição de modelo tabular (TMDL)

Aplica-se a: SQL Server 2016 e posterior, Analysis Services Azure Analysis Services Fabric ou Power BI Premium

TMDL (Tabular Model Definition Language) é uma sintaxe de definição de modelo de objeto para modelos de dados tabulares no nível de compatibilidade 1200 ou superior.

Os principais elementos da TMDL incluem:

  • Compatibilidade total com todo o modelo de objeto tabular (TOM). Cada objeto TMDL expõe as mesmas propriedades que o TOM.
  • Baseado em texto e otimizado para interação humana e legibilidade. TMDL usa uma sintaxe gramatical semelhante a YAML. Cada objeto TMDL é representado em texto com delimitadores mínimos e usa recuo para demarcar relações pai-filho.
  • Melhor experiência de edição, especialmente em propriedades com expressões incorporadas de diferentes tipos de conteúdo, como DAX (Data Analysis Expression) e M.
  • Melhor para colaboração por causa de sua representação de pasta onde cada objeto de modelo tem uma representação de arquivo individual, tornando-o mais amigável ao controle do código-fonte.

Um aspeto importante do TMDL é o uso de indentação de espaços em branco para denotar uma estrutura de objeto TOM. O exemplo a seguir mostra como é fácil representar um modelo tabular ao usar 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]

Estrutura de pastas TMDL

Ao contrário do TMSL, o TMDL usa uma estrutura de pastas. A estrutura de pastas padrão tem apenas um nível de subpastas, todas com arquivos .tmdl dentro:

  • culturas
  • perspetivas
  • roles
  • tables

E arquivos raiz para:

Aqui está um exemplo de uma pasta TMDL:

TMDL/
├── cultures/
│   ├── en-US.tmdl
│   └── pt-PT.tmdl
├── perspectives/
│   └── perspective1.tmdl
├── roles/
│   ├── role1.tmdl
│   └── role2.tmdl
├── tables/
│   ├── About.tmdl
│   ├── Calendar.tmdl
│   ├── Customer.tmdl
│   ├── Product.tmdl
│   ├── Sales.tmdl
│   └── Store.tmdl
├── relationships.tmdl
├── functions.tmdl
├── expressions.tmdl
├── dataSources.tmdl
├── model.tmdl
└── database.tmdl

As definições incluem:

  • Um arquivo para definição de banco de dados.
  • Um arquivo para definição de modelo.
  • Um arquivo para todas as fontes de dados no modelo.
  • Um arquivo para todas as expressões no modelo.
  • Um arquivo para todas as funções (DAX User Defined Functions no modelo.
  • Um arquivo para todos os relacionamentos no modelo.
  • Um arquivo para cada esquema linguístico de cultura.
  • Um arquivo para cada perspetiva.
  • Um arquivo para cada função.
  • Um ficheiro para cada tabela.
  • Todas as propriedades internas de metadados de tabelas (Coluna, Hierarquias, Partições,...) residem no arquivo TMDL da tabela pai.

TMDL API

Semelhante à TMSL (Tabular Model Scripting Language), há uma classe para manipular a serialização TMDL. Para TMDL, a classe é TmdlSerializer, sob o namespace Microsoft.AnalysisServices.Tabular .

A classe TmdlSerializer expõe métodos para serializar e desserializar documentos TMDL:

Serialização das pastas

public static void SerializeDatabaseToFolder (Database database, string path)

  • Recebe um objeto de banco de dados TOM e o caminho de saída TMDL.
  • Serializa o banco de dados TOM em uma representação de pasta TMDL.

Saiba mais sobre como serializar para uma pasta.

public static Database DeserializeDatabaseFromFolder (string path)

  • Recebe um caminho completo para uma pasta TMDL.
  • Retorna a representação do objeto de banco de dados TOM da pasta TMDL.

Saiba mais sobre como desserializar de pastas.

Serialização de cadeia de caracteres

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

  • Recebe um objeto TOM e retorna sua representação de texto TMDL.

Saiba mais sobre como serializar um objeto em uma cadeia de caracteres.

Serialização de fluxo

Você pode serializar/desserializar TMDL de/para fluxos, permitindo converter um objeto TOM em fluxos de bytes para armazenamento, transmissão e interoperabilidade entre plataformas. A API Stream também permite controlar quais documentos TMDL são carregados e quais documentos TMDL são produzidos.

A serialização de fluxo TMDL é manipulada pela classe MetadataSerializationContext .

Saiba mais sobre como serializar de/para TMDL usando fluxos.

Linguagem TMDL

Declaração de objeto

Exceto para o objeto Server, TMDL expõe toda a árvore de objetos do Banco de Dados TOM no namespace Microsoft.AnalysisServices.Tabular.

Um objeto TMDL é declarado especificando o tipo de objeto TOM seguido de seu nome. No exemplo de código a seguir, cada tipo de objeto: model, table, column é seguido por um nome de objeto.

model Model    
    culture: en-US    

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

    column 'Customer Key'
        datatype: int64
        sourceColumn: CustomerKey

Objetos como partition ou measure com propriedades padrão que podem ser atribuídas após o delimitador igual (=) na mesma linha da declaração de objeto ou na seguinte linha para uma expressão de várias linhas:

table Sales

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

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

O nome do objeto TMDL deve ser colocado entre aspas simples (') se incluir qualquer um dos seguintes caracteres:

  • Ponto (.)
  • Igual (=)
  • Cólon (:)
  • Citação simples (')
  • Espaço em branco ( )

Se um nome de objeto contiver aspas simples ('), use duas aspas simples para escapar dele.

Propriedades do objeto

As propriedades do objeto são especificadas após a declaração do objeto ou a expressão multilinha da propriedade padrão do objeto. Os valores da propriedade do objeto são especificados seguindo o delimitador de dois pontos (:). Por exemplo:

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"

As seguintes regras se aplicam aos valores de propriedade:

  • O valor deve estar na mesma linha após os dois pontos e não pode ocupar múltiplas linhas.

  • Valores de propriedade de texto

    • Aspas duplas iniciais e finais são opcionais e automaticamente eliminadas durante a serialização.
    • Deve ser colocado entre aspas duplas (") se o texto contiver espaços em branco à direita ou à esquerda.
    • Quando colocado entre aspas duplas, se o valor contiver aspas duplas, use duas aspas duplas para escapar delas (veja displayFolder a propriedade no exemplo de código acima).
  • As propriedades booleanas podem ser definidas usando a sintaxe padrão do par chave/valor, como com a 'isAvailableInMdx' propriedade no exemplo anterior. Eles também podem ser definidos usando uma sintaxe de atalho onde apenas o nome da propriedade é declarado e true está implícito. Considere, por exemplo, a propriedade 'isHidden' no exemplo anterior.

Referências de objeto nomedado

Algumas propriedades de objeto contêm referências a outros objetos de modelo, por exemplo:

  • Referência de coluna em níveis hierárquicos.
  • Referência "sortByColumn" em cada coluna da tabela.
  • Referência de tabela/coluna/medida em perspetivas.

Em TMDL, as referências utilizam o nome do objeto e seguem os mesmos requisitos de escape e aspas simples (') que delimitam a declaração do objeto. No exemplo de código a seguir, você vê propriedades de objeto que contêm uma referência a outro objeto: column.sortByColumn, level.columnperspectiveMeasure.measure , e perspectiveTable.table.


table Product

    column Category
        sortByColumn: 'Category Order'    

 hierarchy 'Product Hierarchy'

  level Category   
   column: Category  
 

perspective Product

 perspectiveTable Product

        perspectiveMeasure '# Products'

Se necessário para fazer referência a um nome totalmente qualificado, TMDL usa notação de ponto para fazer referência a um objeto, por exemplo: 'Table 1'.'Column 1'

Objetos filho

A árvore de objetos TOM contém objetos filho em muitos lugares e em diferentes níveis. Por exemplo:

  • Um objeto de modelo contém objetos de tabela, função e expressão.
  • Um objeto tabela contém objetos de coluna, medida e hierarquia.

A TMDL não declara subcoleções explicitamente. Em vez disso, todos os elementos filho aplicáveis dentro do escopo de seu respetivo pai implicitamente compõem os elementos da coleção correspondente. Por exemplo, todos os elementos de coluna dentro do escopo de uma tabela específica tornam-se elementos da coleção de colunas dessa tabela no TOM, como mostrado aqui:

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])    

Os objetos filho não precisam ser contíguos. Por exemplo, você pode declarar colunas e medidas em qualquer ordem e misturadas.

Propriedades predefinidas

Alguns tipos de objeto têm uma propriedade padrão que, na maioria das vezes, são tratadas como expressões. A propriedade padrão é específica do tipo de objeto. Quando aplicável, o valor da propriedade ou expressão é especificado seguindo o delimitador de igual (=) - após a declaração de seção.

Sintaxe suportada:

  • O valor é especificado na mesma linha que o cabeçalho da seção.
  • O valor é especificado como uma expressão de várias linhas seguindo o cabeçalho da seção.

No exemplo de código a seguir, medida Sales Amount e partição Sales-Partition1 são linha única e medida Quantity é multilinha:

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

Expressions

Há propriedades de objeto que, embora sejam uma propriedade de texto no TOM, obtêm uma análise especial no TMDL. O texto inteiro é lido literalmente porque pode incluir caracteres especiais, como aspas ou colchetes em expressões M ou DAX. As expressões podem ser multilinha ou linha única. Se tiverem várias linhas, elas devem situar-se na linha imediatamente a seguir à declaração da propriedade ou do objeto.

Um valor de expressão em TMDL é especificado seguindo um delimitador de igual (=), como no exemplo a seguir:

table Table1

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

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

As seguintes regras especiais aplicam-se às expressões:

  • As expressões de várias linhas devem ser recuadas um nível mais do que as propriedades do objeto pai, e toda a expressão deve permanecer dentro desse nível de recuo.
  • Todos os espaços em branco de indentação externos são removidos além do nível de indentação do objeto pai.
  • Espaços em branco verticais (linhas em branco sem espaços em branco) são permitidos e são considerados parte da expressão.
  • As linhas em branco e os espaços finais são removidos.
  • Para impor uma indentação diferente ou para preservar linhas em branco ou espaços em branco à direita, use os três `backticks` (```) para enclausurar.
  • Por padrão, o serializador TMDL incluirá backticks se o valor da expressão contiver algo que possa causar uma modificação na viagem de ida e volta (por exemplo, espaços em branco à direita, linhas em branco com espaços em branco).

As expressões delimitadas com três backticks (```) são lidas literalmente, incluindo indentações, linhas em branco e espaços em branco. O delimitador deve ser aplicado imediatamente após o sinal de igual (=) e a linha que segue a expressão e não pode ter nada depois dele, como no exemplo a seguir:

table Table1

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

            ```

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

O uso do delimitador de três backticks (```) é opcional e só é necessário em situações particulares. Na maioria das situações, usar o recuo correto e a declaração de objeto garante uma análise correta de qualquer expressão adicionada à propriedade.

Quando a expressão está incluída em backticks, aplicam-se as seguintes regras:

  • Tudo entre três backticks (```) é considerado parte da expressão de múltiplos blocos e as regras de indentação do TMDL não são aplicadas. O delimitador final determina as indentações dentro da expressão.
  • O recuo relativo dentro da expressão é mantido. O delimitador final (```) determina o limite esquerdo da expressão (ver 'Medida1' no exemplo anterior).

As seguintes propriedades são tratadas como expressões:

Tipo de objeto Propriedade Linguagem de expressão
Medida Expression DAX
Função Expression DAX
MPartitionSource Expression M
CalculatedPartitionSource Expression DAX
QueryPartitionSource Query NativeQuery
Item de Cálculo Expression DAX
BasicRefreshPolicy SourceExpression, PollingExpression M
KPI StatusExpression, TargetExpression, TrendExpression DAX
Metadados linguísticos Content XML ou Json
JsonExtendedProperty Valor Json
FormatStringDefintion Expression DAX
Definição de Cobertura de Dados Expression DAX
ExpressãoDeGrupoDeCálculo Expression DAX
NamedExpression Expression DAX
DetailRowsDefinition Expression DAX
Permissão de Tabela FilterExpression DAX
ColunaCalculada Expression DAX

Propriedades padrão por tipo de objeto

A tabela a seguir mostra a propriedade padrão e a linguagem de expressão por tipo de objeto:

Tipo de objeto Propriedade padrão Linguagem de expressão
Medida Expression DAX
Função Expression DAX
ColunaCalculada Expression DAX
Item de Cálculo Expression DAX
FormatStringDefinition Expression DAX
DetailRowsDefinition Expression DAX
Expressão de Cálculo Expression DAX
Definição de Cobertura de Dados Expression DAX
Permissão de Tabela FilterExpression DAX
Permissão de Coluna PermissãoDeMetadados MetadataPermission Enum
NamedExpression Expression M
MPartitionSource Expression M
CalculatedPartitionSource Expression DAX
JsonExtendedProperty Valor Json
Anotação Valor Texto
StringExtendedProperty Valor Texto
Fonte de dados Tipo DataSourceType Enum
Partition Tipo de fonte PartitionSourceType Enum
PropriedadeAlterada Propriedade Texto da propriedade
MembroDePapelDeModeloExterno Tipo de Membro RoleMemberType Enum
Qualquer propriedade JSON personalizada (por exemplo, DataAccessOptions) Documento JSON Json
Metadados linguísticos Content Json

Descrições

TMDL fornece suporte de primeira classe para descrições. Para fins de documentação do modelo, a prática recomendada é fornecer descrições para cada objeto TOM. TMDL trata descrições como uma propriedade especial com suporte de sintaxe explícito. Seguindo os exemplos de muitas outras linguagens, as descrições são especificadas sobre cada declaração de objeto usando sintaxe de barra tripla (///).

Nenhum espaço em branco é permitido entre a extremidade do bloco de descrição e o token de tipo de objeto.

As descrições podem ser divididas em várias linhas. O serializador TMDL divide as descrições de objetos em várias linhas para manter as linhas de documento emitidas abaixo do comprimento máximo. O comprimento máximo padrão é de 80 caracteres.

/// Table Description
table Sales

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

Declaração parcial

TMDL não obriga a declaração de objetos no mesmo documento. É, no entanto, semelhante às classes parciais C# , onde é possível dividir a definição de objeto entre vários arquivos. Por exemplo, você pode declarar uma definição de tabela em um arquivo [table].tmdl e, em seguida, ter todas as medidas de todas as tabelas definidas em um único arquivo [measures].tmdl, conforme mostrado aqui:

table Sales

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

table Product

    measure CountOfProduct = COUNTROWS(…)

Para evitar um erro de análise, a mesma propriedade não pode ser declarada duas vezes. Por exemplo, declarar duas medidas com o mesmo nome para a mesma tabela em dois documentos TMDL diferentes resulta em um erro.

Referências de objetos

Você pode fazer referência a outro objeto TMDL usando a palavra-chave ref seguida pelo tipo e nome do objeto.

Por exemplo, se você serializar um objeto Column usando a API de serialização de cadeia de caracteres, o resultado será:

ref table Table1
	column Column1
		datatype: int64
		sourceColumn: Column1

Ordenação determinística de coleções

A palavra-chave ref também é usada para definir e preservar a ordem de coleta em viagens de ida e volta TOM <> TMDL. É particularmente importante evitar as diferenças de controle de versão em objetos TMDL que são serializados em arquivos individuais: Tabelas, Perfis, Culturas e Perspetivas. A palavra-chave ref é usada no arquivo TMDL do objeto pai para declarar a ordem do item do 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'

São aplicadas as seguintes regras:

  • Durante a desserialização de TMDL:
    • Os objetos referenciados no TMDL, mas com arquivo TMDL ausente, são ignorados.
    • Objetos não referenciados, mas com arquivo TMDL existente, são anexados ao final da coleção.
  • Durante a serialização do TMDL:
    • Todos os objetos de coleção no TOM são referenciados usando a palavra-chave ref .
    • Coleções com apenas um item não emitem uma ref.
    • Linhas em branco não são emitidas entre "ref’s" se forem do mesmo tipo de objeto.

Delimitadores de valor de propriedade

Há apenas dois delimitadores/símbolos para atribuir um valor de propriedade:

Recuo

A TMDL usa regras rígidas de indentação de espaço em branco para denotar a estrutura da hierarquia do TOM. Um documento TMDL utiliza por padrão uma regra única de recuo de tabulação.

Cada objeto pode ter três níveis de recuo:

  • Nível 1 - Declaração de Objeto
    • Nível 2 - Propriedades do objeto
      • Nível 3 - Expressões de várias linhas da propriedade do objeto

Dentro de um documento TMDL, o recuo é aplicado nos seguintes casos:

  • Entre um cabeçalho de seção de objeto e as propriedades do objeto (tabela -> propriedades).

    table Sales
        isHidden
        lineageTag: 9a48bea0-e5fb-40fa-9e81-f61288e31a02
    
  • Entre um objeto e seus objetos filho (tabela -> medidas).

    table Sales
    
        measure 'Sales Amount' = SUMX(...)
    
        measure 'Total Quantity' = SUM(...)
    
  • Entre um objeto e as suas expressões com várias linhas (tabela -> medida -> expressão).

    table Sales
    
        measure 'Sales Amount' = 
                var result = SUMX(...)
                return result
            formatString: $ #,##0
    
  • As expressões de várias linhas devem ser recuadas um nível mais profundo do que as propriedades do objeto e toda a expressão deve estar dentro desse nível de recuo (consulte expressões).

Base de dados e objetos filho diretos do Modelo não precisam ser recuados, pois são implicitamente considerados aninhados sob o Modelo raiz ou a Base de dados.

  • modelo
  • tables
  • expressões partilhadas
  • roles
  • culturas
  • perspetivas
  • Relacionamentos
  • Fontes de dados
  • grupos de consulta
  • anotações no nível do modelo
  • Propriedades estendidas no nível do modelo

Não seguir essas regras de indention gera um erro de análise.

Espaço em branco

Por padrão, TMDL aplica as seguintes regras ao espaço em branco em valores de propriedade e expressão, quando não estão incluídos em backticks (```) ou aspas duplas ("):

  • Nos valores de propriedade, os espaços em branco à esquerda e à direita são removidos.
  • Nas expressões, as linhas de espaço em branco no final das expressões são descartadas.
  • Linhas contendo apenas espaços em branco são reduzidas a linhas vazias (sem espaços ou tabulações).

Invólucro

Por padrão, a API TMDL na serialização/gravação usa camelCase, aplicada a:

  • Tipos de objeto
  • Palavras-chave
  • Valores de enum

Na desserialização/leitura, a API TMDL não diferencia maiúsculas de minúsculas.

Agora que você já entendeu a TMDL, não deixe de ver Introdução à TMDL para saber como obter e implantar uma representação de modelo TMDL de um modelo semântico do Power BI.