Compartilhar via


TMDL (Linguagem de Definição de Modelo Tabular)

Aplica-se a: SQL Server 2016 e posteriores do Analysis Services Azure Analysis Services Fabric/Power BI Premium

Importante

A TMDL (Linguagem de Definição de Modelo tabular) está atualmente em versão prévia. Durante a versão prévia, a funcionalidade e a documentação podem ser alteradas.

A 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 do TMDL incluem:

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

Um aspecto importante do TMDL é o uso do recuo de espaço em branco para indicar 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:

  • cultures
  • perspectivas
  • funções
  • tabelas

E arquivos raiz para:

  • Banco de Dados
  • modelo
  • relacionamentos
  • expressões
  • datasources

Aqui está um exemplo de uma pasta TMDL:

Pasta com uma representação TMDL de um modelo

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 relações no modelo.
  • Um arquivo para cada esquema linguístico de cultura.
  • Um arquivo para cada perspectiva.
  • Um arquivo para cada função.
  • Um arquivo 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 (Linguagem de Script de Modelo Tabular), há uma classe para lidar com a serialização TMDL. Para TMDL, a classe é TmdlSerializer, no namespace Microsoft.AnalysisServices.Tabular .

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

Serialização de pasta

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 para uma cadeia de caracteres.

Serialização de fluxo

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

A serialização do TMDL Stream é tratada pela classe MetadataSerializationContext .

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

Idioma TMDL

Declaração de objeto

Com exceção do objeto Server, o 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 por 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 têm propriedades padrão que podem ser atribuídas após o delimitador igual a (=) 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 deverá ser colocado entre aspas simples (') se incluir qualquer um dos seguintes caracteres:

  • Ponto (.)
  • Igual a (=)
  • Dois-pontos (:)
  • Aspas Simples (')
  • Espaço em branco ( )

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

Propriedades de objeto

As propriedades do objeto são especificadas após a declaração de objeto ou a expressão de várias linhas da propriedade padrão do objeto. Os valores de propriedade do objeto são especificados após 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 a valores de propriedade:

  • O valor deve estar na mesma linha após os dois-pontos e não pode ter várias linhas.

  • Valores da propriedade Text

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

Referências de objeto nomeado

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

  • Referência de coluna em níveis de hierarquia.
  • Referência sortByColumn em cada coluna de tabela.
  • Referência de tabela/coluna/medida em perspectivas.

No TMDL, as referências são feitas usando o nome do objeto e seguem os mesmos requisitos de escape e aspas simples (') que incluem requisitos de declaração de objeto. No exemplo de código a seguir, você verá 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, o TMDL usará a 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 locais e em níveis diferentes. Por exemplo:

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

O TMDL não declara coleções filho explicitamente. Em vez disso, todos os elementos filho aplicáveis dentro do escopo de seu respectivo pai compõem implicitamente os elementos da coleção correspondente. Por exemplo, todos os elementos de coluna dentro do escopo de uma tabela específica se tornam 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])    

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

Propriedades padrão

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 após o delimitador igual (=) após a declaração da 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 após o cabeçalho da seção.

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

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

Expressões

Há propriedades de objeto que, ao ser uma propriedade de texto no TOM, obtêm análise especial no TMDL. O texto inteiro é lido textualmente porque pode incluir caracteres especiais, como aspas ou colchetes em expressões M ou DAX. As expressões podem ser de várias linhas ou de linha única. Se houver várias linhas, elas deverão estar localizadas na linha imediatamente após a declaração de propriedade ou objeto.

Um valor de expressão em TMDL é especificado seguindo um delimitador 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 se aplicam a expressões:

  • Expressões de várias linhas devem ser recuadas um nível mais profundo para as propriedades do objeto pai e toda a expressão deve estar dentro desse nível de recuo.
  • Todos os espaços em branco de recuo externos são removidos além do nível recuado 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.
  • Linhas em branco à direita e espaços em branco são removidos.
  • Para impor um recuo diferente ou preservar linhas ou espaços em branco à direita, use os três backticks (```) delimitantes.
  • Por padrão, o serializador TMDL será incluído com backticks se o valor da expressão contiver qualquer coisa 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).

Expressões entre três backticks (```) são lidas textualmente, incluindo recuo, 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 dela, 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 necessário apenas em situações exclusivas. Para a maioria das situações, usar o recuo correto e a declaração de objeto garante a análise correta de qualquer expressão que você adicionar à propriedade .

Quando a expressão é colocada em backticks, as seguintes regras se aplicam:

  • Tudo entre três backticks (```) é considerado parte da expressão de vários blocos e as regras de recuo TMDL não são aplicadas. O delimitador final determina o recuo dentro da expressão.
  • O recuo relativo dentro da expressão é mantido. O delimitador final (```) determina o limite esquerdo da expressão (consulte 'Measure1' no exemplo anterior).

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

Tipo de objeto Propriedade Linguagem de expressão
Medida Expression DAX
MPartitionSource Expression M
CalculatedPartitionSource Expression DAX
QueryPartitionSource Consulta NativeQuery
CalculationItem Expression DAX
BasicRefreshPolicy SourceExpression, PollingExpression M
KPI StatusExpression, TargetExpression, TrendExpression DAX
LinguisticMetadata Conteúdo XML ou Json
JsonExtendedProperty Valor Json
FormatStringDefintion Expression DAX
DataCoverageDefinition Expression DAX
CalculationGroupExpression Expression DAX
NamedExpression Expression DAX
DetailRowsDefinition Expression DAX
TablePermission FilterExpression DAX
CalculatedColumn 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
CalculatedColumn Expression DAX
CalculationItem Expression DAX
FormatStringDefinition Expression DAX
DetailRowsDefinition Expression DAX
CalculationExpression Expression DAX
DataCoverageDefinition Expression DAX
TablePermission FilterExpression DAX
ColumnPermission MetadataPermission Enumeração MetadataPermission
NamedExpression Expression M
MPartitionSource Expression M
CalculatedPartitionSource Expression DAX
JsonExtendedProperty Valor Json
Annotation Valor Texto
StringExtendedProperty Valor Texto
DataSource Tipo Enumeração DataSourceType
Partition SourceType Enumeração PartitionSourceType
ChangedProperty Propriedade Texto da propriedade
ExternalModelRoleMember MemberType Enumeração RoleMemberType
Qualquer propriedade JSON personalizada (por exemplo, DataAccessOptions) Documento JSON Json
LinguisticMetadata Conteúdo Json

Descrições

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

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

As descrições podem ser divididas entre várias linhas. O serializador TMDL divide as descrições do objeto em várias linhas para manter as linhas de documento emitidas sob o 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

O TMDL não força a declaração de objeto no mesmo documento. No entanto, é semelhante às classes parciais do C# em que é 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 objeto

Você pode referenciar outro objeto TMDL usando o palavra-chave ref seguido pelo tipo de objeto e pelo nome.

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 de coleção determinística

A palavra-chave ref também é usada para definir e preservar a ordenação de coleção em ida e volta do TOM <> TMDL. É particularmente importante evitar diferenciações de controle do código-fonte em objetos TMDL que são serializados em arquivos individuais: Tabelas, Funções, Culturas e Perspectivas. O palavra-chave de referência é usado no arquivo TMDL do objeto pai para declarar a ordenação de 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'

As seguintes regras são aplicadas:

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

Delimitadores de valor de propriedade

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

Recuo

O TMDL usa regras estritas de recuo de espaço em branco para denotar a estrutura da hierarquia TOM. Um documento TMDL usa uma regra de recuo de guia única padrã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 object

Em 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 suas expressões de várias linhas (tabela –> medida –> expressão).

    table Sales
    
        measure 'Sales Amount' = 
                var result = SUMX(...)
                return result
            formatString: $ #,##0
    
  • 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).

O banco de dados e os objetos filho diretos do Modelo não precisam ser recuados porque são implicitamente considerados aninhados no modelo raiz ou no banco de dados:

  • modelo
  • tabelas
  • expressões compartilhadas
  • funções
  • cultures
  • perspectivas
  • 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 recuo gera um erro de análise.

Espaço em branco

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

  • Em valores de propriedade, espaços em branco à esquerda e à direita são cortados.
  • Em expressões, linhas de espaço em branco no final das expressões são descartadas.
  • As linhas de espaço em branco são cortadas para linhas vazias (sem espaços/guias).

Capitalização

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

  • Tipos de objeto
  • Palavras-chave
  • Valores de enumeração

Ao desserializar/ler, a API TMDL não diferencia maiúsculas de minúsculas.

Considerações e limitações

Agora que você tem uma compreensão do TMDL, confira Introdução ao TMDL para saber como obter e implantar uma representação de modelo TMDL de um modelo semântico do Power BI.