Fabiano.Amaral
Exame DP-700 · Habilidades medidas em 21 de julho de 2026

Implementando Soluções de Engenharia de Dados com Microsoft Fabric

Um guia de estudo completo, objetivo por objetivo, construído a partir do Microsoft Learn. Cada título abaixo corresponde a um item do skills outline oficial, então você pode percorrer o outline de cima a baixo com a certeza de não deixar nada de fora.

Domínio 1
30–35%
Implementar e gerenciar uma solução de análise
Domínio 2
30–35%
Ingerir e transformar dados
Domínio 3
30–35%
Monitorar e otimizar uma solução de análise
Certificação
Fabric Data Engineer Associate
Nota de aprovação
700 / 1000
Linguagens
KQL · T-SQL · PySpark
Renovação
Gratuita, anual, online

Como usar este guia

Os três domínios têm peso igual. Isso é incomum, e é o fato mais útil para planejar o DP-700: não existe um domínio "grande" em que valha a pena investir demais, nem um pequeno que dê para pular. Monitoramento e otimização vale exatamente o mesmo que ingestão.

O exame é escrito para quem já constrói soluções de dados e agora precisa tomar decisões específicas do Fabric. A maioria das questões não é "o que o OPTIMIZE faz" — é "dadas estas restrições, qual destes quatro itens do Fabric você usa, e por que não os outros três". Por isso as tabelas de decisão deste guia importam mais do que a sintaxe, e ambas importam mais do que decorar caminhos de menu.

A ordem de estudo que funciona

Leia Fundamentos do Fabric primeiro, mesmo não sendo um domínio do exame — capacity, OneLake e a taxonomia de itens são o vocabulário em que todas as outras respostas são escritas. Depois trabalhe o Domínio 2 (o mais prático), então o Domínio 1 e por último o Domínio 3, porque as questões do Domínio 3 pressupõem que você já sabe o que são um Spark pool, um Dataflow e um Eventhouse.

Os quatro formatos de questão que você vai encontrar

Formato 1

Escolher o item certo

"Você precisa ingerir 200 GB por noite do Snowflake sem código. O que usar?" A resposta está numa tabela de decisão, não na sintaxe. Elimine sempre por persona, latência, complexidade da transformação e custo.

Formato 2

Completar o código

Arrastar-e-soltar ou preencher KQL, T-SQL ou PySpark. Normalmente 3 a 6 linhas. A pegadinha quase sempre é a ordem dos operadores (KQL) ou um ramo WHEN MATCHED ausente (MERGE em T-SQL).

Formato 3

Diagnosticar a falha

Um sintoma mais os logs. Você escolhe a causa ou a correção. Decore as mensagens de erro: HTTP 430, CapacityLimitExceeded, falha de conversão de schema, credencial de shortcut expirada.

Formato 4

Ordenar as etapas

Sequenciar uma conexão Git, uma promoção de deployment pipeline ou uma configuração de segurança. O Fabric tem pré-requisitos rígidos (por exemplo, workspace identity antes de trusted workspace access) — é aí que está a pegadinha.

Recursos em preview podem cair na prova

A Microsoft atualiza o DP-700 mais ou menos a cada seis meses, e recursos em preview aparecem. Onde este guia marca algo como Preview, saiba o que é e que problema resolve — ninguém vai perguntar a data de GA.

Fundamentos do Fabric (o vocabulário)

Capacity, CUs, bursting e smoothing

Tudo no Fabric roda sobre uma capacity — um pool de computação medido em Capacity Units (CU), comprado como SKU F (F2 → F2048) ou herdado de um SKU P do Power BI. Uma capacity pertence a uma região; workspaces são atribuídos a uma capacity. Se você pausar a capacity, tudo dentro dela para, inclusive a replicação de mirroring.

ConceitoO que significaPor que o exame cobra
BurstingUm job pode consumir temporariamente mais computação do que o SKU nominalmente oferece, para terminar rápido.Explica por que um job Spark grande roda numa SKU pequena.
SmoothingO consumo de CU é distribuído ao longo do tempo — operações interativas em 5–64 minutos, background em 24 horas.Explica por que o mesmo job aparece como um pico pequeno no Metrics app.
TimepointUm intervalo de avaliação de 30 segundos. 2.880 timepoints por dia.É a unidade que o Capacity Metrics app plota.
CarryforwardDívida de CU não paga, empurrada para timepoints futuros quando você gasta demais.É o que acaba disparando o throttling.

O throttling é uma penalidade em estágios, aplicada contra o consumo futuro suavizado:

Uso futuro devidoEstágioEfeito
≤ 10 minutosOverage protectionNada. Margem livre de burst.
10 – 60 minutosInteractive delayAtraso de 20 segundos em novas operações interativas.
60 min – 24 horasInteractive rejectionOperações interativas rejeitadas; jobs de background continuam rodando.
> 24 horasBackground rejectionTudo é rejeitado.
Exceções que vale a pena decorar
  • Operações de Warehouse são classificadas como background, então ganham a janela generosa de smoothing de 24 horas.
  • Real-Time Intelligence começa a sofrer throttling já no estágio de 60 minutos — pula completamente o atraso interativo de 20 segundos.
  • Eventstreams não rejeitam nada; em vez disso reduzem a alocação de CU.

Como resolver uma capacity com throttling, em ordem de preferência: esperar (capacities se autorrecuperam à medida que o carryforward é consumido) → aumentar temporariamente o SKU → distribuir cargas entre capacities → habilitar overage billing (taxa 3×) → pausar e retomar, o que zera o uso futuro mas deixa o conteúdo indisponível. Diagnostique com o Microsoft Fabric Capacity Metrics app: a tabela de system events na página Compute, a aba Overages e a métrica Minutes to burndown.

OneLake

Um data lake lógico por tenant, provisionado automaticamente, sem infraestrutura para criar. Sua estrutura é fixa e vale desenhar no papel:

Nível 1
TenantUm OneLake. Não é opcional nem removível.
Nível 2
WorkspaceComporta-se como um container de armazenamento.
Nível 3
ItemLakehouse, Warehouse, Eventhouse… uma pasta.
Nível 4
Tables / FilesTabelas Delta gerenciadas vs. arquivos livres.
  • Delta Parquet é o formato nativo de todas as cargas de trabalho. Warehouse, Lakehouse e Eventhouse gravam em Delta, e é isso que permite uma única cópia ser lida por todos os engines.
  • O OneLake expõe um subconjunto das APIs do ADLS Gen2 e do Blob, então ferramentas externas o endereçam como https://onelake.dfs.fabric.microsoft.com/<workspace>/<item>/Tables/<table>.
  • Uma tenant setting na seção OneLake controla se aplicativos externos (APIs do ADLS, OneLake file explorer) podem acessá-lo. Desligá-la não bloqueia os engines do próprio Fabric.
  • Criptografia em repouso usa chaves gerenciadas pela Microsoft por padrão (chaves gerenciadas pelo cliente são opcionais); TLS 1.2 no mínimo em trânsito.

A taxonomia de itens que você precisa reconhecer de imediato

Data Engineering

Lakehouse

Tabelas Delta + arquivos não estruturados, Spark em primeiro lugar, com um SQL analytics endpoint somente leitura anexado. Schema-on-read.

Data Engineering

Notebook · Spark Job Definition · Environment

Código interativo, código em lote submetido, e a configuração reutilizável de runtime/bibliotecas/computação à qual os outros dois se anexam.

Data Warehouse

Warehouse

T-SQL completo de leitura e escrita, transações ACID multi-tabela, schema-on-write. É o único store do Fabric com DML de verdade.

Real-Time

Eventstream · Eventhouse · KQL Database

Roteamento de streams sem código; o container das KQL databases; o store de séries temporais consultado com KQL.

Data Factory

Pipeline · Dataflow Gen2 · Copy job

Orquestração + atividades; transformação com Power Query; replicação full/incremental/CDC guiada por assistente, sem pipeline.

Plataforma

Mirrored database · Variable library · Activator

Réplica quase em tempo real de um banco externo; valores de configuração por estágio; o motor de regras por trás de alertas e event triggers.

Escolhendo um data store — a tabela de decisão mestra

Esta única tabela responde a uma fatia desproporcional das questões do Domínio 2.

StoreCarga idealPersona / habilidadeAPI de escritaTransações multi-tabela
LakehouseBig data, ML, dados não e semiestruturados, engenharia de dadosData engineer, data scientist — SparkSpark (PySpark, Scala, Spark SQL, R), pipelines, DataflowsNão
WarehouseDW corporativo, BI baseado em SQL, OLAP, suporte transacional completoDesenvolvedor de DW, arquiteto de dados, DBA — T-SQLDML em T-SQL, COPY INTO, CTAS, pipelinesSim
Eventhouse / KQL DBStreaming, telemetria, logs, análise interativa de alta granularidade sobre JSON/textoDesenvolvedor de aplicações, data engineer — KQLEventstream, SDKs, Kafka, .ingest, DataflowsNão
SQL database no FabricOLTP operacional dentro do FabricDesenvolvedor de aplicação/banco, DBA — T-SQLT-SQL (superfície OLTP completa)Sim
Cosmos DB no FabricAplicações de IA, NoSQL, vector searchDesenvolvedor de IA/aplicações — REST/SDKAPI REST, SDKs de linguagemNão
Todos os cinco gravam no OneLake em formato Delta aberto

Portanto a escolha nunca é "qual deles outros engines conseguem ler" — todos conseguem. A escolha é sobre semântica de escrita, a linguagem do desenvolvedor e a latência.

Workspace roles — decore esta matriz

CapacidadeAdminMemberContributorViewer
Atualizar / excluir o workspace
Adicionar ou remover pessoas, inclusive outros admins
Adicionar members e papéis inferiores; permitir recompartilhamento
Criar workspace identity
Conectar o workspace a um repositório Git
Criar / modificar itens de warehouse, database e mirroring
Escrever, excluir, executar notebooks / pipelines / Spark jobs
Ler dados de Lakehouse e Warehouse via T-SQL (ReadData)
Ler dados via APIs do OneLake e Spark (ReadAll)
Ler dados do Lakehouse no Lakehouse explorer
Assinar OneLake events
Ver a saída de execução de pipelines / notebooks
Alterar configurações de gateway; agendar refresh via gateway
A distinção do Viewer que sempre cai

Um Viewer consegue ler dados de Lakehouse e Warehouse via T-SQL (o SQL analytics endpoint), mas não consegue lê-los via Spark, pelas APIs do OneLake ou pelo Lakehouse explorer. Se alguém precisa consultar com um notebook, Viewer não basta — promova para Contributor ou adicione a pessoa a uma OneLake security role.

Domínio 1 · 30–35% do exame

Implementar e gerenciar uma solução de análise

Configuração e governança. Quatro grupos de objetivos: workspace settings, gerenciamento de ciclo de vida, segurança e governança, e orquestração. É neste domínio que vivem as questões de "em que ordem você executa estas etapas".

Objetivo 1.1Configurar workspace settings do Microsoft Fabric

Spark workspace settings

Workspace settings → Data Engineering/ScienceSpark settings. Quatro abas importam: Pool, Environment, Job admission (High concurrency) e Automatic log.

Starter pools vs. custom pools

Starter poolCustom pool
Tempo de início5–10 segundos (pré-aquecido, gerenciado pela Microsoft, best effort)2–5 minutos sob demanda; ~5 s num custom live pool com bibliotecas pré-instaladas
Tamanho de nóSomente MediumSmall → XX-Large
EscalaDinâmica, contra capacidade pré-aquecidaManual ou autoscale (nós mín./máx.)
CobrançaSó enquanto uma sessão está executando de fato. Inicialização, inicialização de contexto ociosa e desalocação não são cobradas.
Use paraExploração ad hoc, iteração rápidaProdução, latência previsível, controle de recursos

Famílias de nós

Tamanho do nóvCoresMemóriaMáx. de nós numa F64
Small432 GB96
Medium864 GB48
Large16128 GB24
X-Large32256 GB12
XX-Large64512 GB6
A fórmula por trás de toda questão de dimensionamento do Spark

1 Capacity Unit = 2 Spark vCores, e o burst multiplier padrão é 3×.
Então F64 → 64 × 2 = 128 vCores base → 384 vCores com burst. É por isso que um pool de 48 nós Medium (48 × 8 = 384) é o máximo que você consegue definir numa F64. X-Large e XX-Large exigem uma SKU que não seja trial.

  • Autoscale — defina nós mínimos e máximos; o decommissioning de executores vem ligado por padrão (spark.yarn.executor.decommission.enabled = true).
  • Dynamic executor allocation — reserva executores na submissão a partir do mínimo, pede mais durante a execução e libera ao terminar. Elimina o tuning manual por estágio.
  • Expiração de sessão — padrão de 20 minutos; o pool é desalocado 2 minutos depois da expiração. Pools de nó único são suportados (driver e executor no mesmo nó, com recursos pela metade).
  • High concurrency — permite que vários notebooks compartilhem uma sessão Spark (e, quando habilitado para pipelines, que atividades de notebook da mesma execução compartilhem a sessão). Reduz drasticamente o custo de inicialização para muitos notebooks pequenos.

Environments

Um Environment é um item do workspace que agrupa três coisas: Spark compute (versão do runtime + propriedades de sessão), bibliotecas (feeds públicos e uploads de .whl/.jar próprios) e resources (arquivos pequenos compartilhados entre os notebooks anexados).

  • Save deixa as mudanças em staging; Publish as aplica. Apenas um publish por vez; você não pode editar bibliotecas ou compute durante um publish.
  • Quick mode publica em cerca de 5 segundos. Full mode leva de 3 a 6 minutos para publicar mais 1 a 3 minutos na inicialização da sessão, mas produz um snapshot — use-o para pipelines, execuções agendadas e cargas compartilhadas.
  • Resources são em tempo real; nunca precisam de publish.
  • Anexe em três níveis: padrão do workspace (Workspace settings → Spark settings → aba Environment), notebook ou Spark job definition. Assim que um environment vira o padrão do workspace, só workspace admins podem atualizá-lo.
  • Anexar entre workspaces exige a mesma capacity e as mesmas configurações de segurança de rede, e a configuração de compute do environment de origem é ignorada — o pool do workspace atual prevalece.
  • Mudanças só valem na próxima sessão.

Domain workspace settings

Domains são agrupamentos lógicos de workspaces por área de negócio — o mecanismo para um modelo de governança federado, tipo data mesh. Subdomains os refinam e herdam os admins do pai.

PapelPode fazer
Fabric adminCriar/renomear/excluir domains, nomear domain admins e contributors, atribuir workspaces, gerenciar todos os domains.
Domain adminEditar a descrição e a imagem, definir contributors, atribuir workspaces, sobrescrever delegated settings. Não pode excluir o domain, mudar o nome dele nem alterar outros admins.
Domain contributorAtribuir os próprios workspaces (precisa ser workspace admin). Sem acesso ao admin portal.
  • Atribua workspaces de três formas: por nome do workspace, por workspace admin (pega todos os workspaces que aquelas pessoas administram) ou por capacity. As duas últimas excluem os "My workspaces" pessoais.
  • Default domain — definido para usuários/grupos específicos: os workspaces não atribuídos deles passam a ser atribuídos, novos workspaces são atribuídos automaticamente e essas pessoas viram domain contributors.
  • Delegated settings permitem que um domain sobrescreva certas tenant settings — em especial sensitivity labels padrão e certification (habilitar/desabilitar, nomear os certificadores, informar uma URL de documentação).
Domains são descoberta, não segurança

Atribuir um workspace a um domain não muda visibilidade, acessibilidade nem permissões dos itens. Muda a filtragem no OneLake catalog e habilita configurações de governança federada. Se uma questão oferecer "atribuir a um domain" como forma de restringir acesso, está errada.

OneLake workspace settings

  • Shortcut cache — On/Off, um período de retenção de 1 a 28 dias e um botão Reset cache. O contador de retenção reinicia a cada acesso ao arquivo. Arquivos maiores que 1 GB não são cacheados. O cache vale para shortcuts de GCS, Amazon S3, compatíveis com S3 e via on-premises gateway — é uma otimização de custo de egresso entre nuvens.
  • Workspace identity — uma managed identity do workspace (criada só por um Admin). É o pré-requisito para trusted workspace access a contas ADLS Gen2 atrás de firewall e para managed private endpoints.
  • Managed private endpoints — conectividade privada do Spark do Fabric a fontes de dados atrás de uma VNet.

Apache Airflow job workspace settings

Apache Airflow job é o sucessor do Workflow Orchestration Manager do ADF: um serviço gerenciado de Airflow para orquestração code-first com DAGs em Python.

  • Airflow 2.10.5 sobre Python 3.12. Não dá para mudar a versão do Airflow de um job existente — crie um novo.
  • Suporta Git sync para armazenar DAGs, Azure Key Vault como backend de segredos, pacotes privados, autoscaling, alta disponibilidade, deferrable operators e pause/resume TTL.
  • Redes privadas/virtuais não são suportadas.
  • Escolha Airflow em vez de um pipeline quando a equipe já escreve DAGs de Airflow, precisa de ramificação ou geração dinâmica de tarefas em Python, ou está migrando DAGs existentes. Escolha um pipeline do Fabric para orquestração sem código.

Objetivo 1.2Implementar gerenciamento de ciclo de vida no Fabric

Configurar controle de versão (Git integration)

A Git integration é configurada no nível do workspace e liga um workspace a um branch e uma pasta. Provedores suportados: Azure DevOps, GitHub e GitHub Enterprise — apenas na nuvem. Somente um Admin do workspace pode conectá-lo a um repositório.

Etapa 1
ConnectWorkspace settings → Git integration. Escolha org, projeto, repo, branch e pasta.
Etapa 2
CommitEnvia as mudanças do workspace para o branch. A estrutura de pastas é preservada.
Etapa 3
UpdateTraz as mudanças do branch para o workspace.
Etapa 4
Branch outCria um novo branch + novo workspace para trabalho isolado de feature.

Tipos de item suportados (lista parcial — saiba o formato geral, não cada entrada): Lakehouse, Notebook, Spark Job Definition, Environment, GraphQL, User Data Functions, Copy Job, Dataflow Gen2, Pipeline, Mirrored Database, Warehouse, Mirrored Azure Databricks Catalog, Eventhouse, Eventstream, KQL Database, KQL Queryset, Real-Time Dashboard, Activator, SQL database, Variable Library. Vários itens de Power BI e Data Science ainda estão em Preview.

Itens não suportados não impedem a conexão

Se o workspace contém tipos de item que o Git não suporta, você ainda consegue conectar. Esses itens são ignorados — nunca salvos, nunca sincronizados, nunca excluídos — mas aparecem no painel de source control e você não consegue fazer commit nem update deles. Reports ligados a modelos semânticos do Azure AS / SSAS, push datasets, live connections e modelos semânticos Model v1 não são suportados.

Estados do Git

  • Synced — idêntico no workspace e no branch.
  • Uncommitted — alterado só no workspace. Faça commit.
  • Modified / Update required — alterado só no branch. Faça update.
  • Conflict — alterado nos dois. Resolva escolhendo a versão do workspace ou a do branch, item a item.

Database projects

Para Warehouses, é o controle de versão do schema em si. Use a extensão SQL Database Projects no VS Code (ou Azure Data Studio) para extrair o warehouse num .sqlproj, comparar com outro ambiente usando Schema Compare e publicar com o SqlPackage. É a resposta quando o requisito é "implantar apenas as mudanças de schema como parte de um release pipeline de DevOps existente", em vez de "promover itens inteiros".

Deployment pipelines

O mecanismo nativo de promoção de conteúdo do Fabric: de 2 a 10 estágios (padrão 3 — Development, Test, Production), cada estágio com um workspace atribuído.

ConceitoComportamento
PairingItens são pareados entre estágios adjacentes e continuam pareados mesmo se renomeados. O pareamento acontece ao atribuir um workspace a um estágio, ou ao implantar conteúdo ainda não pareado.
Duplicatas não pareadasDois itens com o mesmo nome e tipo em workspaces adjacentes que nunca foram pareados vão criar duplicatas na implantação, não sobrescrever. Essa é a pegadinha clássica.
Deployment rulesSobrescritas por estágio para fontes de dados e parâmetros, para que o Test aponte para o lakehouse de Test.
Não é copiadoPermissões e configurações de compartilhamento nunca são copiadas adiante. Alguns agendamentos de refresh também não.
AutomaçãoAPIs REST de Deployment Pipelines, além do Fabric CLI e do provider do Terraform para CI/CD completo.
PermissãoWorkspace admin, para criar pipelines, atribuir workspaces, implantar e definir regras.
Git integration vs. deployment pipelines — resolvem problemas diferentes
Git integrationDeployment pipelines
PropósitoHistórico de versões, branching, code review, backupPromover conteúdo Dev → Test → Prod
Unidade de trabalhoUm commit num branchUma implantação entre estágios adjacentes
GatilhoCommit / update, fluxo de PRBotão manual ou API REST

Uma configuração madura usa os dois: Git no workspace de desenvolvimento e para code review, deployment pipelines (ou as APIs) para promoção.

Variable libraries

Um item do workspace que guarda variáveis de configuração mais value sets alternativos — um por estágio do ciclo de vida — com exatamente um value set ativo por vez. Os consumidores resolvem o valor a partir do conjunto ativo no próprio workspace, então a mesma definição de pipeline aponta para o lakehouse de dev em Dev e para o de prod em Prod, sem precisar de deployment rule.

  • Tipos: string, integer, boolean e referências a itens.
  • Consumidores: Pipeline, shortcut de Lakehouse, Notebook (via notebookutils.variableLibrary e %%configure), Dataflow Gen2, Copy job, User data functions, Plan.
  • Limites: até 1.000 variáveis e 1.000 value sets; menos de 10.000 células no total; item ≤ 1 MB; notas e descrições ≤ 2.048 caracteres.
  • Não dá para excluir o value set ativo — ative outro antes.
  • Funciona tanto com Git integration quanto com deployment pipelines, e é exposto pelas APIs públicas do Fabric.

Objetivo 1.3Configurar segurança e governança

As quatro camadas de controle de acesso do Fabric

Camada 1
Workspace rolesAdmin / Member / Contributor / Viewer. Grosseira, vale para todos os itens.
Camada 2
Item permissionsRead, ReadData, ReadAll, Write, Reshare, Execute, Build.
Camada 3
OneLake securityRoles no plano de dados: escopo de tabela/pasta, mais restrições de RLS e CLS.
Camada 4
Nativa do engineGRANT/DENY em T-SQL, políticas de RLS, CLS, dynamic data masking, RLS no KQL.

Controles de acesso no nível do item

PermissãoConcedeEfeito em Lakehouse / Warehouse
ReadVer o item e seus metadados; conectar ao SQL analytics endpointSó metadados — nenhum dado sem uma concessão adicional
ReadDataConsultar dados via T-SQLAcesso pelo SQL endpoint (modo delegated identity)
ReadAllLer os arquivos subjacentesAcesso via OneLake / Spark / Lakehouse explorer; corresponde à role DefaultReader do OneLake
WriteModificar o itemAcesso completo a metadados + SQL + OneLake
ExecuteExecutar o itemNotebooks, pipelines, Spark job definitions
BuildConstruir conteúdo novo sobre um modelo semânticoNecessário para criar relatórios Direct Lake
ReshareRepassar a concessão

OneLake security (roles no plano de dados)

Uma OneLake security role tem quatro partes: dados (as tabelas ou pastas que ela cobre), permissões, membros e restrições (exclusões de linhas e colunas). As roles são definidas uma vez e aplicadas por todos os engines do Fabric — e por "authorized engines" externos registrados, que buscam a política efetiva pelas APIs do OneLake.

  • Somente workspace Admin ou Member pode criar OneLake security roles.
  • Elas governam Viewers e usuários com permissão de item Read/ReadData. Workspace Admins, Members e Contributors as ignoram completamente — sempre leem e escrevem tudo.
  • Todo lakehouse já vem com uma role DefaultReader que concede acesso a quem tem ReadAll. Ela pode ser editada ou excluída.
  • Segurança em nível de pasta no lakehouse de origem também governa os shortcuts que apontam para ele — a segurança acompanha o dado, não a referência.
  • Subpastas herdam as permissões do pai por padrão.
"Restringir o engenheiro a uma pasta" — por que a resposta óbvia falha

Se a pessoa é Contributor do workspace, nenhuma OneLake role vai restringi-la. A sequência correta é: remover a pessoa do workspace role, conceder Read no nível do item e então adicioná-la a uma OneLake security role com escopo naquela pasta.

Segurança de linha, coluna e objeto no Warehouse

Row-level security

Uma função de predicado inline table-valued mais uma security policy. RLS funciona tanto no Warehouse quanto no SQL analytics endpoint.

T-SQL · Row-level security
-- 1. A função de predicado: retorna uma linha quando o acesso é permitido
CREATE FUNCTION Security.tvf_SecurityPredicate(@SalesRep AS nvarchar(50))
    RETURNS TABLE
WITH SCHEMABINDING
AS
    RETURN SELECT 1 AS result
    WHERE @SalesRep = USER_NAME()
       OR USER_NAME() = 'gerente@contoso.com';
GO

-- 2. Vincule à tabela
CREATE SECURITY POLICY Security.SalesFilter
ADD FILTER PREDICATE Security.tvf_SecurityPredicate(SalesRep)
ON dbo.Sales
WITH (STATE = ON);
GO
FILTER vs. BLOCK predicate

ADD FILTER PREDICATE esconde linhas silenciosamente nas leituras. ADD BLOCK PREDICATE ... AFTER INSERT | AFTER UPDATE | BEFORE UPDATE | BEFORE DELETE lança erro em escritas que violariam a regra. Se uma questão pergunta como impedir um usuário de gravar uma linha fora da região dele, a resposta é block predicate.

Column-level security

T-SQL · Column-level security
GRANT SELECT ON dbo.Employees(EmployeeId, FirstName, LastName, Department)
    TO [analistas@contoso.com];

-- ou negue colunas específicas numa tabela já concedida
DENY SELECT ON dbo.Employees(Salary, NationalId) TO [analistas@contoso.com];

Object-level security

T-SQL · Roles e concessões em objetos
CREATE ROLE SalesAnalyst;
GRANT SELECT ON SCHEMA::sales TO SalesAnalyst;
DENY SELECT ON dbo.PayrollDetail TO SalesAnalyst;
ALTER ROLE SalesAnalyst ADD MEMBER [usuario@contoso.com];

Dynamic data masking

Mascara valores apenas no resultado da consulta — o dado armazenado não muda. Usuários com a permissão UNMASK (e workspace Admin/Member/Contributor) veem os valores reais.

FunçãoSintaxeResultado
Defaultdefault()XXXX para strings, 0 para numéricos, 1900-01-01 para datas
Emailemail()aXXX@XXXX.com
Randomrandom(1, 100)Um número aleatório no intervalo — somente tipos numéricos
Partialpartial(0,"XXXX-",4)Preserva um prefixo e um sufixo, preenche o meio
Datetimedatetime("M")Mascara tudo, exceto a parte da data escolhida
T-SQL · Dynamic data masking
ALTER TABLE dbo.Customers
    ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');

ALTER TABLE dbo.Payments
    ALTER COLUMN CardNumber ADD MASKED WITH (FUNCTION = 'partial(0,"XXXX-XXXX-XXXX-",4)');

GRANT UNMASK TO [financeiro-admin@contoso.com];
ALTER TABLE dbo.Customers ALTER COLUMN Email DROP MASKED;   -- remover
Masking é ofuscação, não proteção

Quem pode consultar a tabela ainda consegue inferir valores mascarados com WHERE Salary > 100000. DDM complementa RLS/CLS; nunca substitui.

Sensitivity labels e endorsement

  • Sensitivity labels vêm do Microsoft Purview Information Protection. São aplicados por item, propagam-se para baixo pela linhagem (um relatório herda do seu modelo semântico) e podem carregar criptografia que acompanha os arquivos exportados. Aplicá-los exige que o label esteja publicado para o usuário e a tenant setting habilitada. Um domain pode definir um label padrão via delegated settings.
  • Endorsement tem três níveis:
    • Promoted — qualquer usuário com permissão de escrita no item pode promover.
    • Certified — somente usuários nomeados como certificadores pelo Fabric admin (ou pelo domain admin, via delegated settings) podem certificar.
    • Master data — a fonte autoritativa para uma área de assunto.
    Itens endossados aparecem melhor ranqueados e são filtráveis no OneLake catalog.

Fabric audit logs

  • A atividade do Fabric flui para o unified audit log do Microsoft Purview / Microsoft 365. Acesse pelo portal de compliance do Purview, pelo link Audit logs no admin portal do Fabric, ou programaticamente com Get-PowerBIActivityEvent / a API REST Admin Activity Events.
  • O admin monitoring workspace traz o relatório Feature usage and adoption e um modelo semântico de dados de atividade para Fabric admins.
  • Workspace monitoring (workspace settings → Monitoring → Log workspace activity) provisiona um Eventhouse somente leitura no workspace que coleta logs de diagnóstico e métricas de Eventhouse, Eventstream, pipelines, Copy jobs, mirrored databases, GraphQL e modelos semânticos. A retenção é de 30 dias; é cobrado como consumo normal de capacity de Eventhouse/Eventstream; você pode habilitar workspace monitoring ou Log Analytics, não os dois. Consulte com KQL ou SQL.
  • Operações de plano de dados do OneLake aparecem com nomes que correspondem às APIs do ADLS Gen2 (CreateFile, DeleteFile…). Requisições de leitura e requisições das cargas de trabalho do Fabric não são incluídas.

Objetivo 1.4Orquestrar processos

Escolher entre Dataflow Gen2, pipeline e notebook

Copy activity (pipeline)Copy jobDataflow Gen2Notebook / SparkEventstream
Caso de usoMigração de lake/DW, ingestão, transformação leveIngestão, cópia incremental, replicaçãoIngerir, transformar, limpar, perfilarIngerir, transformar, processar, perfilarIngestão e transformação de eventos
PersonaData engineer / integradorAnalista, integrador, engenheiroEngenheiro, integrador, analistaData engineer, integradorEngenheiro, cientista, desenvolvedor
HabilidadesETL, SQL, JSONETL, SQL, JSONETL, M, SQLSpark (Python, Scala, SQL, R)SQL, JSON, mensageria
CódigoSem/baixo códigoSem/baixo códigoSem/baixo códigoCom códigoSem código
Fontes50+ conectores50+ conectores150+ conectoresCentenas de bibliotecas SparkCDC, Kafka, mensageria, streams
Complexidade de transformaçãoBaixaBaixaBaixa → alta (300+ funções)Baixa → alta (ilimitada)Baixa
InterfaceAssistente, canvasAssistente, canvasPower QueryNotebook, Spark job definitionCanvas
Como eliminar alternativas rapidamente
  • O cenário diz "Power Query", "o analista sabe M" ou "150+ conectores" → Dataflow Gen2.
  • O cenário diz "petabyte", "lógica complexa/customizada", "ML", "não estruturado" → notebook.
  • O cenário diz "CDC", "sem precisar de pipeline", "poucos cliques", "retomar de onde parou" → Copy job.
  • O cenário diz "orquestrar", "em caso de falha", "iterar sobre", "depois atualizar o modelo" → pipeline.
  • O cenário diz "sem agendamento", "à medida que os eventos chegam" → Eventstream.

Atividades de pipeline que você precisa reconhecer

Mover e transformar

Copy data · Copy job · Dataflow Gen2 · Notebook · Spark Job Definition · Script · Stored procedure · Lakehouse maintenance Preview

Controle de fluxo

ForEach · If Condition · Switch · Until · Wait · Set variable · Filter · Invoke pipeline · Fail

Lookup e metadados

Lookup · Get Metadata · Web · Web hook · Azure Function

Notificação

Office 365 Outlook · Teams · Semantic model refresh · KQL activity

Toda atividade suporta quatro condições de dependência na seta de saída: On success, On failure, On completion e On skip. As atividades também expõem Retry, Retry interval, Timeout e Secure output/input na aba General.

Parâmetros e expressões dinâmicas

Parâmetros são definidos uma vez por execução e são somente leitura dentro dela. Variáveis são mutáveis durante a execução, via as atividades Set variable e Append variable.

ExpressãoRetorna
@pipeline().PipelineName / .PipelineNome / ID do pipeline
@pipeline().RunIdID desta execução — a chave de correlação padrão para logging
@pipeline().TriggerTime, .TriggerName, .TriggerIdMetadados do trigger, em UTC ISO 8601
@pipeline().DataFactoryID do workspace
@pipeline()?.TriggeredByPipelineNameNome do pipeline pai, ou null
@pipeline().parameters.<nome> / @variables('nome')Valor do parâmetro / variável
@activity('Lookup1').output.firstRow.WatermarkValueUm campo da saída de uma atividade anterior
@activity('Copy1').error.messageMensagem de falha, para logar no caminho de erro
@item()O elemento atual dentro de um ForEach
@pipeline()?.TriggerEvent?.FileNameNome do arquivo vindo de um storage event trigger (? protege contra null em execuções manuais)
Linguagem de expressões de pipeline
// Interpolação de string usa @{ }; um @ sozinho inicia uma expressão; @@ escapa um @ literal
"Test_@{formatDateTime(utcNow(), 'yyyy-MM-dd')}"

// Funções comuns por categoria
Data/hora : addDays addHours addMinutes formatDateTime utcNow startOfDay ticks convertFromUtc
String    : concat replace split substring startsWith endsWith toLower trim indexOf guid
Coleção   : contains empty first last length skip take union join intersection
Lógica    : and or not if equals greater greaterOrEquals less lessOrEquals
Conversão : array bool float int string json coalesce createArray base64 uriComponent
Matemática: add sub mul div mod min max rand range

// Exemplos práticos
@concat('vendas_', formatDateTime(utcNow(), 'yyyyMMdd'), '.parquet')
@if(greater(activity('Lookup_RowCount').output.firstRow.cnt, 0), 'load', 'skip')
@formatDateTime(addDays(utcNow(), -1), 'yyyy-MM-ddTHH:mm:ssZ')
@coalesce(pipeline().parameters.RunDate, formatDateTime(utcNow(), 'yyyy-MM-dd'))

Passando parâmetros para um notebook

Python · notebookutils
# No notebook CHAMADO: marque uma célula como "parameter cell" (no menu da célula)
# e declare os padrões ali. Os valores do chamador sobrescrevem em tempo de execução.
run_date = "2026-01-01"
layer    = "bronze"

# Devolver um valor ao chamador
import notebookutils
notebookutils.notebook.exit(str(rows_written))

# No notebook CHAMADOR
exit_val = notebookutils.notebook.run("Load_Bronze", 90, {"run_date": "2026-08-30", "layer": "silver"})
# 4º argumento posicional = ID do workspace, para chamadas entre workspaces (Runtime 1.2+)

A partir de um pipeline, os Base parameters da atividade Notebook mapeiam para a mesma parameter cell, e o valor de exit() do notebook é lido adiante como @activity('Notebook1').output.result.exitValue.

Orquestrando muitos notebooks com um DAG

Python · notebookutils.notebook.runMultiple
DAG = {
    "activities": [
        {"name": "LoadCustomers", "path": "nb_load_customers",
         "timeoutPerCellInSeconds": 120, "args": {"layer": "bronze"}},
        {"name": "LoadOrders", "path": "nb_load_orders",
         "timeoutPerCellInSeconds": 120, "args": {"layer": "bronze"}},
        {"name": "BuildFactSales", "path": "nb_build_fact",
         "timeoutPerCellInSeconds": 300,
         "retry": 1, "retryIntervalInSeconds": 30,
         "dependencies": ["LoadCustomers", "LoadOrders"]}
    ],
    "timeoutInSeconds": 43200,   # 12 h, o padrão
    "concurrency": 50          # 0 = sem limite
}
notebookutils.notebook.runMultiple(DAG, {"displayDAGViaGraphviz": True})

# Forma paralela simples, sem dependências:
notebookutils.notebook.runMultiple(["nb_a", "nb_b", "nb_c"])
runMultiple vs. um pipeline de atividades de notebook

O runMultiple executa todos os notebooks filhos numa única sessão Spark — sem inicialização de sessão por notebook, então dez notebooks pequenos terminam numa fração do tempo e do CU. Use um pipeline quando precisar de tipos de atividade variados, orquestração entre itens, retries no nível do item ou event triggers.

Agendamentos e event-based triggers

MecanismoComo funcionaObservações
Sob demandaRun no editorOs trigger parameters resolvem para null — por isso as proteções com ? importam
Agendamento fixoHome → Schedule. Frequência, data de início e fim, fuso horárioData de início e fim são obrigatórias; use uma data de fim bem distante. Até 20 agendamentos por pipeline
Agendamento por intervalo PreviewIntervalos fixos e não sobrepostosExpõe Window start time / Window end time como trigger parameters — a forma limpa de fazer cargas em lote com janela tumbling
Storage event triggerHome → Trigger → cria um Eventstream + um item Activator (Reflex)Fontes: OneLake events, Azure Blob Storage events. Filtre pelo campo Subject (pasta, nome do arquivo, extensão, container)
Fabric item / job eventsItem do workspace criado/atualizado/excluído; job eventsTipos de evento como Microsoft.Fabric.ItemCreateSucceeded, …ItemUpdateFailed

Os payloads de storage event seguem o schema CloudEvents: source, subject, type (por exemplo Microsoft.Storage.BlobCreated), time, id, data, specversion. O Fabric extrai nome do arquivo e caminho da pasta do Subject e os expõe no expression builder como trigger parameters.

Onde o trigger realmente fica

Um event trigger criado a partir de um pipeline é armazenado como um item Activator (Reflex) separado no workspace, não dentro do pipeline. Para encontrá-lo, editá-lo ou desativá-lo, abra esse item Reflex ou use Triggers → View triggers no pipeline. Excluir o pipeline não exclui o trigger.

Padrões de orquestração que vale conhecer pelo nome

Ingestão orientada a metadados

Uma tabela de controle lista fontes, destinos, watermarks e tipo de carga. Um Lookup a lê, um ForEach itera sobre @activity('Lookup').output.value, e uma única Copy activity parametrizada atende todas as fontes. Desligue Sequential e ajuste o Batch count (máx. 50) para paralelismo.

Orquestração medallion

Bronze (bruto, append-only) → Silver (limpo, deduplicado, conformado) → Gold (star schema, agregado). Um pipeline por camada, encadeados com Invoke pipeline, para que cada camada possa ser reexecutada independentemente.

Loop de watermark

Lookup do watermark antigo → Copy das linhas > watermark → Lookup do novo máximo → Stored procedure grava o novo watermark. Atualize o watermark somente em caso de sucesso, para que uma falha reprocesse em vez de pular dados.

Reexecuções idempotentes

Projete de forma que reexecutar não duplique: MERGE em vez de INSERT, sobrescrita de partição com replaceWhere em vez de append, e uma chave de negócio determinística. Cenários de prova adoram "o pipeline foi reexecutado após uma falha e as linhas duplicaram".

Domínio 2 · 30–35% do exame

Ingerir e transformar dados

O domínio mais prático. Padrões de carga e modelagem dimensional, ingestão e transformação em lote com PySpark / T-SQL / KQL, e streaming com Eventstream, Eventhouse e Spark Structured Streaming.

Objetivo 2.1Projetar e implementar padrões de carga

Cargas full e incremental

PadrãoQuandoImplementação no Fabric
Carga full / truncate-and-reloadTabelas pequenas, sem marcador de mudança confiável, reconstrução de dimensões cujas surrogate keys não são referenciadasTRUNCATE TABLE + INSERT…SELECT, CTAS, ou mode("overwrite") no Spark
Watermark / high-water markA origem tem uma coluna monotonicamente crescente (ROWVERSION, datetime, identity)Lookup + Copy parametrizado; ou Copy job em modo incremental. Captura apenas inserts e updates
CDCVocê precisa capturar deletes, ou a origem muda muitoCopy job em modo CDC, fontes CDC do Eventstream, ou Mirroring
Sobrescrita de partiçãoRecarregar uma fatia delimitada, por exemplo o dia anteriorreplaceWhere do Delta, ou dynamic partition overwrite
Upsert / mergeCorreções tardias numa tabela existenteMERGE do Delta no Spark, MERGE em T-SQL no Warehouse
PySpark · padrões incrementais
from delta.tables import DeltaTable
from pyspark.sql import functions as F

# 1. Ler o watermark atual do destino
watermark = spark.sql("SELECT COALESCE(MAX(ModifiedDate), '1900-01-01') AS wm FROM silver.customers") \
                 .collect()[0]["wm"]

# 2. Trazer apenas linhas novas/alteradas
src = (spark.read.format("delta").load("Tables/bronze/customers")
            .filter(F.col("ModifiedDate") > F.lit(watermark)))

# 3. UPSERT com MERGE
tgt = DeltaTable.forName(spark, "silver.customers")
(tgt.alias("t")
    .merge(src.alias("s"), "t.CustomerId = s.CustomerId")
    .whenMatchedUpdateAll(condition="s.ModifiedDate > t.ModifiedDate")
    .whenNotMatchedInsertAll()
    .whenNotMatchedBySourceUpdate(set={"IsDeleted": "true"})   # soft delete
    .execute())

# 4. Alternativa: sobrescrita idempotente de uma fatia delimitada
(df.write.format("delta").mode("overwrite")
   .option("replaceWhere", "LoadDate >= '2026-08-01' AND LoadDate < '2026-09-01'")
   .saveAsTable("silver.orders"))

Preparando dados para um modelo dimensional

A orientação do Fabric é o Kimball clássico: um star schema com tabelas fato cercadas por tabelas dimensão. Fatos guardam medidas mais as chaves de dimensão, num grão declarado; dimensões descrevem as entidades.

Constraints do Fabric Warehouse são NOT ENFORCED

PRIMARY KEY, UNIQUE e FOREIGN KEY só podem ser criadas com a opção NOT ENFORCED. São metadados usados pelo otimizador de consultas e por ferramentas de modelagem — o engine deixa você inserir uma chave duplicada ou uma linha de fato órfã sem reclamar. Seu ETL precisa garantir unicidade e integridade referencial por conta própria.

Slowly changing dimensions

TipoComportamentoImplementação
Tipo 0Nunca muda (ex.: data original de cadastro)Apenas insert
Tipo 1Sobrescreve — sem históricoMERGE … WHEN MATCHED THEN UPDATE
Tipo 2Nova linha a cada mudança, com StartDate/EndDate/IsCurrentExpira a linha atual e insere a nova versão
Tipo 3Guarda um valor anterior numa coluna extraPreviousValue = Value e depois atualiza Value
Tipo 6Híbrido 1+2+3Linhas Tipo 2 mais uma coluna de "valor atual" atualizada em todas as linhas
T-SQL · SCD Tipo 2 no Fabric Warehouse
-- Etapa 1: expirar linhas cujos atributos monitorados mudaram
UPDATE d
   SET d.EndDate  = CAST(GETDATE() AS date),
       d.IsCurrent = 0
  FROM dbo.DimProduct AS d
  JOIN staging.Products AS s ON s.ProductID = d.ProductID
 WHERE d.IsCurrent = 1
   AND (d.ProductName <> s.ProductName OR d.Category <> s.Category);

-- Etapa 2: inserir a nova versão vigente (e os membros totalmente novos)
INSERT INTO dbo.DimProduct
      (ProductID, ProductName, Category, StartDate, EndDate, IsCurrent, IsInferred)
SELECT s.ProductID, s.ProductName, s.Category, CAST(GETDATE() AS date), NULL, 1, 0
  FROM staging.Products AS s
  LEFT JOIN dbo.DimProduct AS d
       ON d.ProductID = s.ProductID AND d.IsCurrent = 1
 WHERE d.ProductID IS NULL;

-- Etapa 3: soft delete dos membros que sumiram da origem
UPDATE dbo.DimProduct
   SET IsDeleted = 1, IsCurrent = 0, EndDate = CAST(GETDATE() AS date)
 WHERE IsCurrent = 1
   AND NOT EXISTS (SELECT 1 FROM staging.Products s WHERE s.ProductID = DimProduct.ProductID);
T-SQL · carga de fato com lookup SCD2 correto no tempo
INSERT INTO dbo.FactSales (DateKey, CustomerKey, ProductKey, Quantity, SalesAmount, SourceOrderNumber)
SELECT CONVERT(int, FORMAT(o.OrderDate, 'yyyyMMdd')),
       ISNULL(c.CustomerKey, -1),          -- -1 = o membro "Unknown"
       ISNULL(p.ProductKey,  -1),
       l.Quantity, l.Quantity * l.UnitPrice, o.SalesOrderNumber
  FROM staging.SalesOrders o
  JOIN staging.SalesOrderLines l ON l.SalesOrderID = o.SalesOrderID
  -- join point-in-time: pega a versão da dimensão válida na data do pedido
  LEFT JOIN dbo.DimCustomer c
         ON c.CustomerID = o.CustomerID
        AND o.OrderDate >= c.StartDate
        AND (o.OrderDate < c.EndDate OR c.EndDate IS NULL)
  LEFT JOIN dbo.DimProduct p
         ON p.ProductID = l.ProductID AND p.IsCurrent = 1
 WHERE o.SalesOrderNumber > @LastLoadedOrderNumber;   -- high-water mark

Regras que o exame cobra sobre carga dimensional

  • Nunca faça truncate-and-reload numa dimensão cujas surrogate keys são referenciadas por fatos — você deixaria todas as linhas de fato órfãs.
  • Soft delete, nunca hard delete, de membros de dimensão. Fatos históricos ainda apontam para eles.
  • Membros inferidos (ou fatos que chegam cedo demais): quando um fato referencia uma chave de dimensão desconhecida, insira uma linha placeholder marcada com IsInferred = 1 e enriqueça-a quando o registro real chegar.
  • A dimensão de data não tem sistema de origem — gere-a com uma CTE recursiva ou uma tabela de números, bem além da data atual.
  • Carregue dimensões antes dos fatos, sempre. Os fatos precisam das chaves.
  • Prefira tabelas de staging num schema staging próprio, limpas com TRUNCATE TABLE no início de cada execução.

Um padrão de carga para dados de streaming

Dados de streaming que chegam ao repouso seguem o formato medallion com um ajuste — a camada bronze é append-only e nunca é alterada:

Bronze
Bruto, append-onlyPayload exato mais o timestamp de ingestão. Nunca editado, então sempre pode ser reprocessado.
Silver
Limpo, deduplicadoSchema aplicado, dados tardios reconciliados, chaves de negócio conformadas.
Gold
Star schema / agregadosO que modelos semânticos Direct Lake e dashboards leem.

Lakehouse ou Eventhouse para a landing zone? Eventhouse quando as consultas são de séries temporais e interativas e a latência é medida em segundos; Lakehouse quando o stream alimenta os mesmos pipelines em lote de todo o resto. Dá para ter os dois barato: aterrisse no Eventhouse e ligue o OneLake availability, que materializa uma cópia Delta legível pelo Spark e pelo SQL endpoint sem custo extra de armazenamento.

Objetivo 2.2Ingerir e transformar dados em lote

OneLake shortcuts

Um shortcut é um ponteiro que aparece como uma pasta. Nenhum dado é copiado e nenhum armazenamento é consumido.

Shortcuts internos

Apontam para outro item do Fabric: Lakehouse, Warehouse, KQL database, Mirrored database, Mirrored Azure Databricks catalog, SQL database, modelo semântico. A autorização usa a identidade de quem chama — a pessoa precisa de permissão de leitura no destino.

Shortcuts externos

Amazon S3 · compatível com S3 · ADLS Gen2 · Azure Blob Storage · Google Cloud Storage · Dataverse · Iceberg · OneDrive/SharePoint. A autorização é delegada por uma cloud connection, então só quem tem permissão nessa conexão consegue criar o shortcut.

Tables/Files/
AninhamentoSó no nível superior — sem subdiretóriosQualquer profundidade
DescobertaMetadados e schema Delta sincronizam automaticamente; a tabela aparece no SQL endpointSem descoberta de tabelas
Use paraDatasets Delta estruturados, fontes internas do OneLake, schema shortcutsDados não e semiestruturados, qualquer formato, stores externos
Lendo um shortcut a partir de cada engine
# Spark — um shortcut em Tables/ se comporta exatamente como uma tabela nativa
df = spark.read.format("delta").load("Tables/MyShortcut")
df = spark.sql("SELECT * FROM MyLakehouse.MyShortcut LIMIT 1000")

-- SQL analytics endpoint
SELECT TOP (100) * FROM [MyLakehouse].[dbo].[MyShortcut];

// KQL — um shortcut numa KQL database é uma external table
external_table('MyShortcut')
| take 100
Limites e armadilhas de shortcuts
  • Até 100.000 shortcuts por item; até 10 shortcuts por caminho do OneLake; o encadeamento é limitado a 5 níveis de profundidade.
  • Nomes não podem conter % nem +, e caracteres não latinos não são suportados. Delta não suporta nomes de tabela com espaços — um shortcut com espaço no nome não será reconhecido como tabela Delta.
  • Excluir um shortcut remove apenas o ponteiro. Mas excluir conteúdo dentro de um shortcut exclui na origem se você tiver permissão lá.
  • A visualização de linhagem tem escopo de um workspace e não mostra shortcuts externos.
  • Pode levar até um minuto para a Table API reconhecer um shortcut novo.
  • Schema shortcuts só funcionam em lakehouses com schema habilitado.

Mirroring

O mirroring replica continuamente um banco operacional externo para o OneLake como tabelas Delta, sem ETL para construir. A computação de replicação é gratuita e cada capacity unit inclui 1 TB de armazenamento de mirroring gratuito (então F64 → 64 TB). A latência pode chegar a ~15 segundos.

ModalidadeO que replicaFontes
Database mirroringDados e metadados, gravados como Delta no OneLakeAzure SQL DB, Azure SQL MI, SQL Server, Azure Cosmos DB, Azure Database for PostgreSQL, Snowflake, Oracle, Google BigQuery, SAP Datasphere, Fabric SQL DB; MySQL e SharePoint list em Preview
Metadata mirroringSó a estrutura de catálogo — os dados ficam onde estão e são acessados por shortcutsAzure Databricks Unity Catalog; Dremio Preview
Open mirroringVocê envia os dados de mudança para uma landing zone no OneLake via API públicaQualquer aplicação própria ou de ISV
  • Cria duas coisas no workspace: o processo de replicação e um SQL analytics endpoint somente leitura.
  • Exige uma capacity do Fabric em execução — pausar a capacity interrompe a replicação.
  • A retenção Delta é de 1 dia por padrão para bancos criados após meados de junho de 2025 (7 dias para os mais antigos); configurável em Settings → Delta table management ou via retentionInDays na API.
Mirroring vs. shortcut vs. Copy — a regra de uma linha
  • Mirroring — um banco operacional que você quer disponível de forma contínua e barata para análise, em Delta, quase em tempo real.
  • Shortcut — dados que já estão num lake (OneLake, ADLS, S3, GCS) e que você não quer duplicar.
  • Copy activity / Copy job — movimentação em lote agendada, ou qualquer caso em que você precise de transformação, filtragem ou gateway.

Ingerindo com pipelines e Copy job

Copy activity

  • Mais de 50 conectores de origem e 40 de destino; suporta staging (um salto intermediário em blob/lakehouse) para origens que não conseguem enviar direto ao destino.
  • Degree of copy parallelism, tolerância a falhas (pular linhas incompatíveis e registrá-las) e copy behavior (preservar hierarquia, achatar hierarquia, mesclar arquivos).
  • Conectividade a ambientes on-premises pelo on-premises data gateway, e a uma VNet pelo VNet data gateway.
  • Métricas de saída disponíveis adiante: rowsRead, rowsCopied, rowsSkipped, throughput, dataConsistencyVerification.

Copy job

Um item independente — sem precisar de pipeline — para cópia full, incremental e replicação por CDC.

Incremental por watermarkBaseado em CDC
CapturaInserts e updatesInserts, updates e deletes
Precisa deUma coluna incremental confiável: ROWVERSION, datetime, date, string interpretada como datetime, inteiroCDC habilitado na origem e suportado pelo conector
Permite destino SCD2NãoSim

Métodos de atualização no destino: Append (padrão), Merge (exige coluna-chave; com CDC também aplica deletes), Overwrite e SCD Tipo 2 com effective dating. O Copy job também grava colunas de auditoria opcionais por linha — hora de extração, caminho do arquivo de origem, IDs de workspace/job/run e os limites da janela incremental — que é a resposta nativa para questões de linhagem em nível de linha. Ele retoma do último checkpoint bem-sucedido após uma falha, suporta Git/CI-CD e Variable libraries, e tem um modo de auto-partitioning em Preview para leituras paralelas de tabelas grandes.

Transformando com PySpark, SQL e KQL

PySpark · o vocabulário de transformação
from pyspark.sql import functions as F, Window

# Leitura / escrita
df = spark.read.format("delta").load("Tables/bronze/orders")
df = spark.read.option("header",True).option("inferSchema",True).csv("Files/raw/*.csv")
df.write.format("delta").mode("append").saveAsTable("silver.orders")

# Formato
df2 = (df.withColumn("OrderYear", F.year("OrderDate"))
         .withColumn("Net", F.col("Gross") - F.col("Discount"))
         .withColumnRenamed("cust_id", "CustomerId")
         .drop("_ingest_raw")
         .filter(F.col("Status") != "Cancelled"))

# Agrupar e agregar
agg = (df2.groupBy("CustomerId", "OrderYear")
          .agg(F.sum("Net").alias("Revenue"),
               F.countDistinct("OrderId").alias("Orders"),
               F.max("OrderDate").alias("LastOrder")))

# Desnormalizar — faça broadcast do lado pequeno para evitar shuffle
wide = df2.join(F.broadcast(dim_customer), "CustomerId", "left")

# Window functions: manter a linha mais recente por chave
w = Window.partitionBy("CustomerId").orderBy(F.col("ModifiedDate").desc())
latest = df2.withColumn("rn", F.row_number().over(w)).filter("rn = 1").drop("rn")

# Semiestruturado
flat = (df.withColumn("j", F.from_json("payload", schema))
          .select("j.*")
          .withColumn("tag", F.explode("tags")))
pivoted = df2.groupBy("CustomerId").pivot("OrderYear").sum("Net")
Magics de notebook e trabalho entre linguagens
# %%pyspark  %%sql  %%scala  %%sparkr  %%html  %%configure

%%sql
CREATE OR REPLACE TABLE silver.customers AS
SELECT CustomerId, INITCAP(Name) AS Name, Country
FROM   bronze.customers
WHERE  CustomerId IS NOT NULL;

%%configure
{ "defaultLakehouse": { "name": "lh_silver" },
  "conf": { "spark.sql.shuffle.partitions": "200" } }
T-SQL · padrões de transformação no Warehouse
-- CTAS: a forma mais rápida de materializar um resultado transformado
CREATE TABLE gold.SalesByRegion AS
SELECT r.RegionName,
       SUM(f.SalesAmount)                    AS Revenue,
       COUNT_BIG(*)                           AS OrderCount,
       SUM(f.SalesAmount) / NULLIF(COUNT_BIG(*),0) AS AvgOrder
  FROM dbo.FactSales f
  JOIN dbo.DimRegion r ON r.RegionKey = f.RegionKey
 GROUP BY r.RegionName;

-- Window functions
SELECT CustomerId, OrderDate, SalesAmount,
       SUM(SalesAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate
                              ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningTotal,
       LAG(SalesAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate)        AS PrevOrder,
       ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY OrderDate DESC)      AS Recency
  FROM dbo.FactSales;

-- Agregação em múltiplos níveis
SELECT Country, City, SUM(Revenue) AS Revenue, GROUPING(City) AS IsCityTotal
  FROM gold.Sales
 GROUP BY ROLLUP (Country, City);       -- também: CUBE, GROUPING SETS

-- Cross-warehouse / cross-lakehouse numa consulta só (nome em três partes)
INSERT INTO gold.DimProduct
SELECT * FROM LakehouseSilver.dbo.products;
KQL · o essencial de transformação
Events
| where Timestamp > ago(7d) and Level in ("Error", "Critical")
| extend Duration = EndTime - StartTime,
         Region   = tostring(parse_json(Properties).region)
| project Timestamp, DeviceId, Region, Duration, Message
| summarize ErrorCount = count(),
            Devices    = dcount(DeviceId),
            p95        = percentile(Duration, 95),
            LastSeen   = max(Timestamp)
          by Region, bin(Timestamp, 1h)
| order by Timestamp asc, ErrorCount desc

Dados duplicados, ausentes e atrasados

ProblemaSparkT-SQLKQL
Duplicatas exatasdf.dropDuplicates(["OrderId"])ROW_NUMBER() + WHERE rn = 1summarize arg_max(Timestamp, *) by Id
Duplicatas num streamdropDuplicatesWithinWatermark(["Id"])summarize take_any(*) by Id
Valores ausentesdf.na.fill({"Qty":0}), df.na.drop(subset=[…])COALESCE(), ISNULL()coalesce(), iff(isnull(x), 0, x)
Chave de dimensão desconhecidaLeft join e depois coalesce(key, lit(-1))ISNULL(d.Key, -1) ou inserir um membro inferidoJoin leftouter + valor padrão
Eventos atrasadoswithWatermark("ts","10 minutes")Reprocessar a partição afetada com MERGEPolíticas de ingestion time; consultar por ingestion_time()
Linhas ruinsDirecionar para uma tabela quarantine em vez de falhar o jobTRY_CAST + uma tabela de rejeitadosUpdate policy com IsTransactional = false
Ingestão no Warehouse: os números para decorar
  • COPY INTO suporta CSV, JSONL e Parquet vindos de ADLS Gen2 e Azure Blob Storage; por padrão autentica com a identidade Entra de quem executa.
  • Mantenha os arquivos de origem com ao menos 4 MB (a orientação de performance da Microsoft mira 100 MB – 1 GB por arquivo) e use muitos arquivos em paralelo.
  • ADLS Gen2 tem desempenho melhor que o Blob Storage.
  • Evite INSERT unitários — agrupe com COPY INTO, INSERT…SELECT ou CTAS.
  • OPENROWSET(BULK …) consulta arquivos externos inline; o bcp está disponível em Preview para carga pelo lado do cliente.

Superfície T-SQL do Fabric Warehouse

Suportado

Tabelas, views, stored procedures, funções, roles e permissões · colunas IDENTITY · MERGE · TRUNCATE TABLE · tabelas #temp com escopo de sessão · CTEs (CTEs aninhadas em Preview) · um subconjunto de query/join hints · sp_rename para colunas · ALTER TABLE ADD de coluna anulável / DROP COLUMN / adicionar-remover constraints NOT ENFORCED · ALTER COLUMN em Preview · transações explícitas com snapshot isolation.

Não suportado

Triggers · Materialized views · Synonyms · CREATE USER · BULK LOAD · consultas recursivas · estatísticas multi-coluna criadas manualmente · SELECT … FOR XML · SET ROWCOUNT · SET TRANSACTION ISOLATION LEVEL · PREDICT · tipo de dado vector · / ou \ em nomes de schema/tabela.

O SQL analytics endpoint (de um Lakehouse ou mirrored database) é somente leitura: sem DDL, sem INSERT/UPDATE/DELETE. Ele suporta views, funções, stored procedures, RLS, CLS e DDM.

Lakehouses com schema habilitado

  • Schemas agrupam tabelas por domínio e vêm ligados por padrão em lakehouses novos. Todo lakehouse com schema tem um schema dbo que não pode ser renomeado nem removido. Nomes de schema aceitam apenas letras, dígitos e underscores.
  • Grave em um deles com df.write.mode("overwrite").saveAsTable("marketing.campaigns"). Sem o prefixo de schema, a tabela vai para dbo.
  • Schema shortcuts mapeiam um schema inteiro para o schema de outro lakehouse ou uma pasta do ADLS Gen2.
  • Spark SQL entre workspaces usa o nome em quatro partes workspace.lakehouse.schema.table (três partes para um lakehouse sem schema).
  • Limitação: lakehouses com schema habilitado não podem ser compartilhados pelo compartilhamento de workspace — exponha-os por shortcuts em um lakehouse que o usuário já alcança.

Objetivo 2.3Ingerir e transformar dados de streaming

A pilha do Real-Time Intelligence

Descobrir
Real-Time hubCatálogo de todos os streams e fontes de evento do tenant.
Mover
EventstreamIngestão, transformação e roteamento sem código.
Armazenar
Eventhouse → KQL DBStore de séries temporais, consultado com KQL.
Analisar
KQL queryset · DashboardExploração e visualização.
Agir
ActivatorRegras, alertas e disparo de itens do Fabric.

Escolhendo um engine de streaming

EventstreamSpark Structured StreamingKQL update policyDataflow Gen2
CódigoCanvas sem código (SQL operator em Preview)PySpark / ScalaKQLPower Query M
LatênciaSegundosSegundos a minutos (micro-batch)No momento da ingestãoMinutos (lote)
Riqueza de transformaçãoBaixa — filter, fields, aggregate, join, union, expandIlimitadaMédia — qualquer KQL sobre o extent que chegaAlta, mas em lote
PersonaIntegrador, analistaData engineerDesenvolvedor KQLAnalista
Escolha quandoRotear muitas fontes para muitos destinos com transformação leveJoins complexos, scoring de ML, estado customizadoRemodelar dados enquanto chegam num EventhouseO requisito não é realmente streaming

Eventstream

Fontes

Azure

Event Hubs · IoT Hub · Event Grid · Service Bus · Blob Storage events · Azure Data Explorer Preview · IoT Operations

CDC

Azure SQL DB · Azure SQL MI · SQL Server em VM · PostgreSQL · MySQL · Cosmos DB · Oracle Preview · MongoDB Preview · Mirrored database change feed Preview

Mensageria

Apache Kafka · Confluent Cloud · Amazon MSK · Amazon Kinesis · Google Cloud Pub/Sub · MQTT Preview · Solace PubSub+ Preview

Nativas do Fabric

Workspace item events · OneLake events · Job events · Capacity events · Anomaly detection events Preview

Customizadas

Custom endpoint / custom app (connection string no protocolo Kafka) · HTTP Preview

Dados de exemplo

Bicycles · Yellow Taxi · Stock market · Buses · Clima em tempo real — para demos e laboratórios de estudo

Operadores de transformação

OperadorO que faz
FilterMantém eventos que atendem a uma condição (checagens de null, comparações, conforme o tipo do campo)
Manage fieldsAdiciona, remove e renomeia campos; muda tipos de dados
AggregateSoma / mínimo / máximo / média numa janela de tempo
Group byAgregações sobre eventos numa janela de tempo, agrupadas por um ou mais campos, com todos os tipos de janela
UnionCombina dois ou mais streams com nomes e tipos de campo iguais; campos que não batem são descartados
ExpandUma linha por elemento de um array
JoinCombina dois streams por uma condição de correspondência
SQL operator PreviewSQL code-first para janelamento, joins e agregações avançadas

Destinos

  • Eventhouse — dois modos: direct ingestion (caminho mais rápido, eventos brutos direto para uma tabela KQL) ou event processing before ingestion (aplica os operadores antes).
  • Lakehouse — grava em Delta. Formato de entrada JSON, Avro ou CSV. Ajuste Minimum rows (1 – 2.000.000) e Maximum duration (1 minuto – 2 horas): menos linhas ou duração menor ⇒ mais arquivos pequenos.
  • Derived stream — o próprio stream transformado, republicado para que vários destinos (e o Real-Time hub) o consumam. Suporta pause/resume.
  • Activator — para regras e alertas.
  • Custom endpoint — aplicações externas leem pelo protocolo Kafka.
  • Spark notebook Preview — entrega os eventos a um job de Structured Streaming.
Limites do Eventstream e a armadilha do schema no Lakehouse
  • Tamanho máximo de mensagem 1 MB; retenção máxima 90 dias; garantia de entrega at least once; capacity recomendada F4 ou maior.
  • O destino Lakehouse aplica schema enforcement com base no primeiro registro. Colunas extras em eventos posteriores são descartadas, colunas ausentes viram NULL e um registro sem nenhuma interseção falha na conversão de schema. Não aponte uma fonte com schema variável (como CDC de banco) direto para um destino Lakehouse — use um Eventhouse, ou DeltaFlow.
  • DeltaFlow Preview achata o JSON aninhado do Debezium (CDC) num schema tabular, registra-o no schema registry do Fabric, cria as tabelas de destino automaticamente e trata a evolução de schema.

Eventhouse, KQL databases e OneLake

  • Um Eventhouse é um container que abriga uma ou mais KQL databases que compartilham sua capacity e seus recursos. Cada database ganha um KQL queryset embutido.
  • Por padrão o serviço é suspenso quando fica ocioso e reativa em poucos segundos. O capacity planner permite definir um cronograma semanal recorrente em blocos de 60 minutos com um mínimo de CU por bloco (mínimo padrão de 2 CU) mais autoscale acima disso — é a resposta quando a questão diz "as consultas nunca podem pagar penalidade de cold start no horário comercial".
  • Os dados são indexados e particionados por hora de chegada, e é por isso que KQL filtrado por tempo é tão rápido.

OneLake availability — "uma cópia lógica"

Habilite no nível de database ou de tabela (com backfill opcional das tabelas existentes) e os dados do KQL também são materializados como Delta no OneLake, legíveis por Spark, SQL endpoint, Warehouse, Lakehouse e Direct Lake — sem custo extra de armazenamento. A política de retenção do database também governa a cópia no OneLake.

KQL · ajustando e monitorando a mirroring policy
// Padrão: agrupa até arquivos Parquet de ~200–256 MB ou até 3 horas. Faixa: 5 min – 3 h.
.alter-merge table Telemetry policy mirroring dataformat=parquet
    with (IsEnabled=true, TargetLatencyInMinutes=5)

// Latência 00:00:00 significa que tudo já está no OneLake
.show table mirroring operations
O que o OneLake availability proíbe

Enquanto estiver habilitado você não pode renomear tabelas, mudar o tipo de uma coluna, aplicar row-level security, nem excluir / truncar / purgar dados. Desabilite, faça a alteração e reabilite. E reduzir o TargetLatencyInMinutes cria muitos arquivos pequenos e degrada a leitura — a resposta errada clássica para "as consultas ficaram mais lentas depois que reduzimos a latência".

Tabelas nativas vs. OneLake shortcuts vs. query acceleration

Tabela KQL nativaOneLake shortcutShortcut + query acceleration
Onde o dado ficaDentro do EventhouseExternamente, referenciadoExternamente, com uma janela quente em cache
PerformanceMelhorMenorQuase nativa para dados recentes
DuplicaçãoSimNenhumaApenas o cache
Escolha quandoO dado é consultado e atualizado constantementeAcesso ocasional ou ad hoc a Delta externoConsultas frequentes sobre dados Delta externos recentes

O query acceleration mantém em cache os dados do shortcut dentro de uma janela configurável em dias (herdada do database pai por padrão), com base no modificationTime do log Delta. Habilite na criação do shortcut (botão Accelerate) ou depois via Manage → Data policies → Query acceleration. Funciona apenas com tabelas Delta, exige workspace Admin / Member / Contributor e, por conformidade, convém manter o Eventhouse na mesma região dos dados.

Processando dados com KQL

KQL · referência de operadores
// ---- filtragem e formato ----
| where Level == "Error" and Timestamp between (ago(1d) .. now())
| where Message has "timeout"        // 'has' = correspondência de termo indexado, RÁPIDO
| where Message contains "time"     // varredura de substring, LENTO — saiba a diferença
| take 10  /  | limit 10            // linhas arbitrárias, sem garantia de ordem
| top 10 by Duration desc          // ordenado
| project Timestamp, DeviceId, Duration
| project-away RawPayload
| project-rename ts = Timestamp
| extend Minutes = Duration / 1m
| distinct DeviceId
| sort by Timestamp desc

// ---- agregação ----
| summarize count(), dcount(DeviceId), sum(Bytes), avg(Latency),
            min(Timestamp), max(Timestamp),
            percentile(Latency, 95), percentiles(Latency, 50, 90, 99),
            make_list(EventId), make_set(Region),
            arg_max(Timestamp, *),        // a linha mais recente inteira por grupo
            arg_min(Timestamp, Status),
            take_any(*)
          by Region, bin(Timestamp, 5m)

// ---- séries temporais ----
| make-series Total = sum(Bytes) default=0
    on Timestamp from ago(7d) to now() step 1h by DeviceId
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total)
| render timechart

// ---- joins ----
Devices
| join kind=leftouter (Telemetry | summarize LastSeen = max(Timestamp) by DeviceId)
     on DeviceId
| join kind=inner hint.strategy=broadcast (SmallLookup) on $left.Id == $right.Key
| lookup (DimDevice) on DeviceId        // left-outer otimizado contra uma dimensão pequena
| union withsource=SourceTable Errors, Warnings

// ---- semiestruturado ----
| extend p = parse_json(Payload)
| extend City = tostring(p.location.city), Temp = todouble(p.temp)
| mv-expand tag = p.tags to typeof(string)
| parse Message with "user=" User " action=" Action

// ---- utilidades ----
let threshold = 500;
let hot = materialize(Telemetry | where Timestamp > ago(1h));   // cacheia uma subconsulta reutilizada
let demo = datatable(Id:int, Name:string) [1, "a", 2, "b"];
| extend Ingested = ingestion_time()      // quando o Kusto recebeu, vs. hora do evento
| serialize | extend Delta = Value - prev(Value, 1)

Tipos de join — a tabela completa

TipoRetornaColunas de saída
innerunique (padrão!)Linhas da esquerda deduplicadas pela chave de join, casadas com a direitaOs dois lados
innerInner join padrão, sem dedupOs dois lados
leftouter / rightouterTodas as linhas daquele lado, com nulls onde não houver correspondênciaOs dois lados
fullouterTodas as linhas dos dois ladosOs dois lados
leftsemi / rightsemiLinhas daquele lado que têm correspondênciaSomente aquele lado
leftanti / rightantiLinhas daquele lado que não têm correspondênciaSomente aquele lado
Dois fatos de KQL que custam pontos

1. O tipo de join padrão é innerunique, não inner. Ele deduplica silenciosamente o lado esquerdo, então as contagens saem menores do que o esperado. Sempre declare kind=inner quando quiser um inner join de verdade.
2. Coloque a tabela menor à esquerda do join para melhor desempenho — o oposto do hábito em SQL.

Update policies e materialized views

KQL · update policy (transformar na ingestão)
.create table RawLogs (OriginalRecord:string)
.create table ParsedLogs (Timestamp:datetime, ThreadId:int, Message:string)

.create function ExtractLogs() {
    RawLogs
    | parse OriginalRecord with "[" Timestamp:datetime "] [ThreadId:" ThreadId:int "] " Message:string
    | project-away OriginalRecord
}

.alter table ParsedLogs policy update
@'[{ "IsEnabled": true,
    "Source": "RawLogs",
    "Query": "ExtractLogs()",
    "IsTransactional": true,
    "PropagateIngestionProperties": false }]'

// Descartar a cópia bruta depois de transformada
.alter-merge table RawLogs policy retention softdelete = 0s
PropriedadeSignificado
IsEnabledLiga/desliga
SourceA tabela cuja ingestão dispara a política
SourceIsWildCardTrata Source como padrão (SourceTable*); a função então usa $source_table
QueryA transformação, geralmente uma função armazenada
IsTransactionaltrue ⇒ uma falha na política também faz a ingestão de origem falhar. Padrão false, o que significa que dados ruins ficam só na tabela de origem
PropagateIngestionPropertiesLeva tags de extent e hora de criação para a tabela de destino
ManagedIdentityObrigatório se a consulta ler tabelas de outro database

Update policies disparam em .ingest, .set, .append, .set-or-append, .set-or-replace, .move extents e .replace extents. Não podem fazer consultas cross-cluster, callouts, nem usar nomes qualificados database()/cluster(). Investigue falhas com .show ingestion failures | where OriginatesFromUpdatePolicy == true.

Update policy vs. materialized view: uma update policy transforma no momento da ingestão e grava numa segunda tabela (boa para parsing, divisão, filtragem). Uma materialized view (.create materialized-view) mantém uma agregação atualizada incrementalmente sobre uma tabela de origem (boa para rollups com summarize/arg_max que você consulta o tempo todo).

Spark Structured Streaming

PySpark · ler um stream e gravar numa tabela Delta
from pyspark.sql import functions as F
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, TimestampType

schema = StructType([
    StructField("deviceId", StringType(),   False),
    StructField("temp",     DoubleType(),   True),
    StructField("eventTime",TimestampType(),True)])

raw = (spark.readStream.format("eventhubs").options(**ehConf).load())

parsed = (raw
    .withColumn("body", F.col("body").cast("string"))
    .select(F.from_json("body", schema).alias("e"))
    .select("e.*"))

query = (parsed
    .repartition(48)                                # alinhar aos cores disponíveis
    .writeStream
    .format("delta")
    .option("checkpointLocation", "Files/checkpoints/telemetry")
    .outputMode("append")
    .partitionBy("deviceId")
    .trigger(processingTime="1 minute")
    .toTable("silver.telemetry"))
PySpark · watermarks, janelas e upserts
# Watermark = quanto tempo esperar por eventos atrasados antes de descartá-los e fechar o estado
windowed = (parsed
    .withWatermark("eventTime", "10 minutes")
    .groupBy(F.window("eventTime", "5 minutes"), "deviceId")   # TUMBLING
    .agg(F.avg("temp").alias("AvgTemp"), F.count("*").alias("Readings")))

# HOPPING / deslizante: windowDuration e depois slideDuration
F.window("eventTime", "10 minutes", "5 minutes")

# SESSION: timeout de intervalo
F.session_window("eventTime", "5 minutes")

# Upsert numa tabela Delta a partir de um stream
def upsert(batch_df, batch_id):
    batch_df.createOrReplaceTempView("updates")
    batch_df.sparkSession.sql("""
        MERGE INTO silver.telemetry AS t
        USING updates AS u ON t.deviceId = u.deviceId AND t.eventTime = u.eventTime
        WHEN MATCHED THEN UPDATE SET *
        WHEN NOT MATCHED THEN INSERT *""")

(parsed.writeStream
    .foreachBatch(upsert)
    .option("checkpointLocation", "Files/checkpoints/upsert")
    .trigger(availableNow=True)      # processa tudo o que há disponível e para
    .start())
ConfiguraçãoOpçõesObservações
Output modeappend · update · completeappend para destinos Delta. complete reescreve todo o resultado a cada lote — só para agregações.
TriggerprocessingTime="1 minute" · availableNow=True · once=True · contínuoavailableNow é o trigger moderno de "processe o acumulado e pare" — ideal para pipelines micro-batch agendados.
CheckpointcheckpointLocationObrigatório. Guarda offsets e estado; apagá-lo reprocessa tudo do zero. Um checkpoint por query.
Optimize writespark.databricks.delta.optimizeWrite.enabled = TrueMescla/divide partições na escrita para você não precisar fazer repartition() na mão.

Para streaming em produção use um Spark job definition com política de retry, e não um notebook, e monitore na aba Structured Streaming do monitoring hub (Input rate, Process rate, Input rows, Batch duration, Operation duration).

Windowing functions — uma tabela, três engines

JanelaComportamentoSobreposiçãoEventstream / SQLSparkKQL
TumblingSegmentos fixos, contíguos, sem sobreposiçãoNãoTumblingWindow(second, 10)window("ts","10 seconds")summarize … by bin(ts, 10s)
HoppingTamanho fixo, avançando por um salto; um evento pode cair em várias janelasSimHoppingWindow(second, 10, 5)window("ts","10 seconds","5 seconds")range + mv-expand, ou make-series
SlidingEmite apenas quando o conteúdo da janela muda (um evento entra ou sai)SimSlidingWindow(second, 10)Aproximada com um salto pequenoseries_fir() sobre uma série
SessionCresce enquanto eventos continuam chegando; fecha após um intervalo de timeout ou uma duração máximaNãoSessionWindow(second, 5, 10)session_window("ts","5 minutes")Operador scan
SnapshotAgrupa eventos com exatamente o mesmo timestampNãoGROUP BY System.Timestamp()groupBy("ts")summarize … by ts
Três regras que respondem à maioria das questões de janelamento
  • Toda janela emite seu resultado no fim da janela.
  • Uma hopping window cujo salto é igual ao tamanho da janela é uma tumbling window.
  • "Reportar a média a cada minuto sobre os últimos cinco minutos" = hopping (tamanho 5 min, salto 1 min). "Reportar a média de cada bloco de cinco minutos" = tumbling.

Activator

  • Objects são formados agrupando eventos por uma object key (ID do dispositivo, ID da conta). As regras então são avaliadas por instância de objeto.
  • Regras stateless julgam cada evento isoladamente e disparam em frações de segundo. Regras stateful mantêm memória por objeto: BECOMES, INCREASES/DECREASES, EXIT RANGE, heartbeat (ausência de dados) e agregações sobre uma janela de lookback.
  • As regras disparam na entrada em um novo estado, e é isso que suprime alertas repetidos.
  • Fontes: Eventstream, Fabric workspace item events, Azure Blob events, Real-Time dashboards, relatórios do Power BI (observações periódicas atreladas ao agendamento de refresh), regras de consulta SQL sobre um Warehouse Preview.
  • Ações: e-mail, mensagem no Teams, fluxo do Power Automate — e itens do Fabric: pipeline, notebook, Spark job definition, Dataflow, Copy job, user data function.
Domínio 3 · 30–35% do exame

Monitorar e otimizar uma solução de análise

O domínio para o qual os candidatos menos se preparam, e que vale exatamente o mesmo que os outros dois. Três grupos de objetivos: monitorar itens do Fabric, identificar e resolver erros, e otimizar performance em seis engines diferentes.

Objetivo 3.1Monitorar itens do Fabric

As superfícies de monitoramento, e qual usar

SuperfícieEscopoUse para
Monitoring hubTodos os itens a que você tem acesso, no tenant inteiro"O que rodou, quando, e deu certo?" Um único lugar para pipelines, notebooks, Dataflows, Spark jobs, Copy jobs, modelos semânticos, manutenção de Lakehouse e mais
Recent runs do itemUm itemHistórico de execuções só daquele item
Capacity Metrics appUma capacityConsumo de CU, bursting, overages, throttling, e qual operação os causou
Workspace monitoringUm workspaceLogs de diagnóstico e métricas detalhadas num Eventhouse consultável (30 dias)
Admin monitoring workspaceTenantFeature usage and adoption, atividade no nível do tenant
Purview / unified audit logTenantQuem fez o quê — auditoria de compliance e segurança

Detalhes do monitoring hub: mostra as 100 atividades mais recentes por tipo de item nos últimos 30 dias. Filtre por status, tipo de item, hora de início, quem submeteu e localização; pesquise por nome; ordene e reorganize colunas. Em cada linha você pode abrir um painel de detalhes (status, hora de início, duração, detalhe do erro), abrir Historical runs para o histórico completo de 30 dias daquele item, e configurar notificações de falha de agendamento Preview (exige Contributor ou Write no item). Dataflow Gen1 não aparece.

Monitorar a ingestão de dados

  • Pipelines — histórico de execuções mais as execuções por atividade. A saída da Copy activity traz rowsRead, rowsCopied, rowsSkipped, throughput, dataConsistencyVerification, e a duração dividida em tempo de fila e de transferência.
  • Copy job — tem seu próprio dashboard em tempo real: status e progresso por tabela, histórico de execuções e alertas de falha; também aparece no workspace monitoring.
  • Eventstream — status dos nós, métricas de throughput e métricas de erro na live view e no workspace monitoring.
  • Ingestão do Eventhouse.show ingestion failures é o comando mais importante; a página System overview do Eventhouse mostra taxa de ingestão, principais databases ingeridos, armazenamento, uso de computação e mudanças de schema.
  • Mirroring — status de replicação e contagem de linhas por tabela na página de monitoramento do mirrored database; uma capacity pausada interrompe a replicação.

Monitorar a transformação de dados

  • Spark — cinco pontos de entrada: o monitoring hub, o Recent runs do item, o monitoramento contextual dentro do notebook (progresso do job por célula, tasks, executores, logs), o monitoramento inline do Spark job definition e os deep links da atividade Spark no pipeline. Por trás deles estão o Spark Advisor (dicas de código e análise de erro em tempo real), o Apache Spark History Server estendido e os snapshots de notebook, que capturam o código e a saída exatos de uma execução.
  • Dataflow Gen2 — histórico de refresh com duração e performance por query, integrado ao monitoring hub.
  • Warehouse — as views de Query Insights mais as DMVs ao vivo:
T-SQL · Query Insights e DMVs
-- Retido por 30 dias; até ~15 minutos de latência; somente consultas de usuário
SELECT TOP 100 distributed_statement_id, query_hash, allocated_cpu_time_ms, label, command
FROM   queryinsights.exec_requests_history
ORDER BY allocated_cpu_time_ms DESC;

-- Detecção de cold start: leitura remota diferente de zero significa que NÃO estava em cache
SELECT distributed_statement_id, query_hash,
       data_scanned_remote_storage_mb, data_scanned_memory_mb, data_scanned_disk_mb, command
FROM   queryinsights.exec_requests_history
ORDER BY data_scanned_remote_storage_mb DESC;

SELECT * FROM queryinsights.long_running_queries    ORDER BY median_total_elapsed_time_ms DESC;
SELECT * FROM queryinsights.frequently_run_queries  ORDER BY number_of_successful_runs   DESC;
SELECT * FROM queryinsights.exec_sessions_history;
SELECT * FROM queryinsights.sql_pool_insights;       -- alocação de recursos e pressão no pool

-- Estado ao vivo (agora, não histórico)
SELECT * FROM sys.dm_exec_requests;
SELECT * FROM sys.dm_exec_sessions;
SELECT * FROM sys.dm_exec_connections;
Marque suas consultas com OPTION (LABEL)

Adicione OPTION (LABEL = 'carga_noturna_fato') aos comandos do seu ETL. O label chega ao queryinsights, então você consegue filtrar o histórico por uma etapa específica do pipeline. Consultas com o mesmo formato (mesma estrutura, predicados diferentes) são agregadas juntas nas views de insight.

Monitorar o refresh de modelos semânticos

  • Modelos Import e DirectQuery: histórico de refresh no modelo, mais uma atividade Semantic model refresh que você pode encadear no fim de um pipeline.
  • Modelos Direct Lake não fazem "refresh" de dados — eles fazem framing (uma operação só de metadados, de segundos, que aponta o modelo para os arquivos Delta mais recentes) e carregam segmentos de coluna na memória sob demanda (transcoding). O que você monitora é o sucesso do framing e se as consultas caíram para DirectQuery.

Monitoramento de capacity e alertas

  • Capacity Metrics app — a página Compute mostra CU segundos por operação, separados em interactive e background; o detalhe de timepoint entra num único intervalo de 30 segundos para nomear a operação culpada; a aba Overages plota carryforward, uso cumulativo e burndown; a tabela de system events registra episódios de throttling; Minutes to burndown estima a recuperação.
  • Alertas — regras do Activator sobre um Eventstream, KQL queryset ou Real-Time dashboard; alertas sobre um visual do Power BI; notificação de falha de pipeline via atividade do Outlook ou Teams, ou as notificações de falha de agendamento nativas; notificações de capacity configuradas pelo capacity admin.

Objetivo 3.2Identificar e resolver erros

Trate esta seção como uma consulta sintoma → causa → correção. O exame formula assim: "um job falha com X — o que você deve fazer primeiro?"

Erros de pipeline

SintomaCausa provávelCorreção
Atividade falha com um código de erro de conectorCredenciais expiradas, firewall, caminho erradoLeia ErrorCode, Message e failureType na saída da atividade; teste a conexão; verifique o status do gateway
Atividades seguintes rodam mesmo com uma anterior falhandoSeta de dependência definida como On completion em vez de On successCorrija a condição de dependência
O pipeline "tem sucesso" mas nada foi carregadoTodas as atividades em caminhos On skip/On completion, ou um ForEach sobre um array vazioAdicione uma checagem de contagem com Lookup mais uma atividade Fail para tornar o vazio um erro explícito
Falhas transitórias de redeSem retry configuradoDefina Retry e Retry interval na aba General da atividade
Atividade longa que travaTimeout padrão generoso demaisDefina um Timeout explícito
Mensagem de erro não capturadaNo caminho de falha, registre @activity('Copy1').error.message e @pipeline().RunId numa tabela

Erros do Dataflow Gen2

  • Comece pelo Refresh history → o refresh que falhou → o detalhe de erro por query.
  • Falhas de staging — os itens internos DataflowsStagingLakehouse / DataflowsStagingWarehouse. Se o staging está falhando, verifique throttling de capacity antes de qualquer outra coisa.
  • Erros de data destination — incompatibilidade de schema entre a saída da query e a tabela de destino existente, ou um tipo que o destino não aceita. Verifique a configuração de fixed vs. dynamic schema do destino.
  • Erros de gateway — gateway on-premises offline, desatualizado ou sem o driver daquela fonte.
  • Erros de avaliação — um passo M falhando com os dados reais (nulls, tipos inesperados). Corrija com try … otherwise ou um passo explícito de conversão de tipo.
  • Query folding quebrado — um passo que não faz folding puxa tudo para o mashup engine e o refresh fica lentíssimo. Clique com o botão direito num passo → View native query; mova os passos que não dobram para o final.

Erros de notebook e Spark

ErroSignificadoCorreção
HTTP 430 TooManyRequestsForCapacityNão há mais Spark vCores disponíveis na capacity (incluindo burst)Cancele um job ativo no monitoring hub, espere a fila, use um pool menor ou aumente o SKU. Jobs interativos são rejeitados; jobs de background entram na fila
Sessão não inicia / erro de LivyCapacity esgotada, ou um publish de environment que falhouVerifique a capacity; republique o environment; verifique o provisionamento de VNet do Private Link (10–15 min no primeiro job)
OOM de executor / Java heap spaceJoin com skew, collect() gigante, poucas partiçõesNó maior, broadcast do lado pequeno, salting da chave com skew, aumentar spark.sql.shuffle.partitions, nunca fazer collect() de um DataFrame grande
Py4JJavaErrorO wrapper Python expondo uma exceção da JVMIgnore o traceback do Python e vá até a linha Caused by do Java
ModuleNotFoundError depois do publishBiblioteca instalada no escopo da sessão, não no EnvironmentAdicione à lista de bibliotecas do Environment e publique; use Full mode para jobs agendados
Escrita concorrente / ConcurrentAppendExceptionDois jobs gravando na mesma tabela DeltaParticione as escritas com replaceWhere, serialize-as, ou faça retry

Erros de Eventhouse e Eventstream

  • .show ingestion failures — o primeiro comando de qualquer investigação em Eventhouse. Motivos comuns: schema incompatível, um ingestion mapping ausente ou errado, JSON/CSV malformado e throttling.
  • Acrescente | where OriginatesFromUpdatePolicy == true para isolar falhas causadas por uma update policy.
  • Se uma update policy transacional está falhando, a ingestão de origem também falha — defina IsTransactional = false se sucesso parcial for aceitável.
  • Eventstream: conectividade da fonte (credenciais, firewall, consumer group já em uso), falhas de escrita no destino e schema drift contra o schema do primeiro registro do destino Lakehouse. Verifique status dos nós e métricas de erro na live view.

Erros de T-SQL e do SQL analytics endpoint

  • T-SQL não suportado — triggers, materialized views, synonyms, CTEs recursivas, SET TRANSACTION ISOLATION LEVEL. É aqui que código migrado quebra primeiro.
  • Uma tabela nova do Lakehouse não aparece no SQL endpoint — a sincronização de metadados do endpoint é assíncrona. Atualize os metadados do endpoint (ou use a atividade de pipeline Refresh SQL endpoint depois da etapa de carga). Este é um cenário de prova bastante comum.
  • Aviso de non-scalable operation — um TOP/ORDER BY global forçou execução em nó único. Adicione OPTION (FORCE DISTRIBUTED PLAN) ou reestruture.
  • Erros de lock / conflito — transações explícitas abertas por muito tempo. Mantenha as transações curtas e orientadas a lote; faça retry com backoff exponencial.
  • Violações de constraint que nunca acontecem — lembre que as constraints são NOT ENFORCED; duplicatas são culpa do seu ETL, não do engine.

Erros de OneLake shortcut

  • Credenciais expiradas ou revogadas na cloud connection — a causa mais frequente de um shortcut que "funcionava ontem". Recrie ou atualize a conexão.
  • Erros de permissão em shortcuts internos — a autorização usa a identidade de quem chama; a pessoa precisa de permissão de leitura no item de destino, não apenas no shortcut.
  • Direct Lake sobre SQL, ou T-SQL em modo delegated identity, passa a identidade do dono do item chamador em vez da do usuário. Mude para Direct Lake sobre OneLake, ou T-SQL em modo user identity.
  • Tabela não reconhecida — um shortcut em Tables/ que não é uma tabela Delta válida, ou tem espaço no nome, ou está num subdiretório.
  • Limite de encadeamento — mais de 5 níveis de shortcut apontando para shortcut.
  • Latência entre regiões — reduza com o cache de shortcuts (retenção de 1 a 28 dias; arquivos acima de 1 GB não são cacheados).
  • ADLS Gen2 atrás de firewall — precisa de workspace identity mais trusted workspace access, ou um managed private endpoint.

Objetivo 3.3Otimizar performance

Otimizar uma tabela de Lakehouse

Quase todo problema de performance em Lakehouse é o problema dos arquivos pequenos: escritas de streaming ou micro-batch frequentes produzem milhares de arquivos Parquet minúsculos, e o overhead de metadados domina a leitura.

OperaçãoO que fazObservações
OPTIMIZEBin-compaction: mescla arquivos Parquet pequenos em arquivos maioresRode depois de ingestão pesada ou de muitos updates
V-OrderOrdenação, encoding e compressão do layout Parquet no momento da escritaEscritas ~15% mais lentas, até 50% mais compressão e leituras muito mais rápidas. Continua 100% compatível com o Parquet open source
VACUUMExclui arquivos não referenciados mais antigos que o limite de retençãoRetenção padrão de 7 dias. Intervalos menores são recusados a menos que spark.databricks.delta.retentionDurationCheck.enabled = false; encurtar destrói o histórico de time travel e pode quebrar leitores concorrentes
ZORDER BYColoca valores relacionados juntos para que o file skipping funcione em colunas de filtro de alta cardinalidadeCombine com particionamento numa coluna de baixa cardinalidade
Merge transactions / limpeza de deletion vectorsReincorpora os arquivos de deletion vector aos dados ParquetDisponível no diálogo de manutenção
Manutenção Delta e controle do V-Order
-- Manutenção de tabela via Spark SQL
OPTIMIZE silver.orders ZORDER BY (CustomerId, OrderDate);
VACUUM   silver.orders RETAIN 168 HOURS;   -- 168 h = o padrão de 7 dias
DESCRIBE HISTORY silver.orders;

-- V-Order, três níveis de controle
SET spark.sql.parquet.vorder.default;                 -- inspecionar (padrão da sessão: false)
SET spark.sql.parquet.vorder.default = TRUE;          -- sessão
ALTER TABLE person SET TBLPROPERTIES("delta.parquet.vorder.enabled" = "true");  -- tabela
PySpark · V-Order por escrita e ajuste de gravação
(df.write.format("delta").mode("overwrite")
   .option("replaceWhere", "start_date >= '2026-01-01' AND end_date <= '2026-01-31'")
   .option("parquet.vorder.enabled", "true")
   .saveAsTable("myschema.mytable"))

spark.conf.set("spark.databricks.delta.optimizeWrite.enabled", True)   # dimensiona arquivos na escrita
spark.conf.set("spark.databricks.delta.stats.collect", True)          # estatísticas para file skipping
Os padrões de V-Order diferem por workload

Warehouse: o V-Order é aplicado automaticamente ao Parquet que ele produz, qualquer que seja o método de ingestão. Desabilite apenas num warehouse puramente de escrita intensiva — e note que ele não pode ser reabilitado depois de desligado no nível do warehouse.
Spark / Lakehouse: o V-Order vem desligado por padrão em workspaces novos, para manter as escritas rápidas. Ligue-o para tabelas gold de leitura intensiva, ou use um resource profile de leitura. O OPTIMIZE o aplica como parte da manutenção.

Orientação de particionamento: particione apenas tabelas grandes o suficiente para justificar, numa coluna de baixa cardinalidade (ano, mês, região), mirando partições na faixa de centenas de MB a alguns GB. Particionar demais recria o problema dos arquivos pequenos. Use ZORDER, não particionamento, para colunas de alta seletividade. Rode a manutenção ad hoc pelo Lakehouse explorer (clique com o botão direito na tabela → Maintenance), ou agende-a com a atividade de pipeline Lakehouse maintenance Preview. Acompanhe no monitoring hub pelos nomes de atividade contendo TableMaintenance. OPTIMIZE e VACUUM valem apenas para tabelas Delta — não para Hive Parquet, ORC, AVRO ou CSV.

Otimizar um pipeline

  • Aumente o degree of copy parallelism da Copy activity; habilite staging quando o caminho direto for lento.
  • Num ForEach, desligue Sequential e defina o Batch count (máx. 50) para controlar a concorrência.
  • Empurre a filtragem para a origem (uma query em vez de ler a tabela inteira) para trafegar menos dados.
  • Prefira o Copy job a lógica incremental artesanal quando o padrão for convencional — ele particiona e paraleliza automaticamente.
  • Nunca itere linha a linha. Substitua um ForEach interno que insere uma linha por vez por uma cópia em lote ou uma stored procedure.
  • Adeque a computação ao trabalho — não suba uma sessão Spark para mover um arquivo.

Otimizar um data warehouse

AlavancaOrientação
EstatísticasO Fabric mantém automaticamente estatísticas de histograma, comprimento médio de coluna e cardinalidade. Você ainda pode fazer CREATE STATISTICS / UPDATE STATISTICS manualmente numa janela de manutenção. Estatísticas multi-coluna não podem ser criadas manualmente.
Tipos de dadosPrefira smallint/int/bigint a decimal para números inteiros. Nunca escreva decimal puro (vira decimal(18,0), 9 bytes por linha). Dimensione varchar(n) pelo dado — evite varchar(8000) e varchar(max). Use date/time/datetime2, nunca strings para datas. Declare NOT NULL onde puder.
Paridade de tiposMantenha tipos idênticos nos dois lados de comparações em JOIN e WHERE, para evitar conversões implícitas.
Cold startA primeira execução paga por carregar dados do OneLake para a memória mais as estatísticas automáticas. data_scanned_remote_storage_mb = 0 significa totalmente em cache — o estado ideal.
IngestãoCOPY INTO em paralelo; arquivos ≥ 100 MB (nunca abaixo de 4 MB); transações em lote; evite inserts unitários em gotejamento.
Formato da consultaProjete apenas as colunas necessárias; filtre cedo; evite SELECT *; prefira TRUNCATE+CTAS a UPDATE/DELETE grandes.
TransaçõesSnapshot isolation, ACID. Mantenha-as curtas — o rollback é barato (reversão de versão Parquet), mas transações longas seguram locks. Monitore sys.dm_tran_locks.
Controle de planoOPTION (FORCE DISTRIBUTED PLAN) quando aparecer o aviso de non-scalable operation.
ModeloUm star schema tem desempenho melhor que uma tabela larga desnormalizada para cargas de BI.

Otimizar Eventstreams e Eventhouses

  • Batching vs. streaming ingestion policy — o batching (padrão) troca um pouco de latência por arquivos bem maiores e desempenho de consulta muito melhor; a streaming ingestion dá visibilidade em menos de um segundo, com custo maior. Escolha por tabela.
  • Caching (hot) policy — a quantidade de dados recentes mantida no SSD do cluster. Alargar a janela quente acelera as consultas e custa mais; estreitar faz o inverso. Ajuste para cobrir o período que as pessoas realmente consultam.
  • Retention policy — por quanto tempo o dado sobrevive. A retenção precisa ser ≥ a janela quente para fazer sentido.
  • Update policies para remodelar na ingestão; materialized views para agregações mantidas incrementalmente que as consultas acessam o tempo todo.
  • Partitioning policy — só para padrões específicos de alta cardinalidade ou hora de ingestão embaralhada; não é o padrão.
  • Eventstream — dimensione a capacity (F4+), mantenha Minimum rows e Maximum duration do destino Lakehouse altos o bastante para evitar arquivos pequenos, e rode Optimize table in notebook nos destinos Delta de streaming.
  • Não baixe o TargetLatencyInMinutes da mirroring policy abaixo do necessário — arquivos pequenos prejudicam todos os leitores downstream.

Otimizar performance do Spark

Aceleradores específicos do Fabric

  • Native Execution Engine — um engine vetorizado em C++ que executa operações Spark nativamente; ganhos grandes sem mudar código.
  • Intelligent Cache — cache automático, local ao nó, de arquivos Delta/Parquet/CSV lidos com frequência.
  • Autotune — ajuste por consulta, guiado por ML, de shuffle partitions, limiares de broadcast e preferências de join.
  • Adaptive Query Execution — replaneja em tempo de execução usando estatísticas reais.

Sessão e concorrência

  • Starter pools para início em 5–10 s; custom live pools quando você precisa de bibliotecas pré-instaladas e ainda quer ~5 s.
  • High concurrency mode para compartilhar uma sessão entre notebooks (e entre atividades de notebook num pipeline).
  • Dynamic executor allocation + autoscale em vez de um pool fixo superdimensionado.
  • Bursting (3×) vem ligado; um capacity admin pode desabilitar o bursting em nível de job para que um único job não monopolize a capacity.

Ajuste no nível da consulta

  • Faça broadcast do lado pequeno de um join (F.broadcast(df)).
  • Trate skew com salting da chave quente ou deixe o AQE dividi-la.
  • Ajuste spark.sql.shuffle.partitions ao volume de dados, não ao padrão.
  • cache()/persist() num DataFrame reutilizado várias vezes — e faça unpersist() depois.
  • Evite UDFs em Python; prefira funções nativas ou pandas_udf.
  • Nunca faça collect() de um DataFrame grande no driver.

Limites de concorrência (por cores)

1 CU = 2 Spark vCores; burst padrão 3×. Numa F64: 128 base → 384 com burst, limite de fila 64. Jobs de background (disparados por pipeline, pelo scheduler, Spark job definitions) entram numa fila FIFO e expiram em 24 horas; jobs interativos de notebook são rejeitados com HTTP 430, nunca enfileirados.

Otimizar performance de consulta (camada semântica)

Direct LakeImportDirectQuery
EngineVertiPaq, lendo Delta diretamenteVertiPaq, sobre uma cópiaDelegado à fonte
RefreshFraming — só metadados, segundosRefresh completo dos dados, minutos a horasNenhum
Latência dos dadosBaixaTão desatualizada quanto o último refreshAo vivo
LicenciamentoExige capacity do FabricQualquer licençaQualquer licença
  • Direct Lake sobre OneLake lê quaisquer tabelas Delta do Fabric, suporta composite models e colunas calculadas, aplica segurança na camada semântica e não tem fallback para DirectQuery.
  • Direct Lake sobre SQL lê tabelas e views de Lakehouse/Warehouse, respeita RLS de SQL — e cai para DirectQuery quando encontra uma view não materializada ou controle de acesso granular baseado em SQL. O fallback é controlado pela propriedade Direct Lake behavior. Fallback costuma ser o motivo de um relatório Direct Lake "ficar lento de repente".
  • Os guardrails escalam com o SKU — contagem de arquivos Parquet, de row groups, de linhas e memória. Ultrapassar a memória não é um bloqueio rígido; causa paging e degradação. F2–F8: 300 M linhas, 10 GB, 3 GB de memória. F64/P1: 1,5 B linhas, tamanho ilimitado, 25 GB de memória. F512/P4: 12 B linhas, 200 GB de memória.
  • A melhor otimização isolada para um modelo Direct Lake é ter tabelas Delta bem mantidas: V-Order aplicado, arquivos compactados, contagem de row groups abaixo do guardrail.
  • Não suportado em Direct Lake: tipos de coluna complexos (Binary, GUID), floats não numéricos, strings acima de 32.764 caracteres, hierarquias definidas pelo usuário em tabelas Direct Lake, gateways, workspaces pessoais e workspaces de origem em outra região.

Folhas de consulta

Números que vale decorar

ValorA que se aplica
1 CU = 2 Spark vCores; burst 3×Dimensionamento de Spark. F64 → 384 vCores
10 min / 60 min / 24 hEstágios de throttling: overage protection → interactive delay (20 s) → interactive rejection → background rejection
5–64 min · 24 hJanelas de smoothing: interativo · background
30 segundosUm timepoint de capacity (2.880 por dia)
7 diasRetenção padrão do VACUUM
30 diasRetenção do Query Insights · histórico do monitoring hub · retenção do workspace monitoring
1–28 diasRetenção do cache de OneLake shortcut (arquivos > 1 GB não são cacheados)
100.000 / 10 / 5Shortcuts por item / por caminho do OneLake / profundidade máxima de encadeamento
1 MB · 90 dias · at least onceEventstream: tamanho máximo da mensagem · retenção máxima · garantia de entrega
5 min – 3 hFaixa do TargetLatencyInMinutes do mirroring de Eventhouse para OneLake (mira arquivos de 200–256 MB)
1 – 2.000.000 linhas · 1 min – 2 hDestino Lakehouse do Eventstream: minimum rows · maximum duration
2–10 estágios (padrão 3)Deployment pipelines
20 agendamentos · batch count 50 no ForEachPor pipeline · limite de paralelismo do ForEach
1.000 / 1.000 / 10.000 / 1 MBVariable library: variáveis · value sets · células totais · tamanho do item
1 TB por CUArmazenamento gratuito de mirroring (F64 → 64 TB)
20 minutosExpiração padrão da sessão Spark (o pool é desalocado 2 min depois)
24 horasExpiração da fila de jobs Spark de background
4 MB / 100 MB – 1 GBIngestão no Warehouse: tamanho mínimo absoluto / tamanho recomendado de arquivo
700 / 1000Nota de aprovação

Fluxogramas de decisão em palavras

"Onde estes dados devem ficar?"

Streaming/telemetria/logs, pessoal de KQL → Eventhouse. DML completo em T-SQL e transações multi-tabela → Warehouse. Spark, não estruturado, ML → Lakehouse. Aplicação OLTP → SQL database no Fabric. Já existe em outro lake → shortcut. É um banco operacional que você quer continuamente → mirror.

"Como trago estes dados?"

Contínuo de um banco operacional suportado → Mirroring. Já está num lake → Shortcut. Bulk/incremental/CDC agendado, sem orquestração → Copy job. Precisa de orquestração ou muitas atividades → Pipeline + Copy activity. Analista com Power Query → Dataflow Gen2. Complexo/customizado → Notebook. Orientado a eventos → Eventstream.

"Por que está lento?"

Lakehouse → arquivos pequenos; rode OPTIMIZE, verifique V-Order e particionamento. Warehouse → cache frio, tipos de dados ruins, estatísticas desatualizadas, plano em nó único. Spark → skew, shuffle partitions, falta de broadcast, inicialização de sessão. Eventhouse → cache quente pequeno demais, sem materialized view, filtro por substring não indexada. Power BI → fallback do Direct Lake ou guardrail estourado.

"Quem pode ver o quê?"

Workspace inteiro → workspace role. Um item → item permission. Tabelas/pastas específicas para um Viewer → OneLake security role. Linhas específicas → RLS. Colunas específicas → CLS. Ocultar um valor da maioria dos usuários → dynamic data masking. Classificar e proteger na exportação → sensitivity label.

KQL vs. T-SQL vs. PySpark — a mesma operação, três dialetos

OperaçãoKQLT-SQLPySpark
Filtrar| where x > 5WHERE x > 5.filter(F.col("x") > 5)
Selecionar colunas| project a, bSELECT a, b.select("a","b")
Nova coluna| extend c = a + bSELECT a + b AS c.withColumn("c", F.col("a")+F.col("b"))
Agregar| summarize sum(x) by gGROUP BY g.groupBy("g").agg(F.sum("x"))
Top N| top 10 by x descSELECT TOP 10 … ORDER BY x DESC.orderBy(F.desc("x")).limit(10)
Contagem distintadcount(x)COUNT(DISTINCT x)F.countDistinct("x")
Linha mais recente por chavesummarize arg_max(ts, *) by kROW_NUMBER() … WHERE rn = 1row_number().over(w)
Bucket de tempobin(ts, 1h)DATETRUNC(hour, ts)F.window("ts","1 hour")
Expandir array| mv-expand tagsF.explode("tags")
Parsear JSONparse_json(col)JSON_VALUE()F.from_json(col, schema)
Upsert.set-or-appendMERGEDeltaTable.merge()

Um plano de estudos de quatro semanas

Semana 1
Fundamentos + Domínio 2ACapacity, OneLake, taxonomia de itens, o guia de decisão de stores. Depois padrões de carga e modelagem dimensional. Construa um lakehouse medallion na mão.
Semana 2
Domínio 2B + 2CShortcuts, mirroring, Copy job, transformações em PySpark/T-SQL. Depois Eventstream → Eventhouse de ponta a ponta, e um exercício de KQL por dia.
Semana 3
Domínio 1Spark pools e environments, Git + deployment pipelines + variable libraries, toda a pilha de segurança, padrões de orquestração e expressões.
Semana 4
Domínio 3 + revisãoSuperfícies de monitoramento, as tabelas de erro, todas as alavancas de otimização. Depois percorra de novo o skills outline oficial e marque tudo que você não consegue explicar em voz alta.

A lista de prontidão

Marque cada item somente quando conseguir explicá-lo para outra pessoa sem consultar nada.

  • Domínio 1 · Implementar e gerenciar
  • Configurar Spark workspace settings — pools, tamanhos de nó, autoscale, dynamic allocation, high concurrency, environments
  • Configurar workspace settings de domain, OneLake e Apache Airflow
  • Configurar controle de versão (Git integration) e implementar database projects
  • Criar e configurar deployment pipelines, deployment rules e variable libraries
  • Implementar controles de acesso no nível de workspace e de item
  • Implementar controles de acesso de linha, coluna, objeto e pasta/arquivo
  • Implementar dynamic data masking
  • Aplicar sensitivity labels e endossar itens
  • Implementar e usar Fabric audit logs e workspace monitoring
  • Configurar e implementar OneLake security roles
  • Escolher entre Dataflow Gen2, pipeline e notebook
  • Projetar e implementar agendamentos e event-based triggers
  • Implementar padrões de orquestração com notebooks e pipelines, incluindo parâmetros e expressões dinâmicas
  • Domínio 2 · Ingerir e transformar
  • Projetar e implementar cargas full e incremental
  • Preparar dados para um modelo dimensional (SCD 1/2/3, surrogate keys, membros inferidos)
  • Projetar e implementar um padrão de carga para dados de streaming
  • Escolher um data store apropriado
  • Escolher entre Dataflows Gen2, notebooks, KQL e T-SQL para transformação
  • Criar e gerenciar OneLake shortcuts
  • Implementar mirroring
  • Ingerir dados usando pipelines e Copy job
  • Transformar dados usando PySpark, SQL e KQL
  • Desnormalizar, agrupar e agregar dados
  • Tratar dados duplicados, ausentes e atrasados
  • Escolher um engine de streaming apropriado
  • Escolher entre tabelas nativas e OneLake shortcuts no Real-Time Intelligence
  • Escolher entre query acceleration e OneLake shortcuts padrão
  • Processar dados usando Eventstreams, Spark Structured Streaming e KQL
  • Criar windowing functions
  • Domínio 3 · Monitorar e otimizar
  • Monitorar ingestão, transformação e refresh de modelo semântico
  • Configurar alertas
  • Identificar e resolver erros de pipeline
  • Identificar e resolver erros de Dataflow Gen2
  • Identificar e resolver erros de notebook
  • Identificar e resolver erros de Eventhouse
  • Identificar e resolver erros de Eventstream
  • Identificar e resolver erros de T-SQL
  • Identificar e resolver erros de OneLake shortcut
  • Otimizar uma tabela de Lakehouse
  • Otimizar um pipeline
  • Otimizar um data warehouse
  • Otimizar Eventstreams e Eventhouses
  • Otimizar performance do Spark
  • Otimizar performance de consulta
Dois dias antes da prova

Pare de ler e comece a fazer. Construa uma coisa de ponta a ponta numa trial capacity: coloque um CSV num Lakehouse, transforme com um notebook, carregue um star schema pequeno num Warehouse, faça dados de exemplo passarem por um Eventstream até um Eventhouse, escreva três consultas KQL contra ele, conecte o workspace ao Git e promova tudo por um deployment pipeline de dois estágios. Tudo em que você tropeçar nessa hora é o que precisa revisar.

Exam DP-700 · Skills measured as of 21 July 2026

Implementing Data Engineering Solutions Using Microsoft Fabric

A complete, objective-by-objective study guide built from Microsoft Learn. Every heading below maps to a bullet in the official skills outline, so you can walk the outline top to bottom and know nothing is missing.

Domain 1
30–35%
Implement and manage an analytics solution
Domain 2
30–35%
Ingest and transform data
Domain 3
30–35%
Monitor and optimize an analytics solution
Credential
Fabric Data Engineer Associate
Passing score
700 / 1000
Languages
KQL · T-SQL · PySpark
Renewal
Free, annually, online

How to use this guide

The three domains carry equal weight. That is unusual, and it is the single most useful planning fact about DP-700: there is no "big" domain to over-invest in and no small one to skip. Monitoring and optimization is worth exactly as much as ingestion.

The exam is written for someone who already builds data solutions and now has to make Fabric-specific choices. Most questions are not "what does OPTIMIZE do" — they are "given these constraints, which of these four Fabric items do you use, and why not the other three." So the decision tables in this guide matter more than the syntax, and both matter more than memorizing UI paths.

Study order that works

Read Fabric fundamentals first even though it is not an exam domain — capacity, OneLake and the item taxonomy are the vocabulary every other answer is written in. Then work Domain 2 (the most hands-on), then Domain 1, then Domain 3 last, because Domain 3 questions assume you already know what a Spark pool, a Dataflow and an Eventhouse are.

The four question shapes you will actually see

Shape 1

Pick the right item

"You need to ingest 200 GB nightly from Snowflake with no code. What do you use?" Answer lives in a decision table, not in syntax. Always eliminate on persona, latency, transformation complexity, and cost.

Shape 2

Complete the code

Drag-and-drop or fill-in KQL, T-SQL, or PySpark. Usually 3–6 lines. The trap is almost always operator order (KQL) or a missing WHEN MATCHED branch (T-SQL MERGE).

Shape 3

Diagnose the failure

A symptom plus logs. You choose the cause or the fix. Learn the error strings: HTTP 430, CapacityLimitExceeded, schema-conversion failure, shortcut credential expiry.

Shape 4

Order the steps

Sequencing a Git connect, a deployment-pipeline promotion, or a security configuration. Fabric has hard prerequisites (e.g. workspace identity before trusted workspace access) — those are the trick.

Preview features are fair game

Microsoft updates DP-700 roughly every six months and preview features do appear. Where this guide marks something Preview, know what it is and what problem it solves — you will not be asked about its GA date.

Fabric fundamentals (the vocabulary)

Capacity, CUs, bursting, smoothing

Everything in Fabric runs on a capacity — a pool of compute measured in Capacity Units (CU), bought as an F SKU (F2 → F2048) or inherited from a Power BI P SKU. A capacity is assigned to a region; workspaces are assigned to a capacity. Pause the capacity and everything in it stops, including mirroring replication.

ConceptWhat it meansWhy the exam cares
BurstingA job may temporarily consume more compute than the SKU nominally provides, so it finishes fast.Explains why a big Spark job succeeds on a small SKU.
SmoothingCU consumption is averaged over time — interactive operations over 5–64 minutes, background operations over 24 hours.Explains why the same job shows a small spike in the Metrics app.
TimepointA 30-second evaluation slot. 2,880 timepoints per day.The unit the Capacity Metrics app charts.
CarryforwardUnpaid CU debt pushed into future timepoints when you overspend.The thing that eventually triggers throttling.

Throttling is a staged penalty applied against future smoothed usage:

Future usage owedStageEffect
≤ 10 minutesOverage protectionNothing. Free burst headroom.
10 – 60 minutesInteractive delay20-second delay added to new interactive operations.
60 min – 24 hoursInteractive rejectionInteractive operations rejected; background jobs still run.
> 24 hoursBackground rejectionEverything rejected.
Exceptions worth memorizing
  • Warehouse operations are classified as background, so they get the generous 24-hour smoothing window.
  • Real-Time Intelligence starts throttling at the 60-minute stage — it skips the 20-second interactive delay entirely.
  • Eventstreams do not reject; they reduce their CU allocation instead.

Fixes for a throttled capacity, in order of preference: wait (capacities self-heal as carryforward burns down) → scale the SKU up temporarily → distribute workloads across capacities → enable overage billing (3× rate) → pause and resume, which zeroes future usage but makes content unavailable. Diagnose with the Microsoft Fabric Capacity Metrics app: the Compute page's system-events table, the Overages tab, and the Minutes to burndown metric.

OneLake

One logical data lake per tenant, automatically provisioned, no infrastructure to create. Its structure is fixed and worth drawing on paper:

Level 1
TenantOne OneLake. Not optional, not deletable.
Level 2
WorkspaceBehaves like a storage container.
Level 3
ItemLakehouse, Warehouse, Eventhouse… a folder.
Level 4
Tables / FilesManaged Delta tables vs. free-form files.
  • Delta Parquet is the native format for every workload. Warehouse, Lakehouse and Eventhouse all land data as Delta, which is what makes one copy readable by all engines.
  • OneLake exposes a subset of the ADLS Gen2 and Blob APIs, so external tools address it as https://onelake.dfs.fabric.microsoft.com/<workspace>/<item>/Tables/<table>.
  • A tenant setting in the OneLake section controls whether external apps (ADLS APIs, OneLake file explorer) may reach it. Turning it off does not block Fabric's own engines.
  • Encryption at rest uses Microsoft-managed keys by default (customer-managed keys optional); TLS 1.2 minimum in transit.

The item taxonomy you must recognize on sight

Data Engineering

Lakehouse

Delta tables + unstructured files, Spark-first, read-only SQL analytics endpoint attached. Schema-on-read.

Data Engineering

Notebook · Spark Job Definition · Environment

Interactive code, submitted batch code, and the reusable runtime/library/compute config the other two attach to.

Data Warehouse

Warehouse

Full read/write T-SQL, multi-table ACID transactions, schema-on-write. The only Fabric store with real DML.

Real-Time

Eventstream · Eventhouse · KQL Database

No-code stream routing; the container for KQL databases; the time-series store queried with KQL.

Data Factory

Pipeline · Dataflow Gen2 · Copy job

Orchestration + activities; Power Query transformation; wizard-driven full/incremental/CDC replication with no pipeline.

Platform

Mirrored database · Variable library · Activator

Near-real-time replica of an external DB; stage-aware config values; the rules engine behind alerts and event triggers.

Choosing a data store — the master decision table

This one table answers a disproportionate share of Domain 2 questions.

StoreIdeal workloadPrimary persona / skillWrite APIMulti-table transactions
LakehouseBig data, ML, unstructured & semi-structured, data engineeringData engineer, data scientist — SparkSpark (PySpark, Scala, Spark SQL, R), pipelines, DataflowsNo
WarehouseEnterprise DW, SQL-based BI, OLAP, full transaction supportDW developer, data architect, DBA — T-SQLT-SQL DML, COPY INTO, CTAS, pipelinesYes
Eventhouse / KQL DBStreaming, telemetry, logs, high-granularity interactive analysis over JSON/textApp developer, data engineer — KQLEventstream, SDKs, Kafka, .ingest, DataflowsNo
SQL database in FabricOperational OLTP inside FabricApp/DB developer, DBA — T-SQLT-SQL (full OLTP surface)Yes
Cosmos DB in FabricAI apps, NoSQL, vector searchAI/app developer — REST/SDKREST API, language SDKsNo
All five land data in OneLake in open Delta format

So the choice is never "which one can other engines read" — they all can. The choice is about write semantics, the developer's language, and latency.

Workspace roles — memorize this matrix

CapabilityAdminMemberContributorViewer
Update / delete the workspace
Add or remove people, including other admins
Add members and lower roles; allow resharing
Create workspace identity
Connect the workspace to a Git repository
Create / modify warehouse, database, mirroring items
Write, delete, execute notebooks / pipelines / Spark jobs
Read Lakehouse & Warehouse data via T-SQL (ReadData)
Read data via OneLake APIs & Spark (ReadAll)
Read Lakehouse data in Lakehouse explorer
Subscribe to OneLake events
View execution output of pipelines / notebooks
Modify gateway connection settings; schedule gateway refresh
The Viewer distinction that gets tested

A Viewer can read Lakehouse and Warehouse data through T-SQL (the SQL analytics endpoint) but cannot read it through Spark, the OneLake APIs, or the Lakehouse explorer. If someone must query with a notebook, Viewer is not enough — either promote them to Contributor or grant them a OneLake security role.

Domain 1 · 30–35% of the exam

Implement and manage an analytics solution

Configuration and governance. Four objective groups: workspace settings, lifecycle management, security and governance, and orchestration. This is the domain where "in what order do you do these steps" questions live.

Objective 1.1Configure Microsoft Fabric workspace settings

Spark workspace settings

Workspace settings → Data Engineering/ScienceSpark settings. Four tabs matter: Pool, Environment, Job admission (High concurrency), and Automatic log.

Starter pools vs. custom pools

Starter poolCustom pool
Start time5–10 seconds (pre-warmed, Microsoft-managed, best effort)2–5 minutes on demand; ~5 s for a custom live pool with pre-installed libraries
Node sizeMedium onlySmall → XX-Large
ScalingDynamic against pre-warmed capacityManual or autoscale (min/max nodes)
BillingOnly while a session is actively executing. Startup, idle context init and deallocation are not billed.
Use forAd-hoc exploration, fast iterationProduction, predictable latency, resource control

Node families

Node sizevCoresMemoryMax nodes on F64
Small432 GB96
Medium864 GB48
Large16128 GB24
X-Large32256 GB12
XX-Large64512 GB6
The formula behind every Spark sizing question

1 Capacity Unit = 2 Spark vCores, and the default burst multiplier is 3×.
So F64 → 64 × 2 = 128 base vCores → 384 vCores with burst. That is why a pool of 48 Medium nodes (48 × 8 = 384) is the maximum you can define on F64. X-Large and XX-Large require a non-trial SKU.

  • Autoscale — set min and max nodes; executor decommissioning is on by default (spark.yarn.executor.decommission.enabled = true).
  • Dynamic executor allocation — reserves executors at submission from the minimum, requests more mid-run, releases them at completion. It removes manual per-stage tuning.
  • Session expiration — default 20 minutes; the pool deallocates 2 minutes after expiry. Single-node pools are supported (driver and executor share one node with halved resources).
  • High concurrency — lets multiple notebooks share one Spark session (and, when enabled for pipelines, notebook activities in the same pipeline run share a session). Cuts start-up cost dramatically for many small notebooks.

Environments

An Environment is a workspace item bundling three things: Spark compute (runtime version + session properties), libraries (public feeds and custom .whl/.jar uploads), and resources (small files shared across attached notebooks).

  • Save stages changes; Publish applies them. Only one publish at a time; you cannot edit libraries or compute during a publish.
  • Quick mode publishes in about 5 seconds. Full mode takes 3–6 minutes to publish plus 1–3 minutes at session startup, but produces a snapshot — use it for pipelines, scheduled runs and shared workloads.
  • Resources are real-time; they never need publishing.
  • Attach at three levels: workspace default (Workspace settings → Spark settings → Environment tab), notebook, or Spark job definition. Once an environment is the workspace default, only workspace admins can update it.
  • Cross-workspace attachment requires the same capacity and network security settings, and the source environment's compute config is ignored — the current workspace's pool wins.
  • Changes only take effect in the next session.

Domain workspace settings

Domains are logical groupings of workspaces by business area — the mechanism for a federated data-mesh governance model. Subdomains refine them and inherit the parent's admins.

RoleCan do
Fabric adminCreate/rename/delete domains, name domain admins and contributors, assign workspaces, manage every domain.
Domain adminEdit the description and image, define contributors, assign workspaces, override delegated settings. Cannot delete the domain, change its name, or change other admins.
Domain contributorAssign their own workspaces (they must be workspace admin). No admin-portal access.
  • Assign workspaces three ways: by workspace name, by workspace admin (grabs every workspace those people admin), or by capacity. The last two exclude personal "My workspaces".
  • Default domain — set for named users/groups: their existing unassigned workspaces get assigned, new workspaces are auto-assigned, and those users become domain contributors.
  • Delegated settings let a domain override selected tenant settings — notably default sensitivity labels and certification (enable/disable, name the certifiers, supply a documentation URL).
Domains are discovery, not security

Assigning a workspace to a domain does not change item visibility, accessibility or permissions. It changes filtering in the OneLake catalog and enables federated governance settings. If a question offers "assign to a domain" as a way to restrict access, it is wrong.

OneLake workspace settings

  • Shortcut cache — On/Off, a retention period of 1–28 days, and a Reset cache button. The retention timer resets each time a file is accessed. Files larger than 1 GB are not cached. Caching applies to GCS, Amazon S3, S3-compatible and on-premises-gateway shortcuts — it is a cross-cloud egress-cost optimization.
  • Workspace identity — a managed identity for the workspace (created by an Admin only). It is the prerequisite for trusted workspace access to firewalled ADLS Gen2 accounts and for managed private endpoints.
  • Managed private endpoints — private connectivity from Fabric Spark to data sources behind a VNet.

Apache Airflow job workspace settings

Apache Airflow job is the successor to ADF's Workflow Orchestration Manager: a managed Airflow service for code-first, Python DAG orchestration.

  • Airflow 2.10.5 on Python 3.12. You cannot change the Airflow version of an existing job — create a new one.
  • Supports Git sync for DAG storage, Azure Key Vault as a secrets backend, private packages, autoscaling, high availability, deferrable operators, and pause/resume TTL.
  • Private/virtual networks are not supported.
  • Choose Airflow over a pipeline when the team already writes Airflow DAGs, needs Python-defined branching/dynamic task generation, or is migrating existing DAGs. Choose a Fabric pipeline for no-code orchestration.

Objective 1.2Implement lifecycle management in Fabric

Configure version control (Git integration)

Git integration is configured at the workspace level and binds one workspace to one branch and one folder. Supported providers: Azure DevOps, GitHub and GitHub Enterprise — cloud only. Only a workspace Admin can connect the workspace to a repo.

Step 1
ConnectWorkspace settings → Git integration. Pick org, project, repo, branch, folder.
Step 2
CommitPush workspace changes into the branch. Folder structure is preserved.
Step 3
UpdatePull branch changes down into the workspace.
Step 4
Branch outCreate a new branch + new workspace for isolated feature work.

Supported item types (partial list — know the shape, not every entry): Lakehouse, Notebook, Spark Job Definition, Environment, GraphQL, User Data Functions, Copy Job, Dataflow Gen2, Pipeline, Mirrored Database, Warehouse, Mirrored Azure Databricks Catalog, Eventhouse, Eventstream, KQL Database, KQL Queryset, Real-Time Dashboard, Activator, SQL database, Variable Library. Many Power BI and Data Science items are still Preview.

Unsupported items do not block the connection

If a workspace contains item types Git does not support, you can still connect. Those items are ignored — never saved, never synced, never deleted — but they do show in the source-control panel and you cannot commit or update them. Reports bound to Azure AS / SSAS semantic models, push datasets, live connections and Model v1 semantic models are not supported.

Git status states

  • Synced — identical in workspace and branch.
  • Uncommitted — changed in the workspace only. Commit.
  • Modified / Update required — changed in the branch only. Update.
  • Conflict — changed in both. Resolve by choosing the workspace version or the branch version per item.

Database projects

For Warehouses, source control of the schema itself. Use the SQL Database Projects extension in VS Code (or Azure Data Studio) to extract the warehouse into a .sqlproj, diff it against another environment with Schema Compare, and publish with SqlPackage. This is the answer when the requirement is "deploy only the schema changes as part of an existing DevOps release pipeline" rather than "promote whole items."

Deployment pipelines

Fabric's built-in content promotion mechanism: 2 to 10 stages (default 3 — Development, Test, Production), each stage assigned one workspace.

ConceptBehavior
PairingItems are paired across adjacent stages and stay paired even if renamed. Pairing happens when you assign a workspace to a stage, or when you deploy previously unpaired content.
Unpaired duplicatesTwo items with the same name and type in adjacent workspaces that were never paired will create duplicates on deploy, not overwrite. This is the classic trap.
Deployment rulesPer-stage overrides for data sources and parameters, so Test points at the Test lakehouse.
Not copiedPermissions and sharing settings are never copied forward. Some refresh schedules are not either.
AutomationDeployment Pipelines REST APIs, plus the Fabric CLI and Terraform provider for full CI/CD.
PermissionWorkspace admin, to create pipelines, assign workspaces, deploy and set rules.
Git integration vs. deployment pipelines — they solve different problems
Git integrationDeployment pipelines
PurposeVersion history, branching, code review, backupPromote content Dev → Test → Prod
Unit of workA commit on a branchA deployment between adjacent stages
TriggerCommit / update, PR workflowManual button or REST API

A mature setup uses both: Git for the dev workspace and code review, deployment pipelines (or the APIs) for promotion.

Variable libraries

A workspace item holding configuration variables plus alternative value sets — one per lifecycle stage — with exactly one active value set at a time. Consumers resolve the value from whichever set is active in their own workspace, so the same pipeline definition points at the dev lakehouse in Dev and the prod lakehouse in Prod without a deployment rule.

  • Types: string, integer, boolean, and item references.
  • Consumers: Pipeline, Lakehouse shortcut, Notebook (via notebookutils.variableLibrary and %%configure), Dataflow Gen2, Copy job, User data functions, Plan.
  • Limits: up to 1,000 variables and 1,000 value sets; fewer than 10,000 total cells; item ≤ 1 MB; notes and descriptions ≤ 2,048 characters.
  • You cannot delete the active value set — activate another one first.
  • Works with both Git integration and deployment pipelines, and is exposed through the Fabric public APIs.

Objective 1.3Configure security and governance

The four layers of Fabric access control

Layer 1
Workspace rolesAdmin / Member / Contributor / Viewer. Coarse, applies to every item.
Layer 2
Item permissionsRead, ReadData, ReadAll, Write, Reshare, Execute, Build.
Layer 3
OneLake securityData-plane roles: table/folder scope, plus RLS and CLS constraints.
Layer 4
Engine-nativeT-SQL GRANT/DENY, RLS policies, CLS, dynamic data masking, KQL RLS.

Item-level access controls

PermissionGrantsEffect on Lakehouse / Warehouse
ReadSee the item and its metadata; connect to the SQL analytics endpointMetadata only — no data without a further grant
ReadDataQuery data over T-SQLSQL endpoint access (delegated identity mode)
ReadAllRead the underlying filesOneLake / Spark / Lakehouse explorer access; maps to the DefaultReader OneLake role
WriteModify the itemFull metadata + SQL + OneLake access
ExecuteRun the itemNotebooks, pipelines, Spark job definitions
BuildBuild new content on a semantic modelRequired for Direct Lake report authoring
ResharePass the grant on

OneLake security (data-plane roles)

A OneLake security role has four parts: data (the tables or folders it covers), permissions, members, and constraints (row- and column-level exclusions). Roles are authored once and enforced by every Fabric engine — and by registered external "authorized engines" that fetch the effective policy through the OneLake APIs.

  • Only workspace Admin or Member can create OneLake security roles.
  • They govern Viewers and users holding item-level Read/ReadData. Workspace Admins, Members and Contributors bypass them entirely — they always read and write everything.
  • Every lakehouse ships with a DefaultReader role that grants access to ReadAll holders. It can be edited or deleted.
  • Folder-level security on the source lakehouse also governs shortcuts that point into it — security follows the data, not the reference.
  • Child folders inherit parent permissions by default.
"Restrict the data engineer to one folder" — why the obvious answer fails

If the person is a workspace Contributor, no OneLake role will restrict them. The correct sequence is: remove them from the workspace role, grant item-level Read, then add them to a OneLake security role scoped to that folder.

Row-, column- and object-level security in the Warehouse

Row-level security

An inline table-valued predicate function plus a security policy. RLS works in both the Warehouse and the SQL analytics endpoint.

T-SQL · Row-level security
-- 1. The predicate function: returns a row when access is allowed
CREATE FUNCTION Security.tvf_SecurityPredicate(@SalesRep AS nvarchar(50))
    RETURNS TABLE
WITH SCHEMABINDING
AS
    RETURN SELECT 1 AS result
    WHERE @SalesRep = USER_NAME()
       OR USER_NAME() = 'manager@contoso.com';
GO

-- 2. Bind it to the table
CREATE SECURITY POLICY Security.SalesFilter
ADD FILTER PREDICATE Security.tvf_SecurityPredicate(SalesRep)
ON dbo.Sales
WITH (STATE = ON);
GO
FILTER vs. BLOCK predicate

ADD FILTER PREDICATE silently hides rows from reads. ADD BLOCK PREDICATE ... AFTER INSERT | AFTER UPDATE | BEFORE UPDATE | BEFORE DELETE raises an error on writes that would violate the rule. If a question asks how to stop a user writing a row outside their region, the answer is a block predicate.

Column-level security

T-SQL · Column-level security
GRANT SELECT ON dbo.Employees(EmployeeId, FirstName, LastName, Department)
    TO [analysts@contoso.com];

-- or deny specific columns on an otherwise-granted table
DENY SELECT ON dbo.Employees(Salary, NationalId) TO [analysts@contoso.com];

Object-level security

T-SQL · Roles and object grants
CREATE ROLE SalesAnalyst;
GRANT SELECT ON SCHEMA::sales TO SalesAnalyst;
DENY SELECT ON dbo.PayrollDetail TO SalesAnalyst;
ALTER ROLE SalesAnalyst ADD MEMBER [user@contoso.com];

Dynamic data masking

Masks values in query results only — the stored data is untouched. Users with the UNMASK permission (and workspace Admin/Member/Contributor) see real values.

FunctionSyntaxResult
Defaultdefault()XXXX for strings, 0 for numerics, 1900-01-01 for dates
Emailemail()aXXX@XXXX.com
Randomrandom(1, 100)A random number in range — numeric types only
Partialpartial(0,"XXXX-",4)Keeps a prefix and suffix, pads the middle
Datetimedatetime("M")Masks all but the chosen date part
T-SQL · Dynamic data masking
ALTER TABLE dbo.Customers
    ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');

ALTER TABLE dbo.Payments
    ALTER COLUMN CardNumber ADD MASKED WITH (FUNCTION = 'partial(0,"XXXX-XXXX-XXXX-",4)');

GRANT UNMASK TO [finance-admin@contoso.com];
ALTER TABLE dbo.Customers ALTER COLUMN Email DROP MASKED;   -- remove
Masking is obfuscation, not protection

A user who can query the table can still infer masked values with WHERE Salary > 100000. DDM complements RLS/CLS; it never replaces them.

Sensitivity labels and endorsement

  • Sensitivity labels come from Microsoft Purview Information Protection. They are applied per item, propagate downstream through the lineage (a report inherits from its semantic model), and can carry encryption that follows exported files. Applying them requires the label to be published to the user and the tenant setting enabled. A domain can set a default label via delegated settings.
  • Endorsement has three levels:
    • Promoted — any user with write permission on the item can promote it.
    • Certified — only users named as certifiers by the Fabric admin (or the domain admin, via delegated settings) can certify.
    • Master data — the authoritative source for a subject area.
    Endorsed items rank higher and are filterable in the OneLake catalog.

Fabric audit logs

  • Fabric activity flows into the Microsoft Purview / Microsoft 365 unified audit log. Access it through the Purview compliance portal, the Fabric admin portal's Audit logs link, or programmatically with Get-PowerBIActivityEvent / the Admin Activity Events REST API.
  • The admin monitoring workspace ships the Feature usage and adoption report and a semantic model of activity data for Fabric admins.
  • Workspace monitoring (workspace settings → Monitoring → Log workspace activity) provisions a read-only Eventhouse in the workspace that collects diagnostic logs and metrics for Eventhouse, Eventstream, pipelines, Copy jobs, mirrored databases, GraphQL and semantic models. Retention is 30 days; it is billed as normal Eventhouse/Eventstream capacity consumption; you can enable workspace monitoring or Log Analytics, not both. Query it with KQL or SQL.
  • OneLake data-plane operations appear with names that map to ADLS Gen2 APIs (CreateFile, DeleteFile…). Read requests and Fabric workload requests are not included.

Objective 1.4Orchestrate processes

Choose between Dataflow Gen2, a pipeline and a notebook

Copy activity (pipeline)Copy jobDataflow Gen2Notebook / SparkEventstream
Use caseLake/DW migration, ingestion, light transformIngestion, incremental copy, replicationIngest, transform, clean, profileIngest, transform, process, profileEvent ingestion & transformation
PersonaData engineer / integratorAnalyst, integrator, engineerEngineer, integrator, analystData engineer, integratorEngineer, scientist, developer
SkillsETL, SQL, JSONETL, SQL, JSONETL, M, SQLSpark (Python, Scala, SQL, R)SQL, JSON, messaging
CodeNo/low codeNo/low codeNo/low codeCodeNo code
Sources50+ connectors50+ connectors150+ connectorsHundreds of Spark librariesCDC, Kafka, messaging, streams
Transform complexityLowLowLow → high (300+ functions)Low → high (unbounded)Low
InterfaceWizard, canvasWizard, canvasPower QueryNotebook, Spark job definitionCanvas
How to eliminate answers fast
  • The scenario says "Power Query", "the analyst knows M", or "150+ connectors" → Dataflow Gen2.
  • The scenario says "petabyte", "complex/custom logic", "ML", "unstructured" → notebook.
  • The scenario says "CDC", "no pipeline needed", "a few clicks", "resume where it left off" → Copy job.
  • The scenario says "orchestrate", "on failure", "loop over", "then refresh the model" → pipeline.
  • The scenario says "no schedule", "as events arrive" → Eventstream.

Pipeline activities you must recognize

Move & transform

Copy data · Copy job · Dataflow Gen2 · Notebook · Spark Job Definition · Script · Stored procedure · Lakehouse maintenance Preview

Control flow

ForEach · If Condition · Switch · Until · Wait · Set variable · Filter · Invoke pipeline · Fail

Lookup & metadata

Lookup · Get Metadata · Web · Web hook · Azure Function

Notify

Office 365 Outlook · Teams · Semantic model refresh · KQL activity

Every activity supports four dependency conditions on its output arrow: On success, On failure, On completion and On skip. Activities also expose Retry, Retry interval, Timeout and Secure output/input on the General tab.

Parameters and dynamic expressions

Parameters are set once per run and are read-only inside it. Variables are mutable during the run via the Set variable and Append variable activities.

ExpressionReturns
@pipeline().PipelineName / .PipelinePipeline name / ID
@pipeline().RunIdID of this run — the standard correlation key for logging
@pipeline().TriggerTime, .TriggerName, .TriggerIdTrigger metadata, UTC ISO 8601
@pipeline().DataFactoryWorkspace ID
@pipeline()?.TriggeredByPipelineNameParent pipeline name, or null
@pipeline().parameters.<name> / @variables('name')Parameter / variable value
@activity('Lookup1').output.firstRow.WatermarkValueA field from an upstream activity's output
@activity('Copy1').error.messageFailure message, for logging on the failure path
@item()The current element inside a ForEach
@pipeline()?.TriggerEvent?.FileNameFile name from a storage-event trigger (? guards nulls in manual runs)
Pipeline expression language
// String interpolation uses @{ }; a bare @ starts an expression; @@ escapes a literal @
"Test_@{formatDateTime(utcNow(), 'yyyy-MM-dd')}"

// Common functions by category
Date/time : addDays addHours addMinutes formatDateTime utcNow startOfDay ticks convertFromUtc
String    : concat replace split substring startsWith endsWith toLower trim indexOf guid
Collection: contains empty first last length skip take union join intersection
Logical   : and or not if equals greater greaterOrEquals less lessOrEquals
Conversion: array bool float int string json coalesce createArray base64 uriComponent
Math      : add sub mul div mod min max rand range

// Worked examples
@concat('sales_', formatDateTime(utcNow(), 'yyyyMMdd'), '.parquet')
@if(greater(activity('Lookup_RowCount').output.firstRow.cnt, 0), 'load', 'skip')
@formatDateTime(addDays(utcNow(), -1), 'yyyy-MM-ddTHH:mm:ssZ')
@coalesce(pipeline().parameters.RunDate, formatDateTime(utcNow(), 'yyyy-MM-dd'))

Passing parameters into a notebook

Python · notebookutils
# In the CALLED notebook: mark a cell as the "parameter cell" (toggle in the cell menu)
# and declare defaults there. The caller's values overwrite them at run time.
run_date = "2026-01-01"
layer    = "bronze"

# Return a value to the caller
import notebookutils
notebookutils.notebook.exit(str(rows_written))

# In the CALLING notebook
exit_val = notebookutils.notebook.run("Load_Bronze", 90, {"run_date": "2026-08-30", "layer": "silver"})
# 4th positional arg = workspace ID, for cross-workspace calls (Runtime 1.2+)

From a pipeline, the Notebook activity's Base parameters map onto the same parameter cell, and the notebook's exit() value is readable downstream as @activity('Notebook1').output.result.exitValue.

Orchestrating many notebooks with a DAG

Python · notebookutils.notebook.runMultiple
DAG = {
    "activities": [
        {"name": "LoadCustomers", "path": "nb_load_customers",
         "timeoutPerCellInSeconds": 120, "args": {"layer": "bronze"}},
        {"name": "LoadOrders", "path": "nb_load_orders",
         "timeoutPerCellInSeconds": 120, "args": {"layer": "bronze"}},
        {"name": "BuildFactSales", "path": "nb_build_fact",
         "timeoutPerCellInSeconds": 300,
         "retry": 1, "retryIntervalInSeconds": 30,
         "dependencies": ["LoadCustomers", "LoadOrders"]}
    ],
    "timeoutInSeconds": 43200,   # 12 h, the default
    "concurrency": 50          # 0 = no limit
}
notebookutils.notebook.runMultiple(DAG, {"displayDAGViaGraphviz": True})

# Simple parallel form, no dependencies:
notebookutils.notebook.runMultiple(["nb_a", "nb_b", "nb_c"])
runMultiple vs. a pipeline of notebook activities

runMultiple runs every child notebook in one Spark session — no per-notebook session startup, so ten small notebooks finish in a fraction of the time and CU. Use a pipeline instead when you need mixed activity types, cross-item orchestration, retries at the item level, or event triggers.

Schedules and event-based triggers

MechanismHow it worksNotes
On-demandRun in the editorTrigger parameters resolve to null — this is why ? null-guards matter
Fixed scheduleHome → Schedule. Frequency, start & end date, time zoneBoth start and end date are required; use a far-future end date. Up to 20 schedules per pipeline
Interval-based schedule PreviewNon-overlapping fixed intervalsExposes Window start time / Window end time as trigger parameters — the clean way to do tumbling-window batch loads
Storage event triggerHome → Trigger → creates an Eventstream + an Activator (Reflex) itemSources: OneLake events, Azure Blob Storage events. Filter with the Subject field (folder, file name, extension, container)
Fabric item / job eventsWorkspace item created/updated/deleted; job eventsEvent types like Microsoft.Fabric.ItemCreateSucceeded, …ItemUpdateFailed

Storage-event payloads follow the CloudEvents schema: source, subject, type (e.g. Microsoft.Storage.BlobCreated), time, id, data, specversion. Fabric parses file name and folder path out of Subject and exposes them in the expression builder as trigger parameters.

Where the trigger actually lives

An event trigger you create from a pipeline is stored as a separate Activator (Reflex) item in the workspace, not inside the pipeline. To find, edit or disable it, open that Reflex item, or use Triggers → View triggers from the pipeline. Deleting the pipeline does not delete the trigger.

Orchestration patterns worth knowing by name

Metadata-driven ingestion

A control table lists sources, targets, watermarks and load type. A Lookup reads it, a ForEach iterates @activity('Lookup').output.value, and one parameterized Copy activity handles every source. Set Sequential off and tune Batch count (max 50) for parallelism.

Medallion orchestration

Bronze (raw, append-only) → Silver (cleansed, deduplicated, conformed) → Gold (star schema, aggregated). One pipeline per layer, chained with Invoke pipeline, so a layer can be re-run independently.

Watermark loop

Lookup old watermark → Copy rows > watermark → Lookup new max → Stored procedure writes the new watermark. Update the watermark only on success, so a failure re-processes rather than skips.

Idempotent re-runs

Design so a re-run cannot double-load: MERGE instead of INSERT, replaceWhere partition overwrite instead of append, and a deterministic business key. Exam scenarios love "the pipeline was re-run after a failure and rows were duplicated."

Domain 2 · 30–35% of the exam

Ingest and transform data

The hands-on domain. Loading patterns and dimensional modelling, batch ingestion and transformation across PySpark / T-SQL / KQL, and streaming with Eventstream, Eventhouse and Spark Structured Streaming.

Objective 2.1Design and implement loading patterns

Full and incremental loads

PatternWhenFabric implementation
Full load / truncate-and-reloadSmall tables, no reliable change marker, dimension rebuilds where surrogate keys are not referencedTRUNCATE TABLE + INSERT…SELECT, CTAS, or Spark mode("overwrite")
Watermark / high-water markSource has a monotonically increasing column (ROWVERSION, datetime, identity)Lookup + parameterized Copy; or Copy job in incremental mode. Tracks inserts and updates only
CDCYou must capture deletes, or the source changes heavilyCopy job CDC mode, Eventstream CDC sources, or Mirroring
Partition overwriteReloading a bounded slice, e.g. yesterdayDelta replaceWhere, or dynamic partition overwrite
Upsert / mergeLate-arriving corrections into an existing tableDelta MERGE in Spark, T-SQL MERGE in the Warehouse
PySpark · incremental patterns
from delta.tables import DeltaTable
from pyspark.sql import functions as F

# 1. Read the current watermark from the target
watermark = spark.sql("SELECT COALESCE(MAX(ModifiedDate), '1900-01-01') AS wm FROM silver.customers") \
                 .collect()[0]["wm"]

# 2. Pull only new/changed rows
src = (spark.read.format("delta").load("Tables/bronze/customers")
            .filter(F.col("ModifiedDate") > F.lit(watermark)))

# 3. UPSERT with MERGE
tgt = DeltaTable.forName(spark, "silver.customers")
(tgt.alias("t")
    .merge(src.alias("s"), "t.CustomerId = s.CustomerId")
    .whenMatchedUpdateAll(condition="s.ModifiedDate > t.ModifiedDate")
    .whenNotMatchedInsertAll()
    .whenNotMatchedBySourceUpdate(set={"IsDeleted": "true"})   # soft delete
    .execute())

# 4. Alternative: idempotent partition overwrite for a bounded slice
(df.write.format("delta").mode("overwrite")
   .option("replaceWhere", "LoadDate >= '2026-08-01' AND LoadDate < '2026-09-01'")
   .saveAsTable("silver.orders"))

Preparing data for a dimensional model

Fabric's guidance is classic Kimball: a star schema of fact tables surrounded by dimension tables. Facts hold measures plus dimension keys at a declared grain; dimensions describe the entities.

Fabric Warehouse constraints are NOT ENFORCED

PRIMARY KEY, UNIQUE and FOREIGN KEY can only be created with the NOT ENFORCED option. They are metadata used by the query optimizer and by modelling tools — the engine will happily let you insert a duplicate key or an orphan fact row. Your ETL must enforce uniqueness and referential integrity itself.

Slowly changing dimensions

TypeBehaviorImplementation
Type 0Never changes (e.g. original signup date)Insert only
Type 1Overwrite — no historyMERGE … WHEN MATCHED THEN UPDATE
Type 2New row per change, with StartDate/EndDate/IsCurrentExpire the current row, then insert the new version
Type 3Keep one prior value in an extra columnPreviousValue = Value then update Value
Type 6Hybrid 1+2+3Type 2 rows plus a "current value" column updated on every row
T-SQL · SCD Type 2 in Fabric Warehouse
-- Step 1: expire rows whose tracked attributes changed
UPDATE d
   SET d.EndDate  = CAST(GETDATE() AS date),
       d.IsCurrent = 0
  FROM dbo.DimProduct AS d
  JOIN staging.Products AS s ON s.ProductID = d.ProductID
 WHERE d.IsCurrent = 1
   AND (d.ProductName <> s.ProductName OR d.Category <> s.Category);

-- Step 2: insert the new current version (and brand-new members)
INSERT INTO dbo.DimProduct
      (ProductID, ProductName, Category, StartDate, EndDate, IsCurrent, IsInferred)
SELECT s.ProductID, s.ProductName, s.Category, CAST(GETDATE() AS date), NULL, 1, 0
  FROM staging.Products AS s
  LEFT JOIN dbo.DimProduct AS d
       ON d.ProductID = s.ProductID AND d.IsCurrent = 1
 WHERE d.ProductID IS NULL;

-- Step 3: soft-delete members that disappeared from the source
UPDATE dbo.DimProduct
   SET IsDeleted = 1, IsCurrent = 0, EndDate = CAST(GETDATE() AS date)
 WHERE IsCurrent = 1
   AND NOT EXISTS (SELECT 1 FROM staging.Products s WHERE s.ProductID = DimProduct.ProductID);
T-SQL · fact load with time-correct SCD2 lookup
INSERT INTO dbo.FactSales (DateKey, CustomerKey, ProductKey, Quantity, SalesAmount, SourceOrderNumber)
SELECT CONVERT(int, FORMAT(o.OrderDate, 'yyyyMMdd')),
       ISNULL(c.CustomerKey, -1),          -- -1 = the "Unknown" member
       ISNULL(p.ProductKey,  -1),
       l.Quantity, l.Quantity * l.UnitPrice, o.SalesOrderNumber
  FROM staging.SalesOrders o
  JOIN staging.SalesOrderLines l ON l.SalesOrderID = o.SalesOrderID
  -- point-in-time join: pick the dimension version valid on the order date
  LEFT JOIN dbo.DimCustomer c
         ON c.CustomerID = o.CustomerID
        AND o.OrderDate >= c.StartDate
        AND (o.OrderDate < c.EndDate OR c.EndDate IS NULL)
  LEFT JOIN dbo.DimProduct p
         ON p.ProductID = l.ProductID AND p.IsCurrent = 1
 WHERE o.SalesOrderNumber > @LastLoadedOrderNumber;   -- high-water mark

Rules the exam tests on dimensional loading

  • Never truncate-and-reload a dimension whose surrogate keys are referenced by facts — you would orphan every fact row.
  • Soft delete, never hard delete, dimension members. Historical facts still point at them.
  • Inferred members (a.k.a. early-arriving facts): when a fact references an unknown dimension key, insert a placeholder row flagged IsInferred = 1, then enrich it when the real record arrives.
  • The date dimension has no source system — generate it with a recursive CTE or a numbers table, well beyond the current date.
  • Load dimensions before facts, always. Facts need the keys.
  • Prefer staging tables in their own staging schema, cleared with TRUNCATE TABLE at the start of each run.

A loading pattern for streaming data

Streaming data reaching rest follows the medallion shape with one adjustment — bronze is append-only and never mutated:

Bronze
Raw, append-onlyExact payload plus ingestion timestamp. Never edited, so it can always be replayed.
Silver
Cleansed, deduplicatedSchema applied, late data reconciled, business keys conformed.
Gold
Star schema / aggregatesWhat Direct Lake semantic models and dashboards read.

Lakehouse or Eventhouse for the landing zone? Eventhouse when the queries are time-series and interactive and latency is measured in seconds; Lakehouse when the stream feeds the same batch pipelines as everything else. You can have both cheaply: land in Eventhouse and turn on OneLake availability, which materializes a Delta copy readable by Spark and the SQL endpoint at no extra storage cost.

Objective 2.2Ingest and transform batch data

OneLake shortcuts

A shortcut is a pointer that appears as a folder. No data is copied and no storage is consumed.

Internal shortcuts

Point at another Fabric item: Lakehouse, Warehouse, KQL database, Mirrored database, Mirrored Azure Databricks catalog, SQL database, semantic model. Authorization uses the calling user's identity — they need read permission on the target.

External shortcuts

Amazon S3 · S3-compatible · ADLS Gen2 · Azure Blob Storage · Google Cloud Storage · Dataverse · Iceberg · OneDrive/SharePoint. Authorization is delegated through a cloud connection, so only users with permission on that connection can create the shortcut.

Tables/Files/
NestingTop level only — no subdirectoriesAny depth
DiscoveryDelta metadata and schema auto-sync; table appears in the SQL endpointNo table discovery
Use forStructured Delta datasets, internal OneLake sources, schema shortcutsUnstructured/semi-structured data, any format, external stores
Reading a shortcut from each engine
# Spark — a shortcut in Tables/ behaves exactly like a native table
df = spark.read.format("delta").load("Tables/MyShortcut")
df = spark.sql("SELECT * FROM MyLakehouse.MyShortcut LIMIT 1000")

-- SQL analytics endpoint
SELECT TOP (100) * FROM [MyLakehouse].[dbo].[MyShortcut];

// KQL — a shortcut in a KQL database is an external table
external_table('MyShortcut')
| take 100
Shortcut limits and gotchas
  • Up to 100,000 shortcuts per item; up to 10 shortcuts per OneLake path; chaining is capped at 5 levels deep.
  • Names cannot contain % or +, and non-Latin characters are unsupported. Delta does not support table names with spaces — a space-named shortcut will not be recognized as a Delta table.
  • Deleting a shortcut removes only the pointer. But deleting content inside a shortcut deletes it at the source if you have permission there.
  • Lineage view is scoped to one workspace and does not show external shortcuts.
  • It can take up to a minute for the Table API to recognize a new shortcut.
  • Schema shortcuts only work in schema-enabled lakehouses.

Mirroring

Mirroring continuously replicates an external operational database into OneLake as Delta tables, with no ETL to build. Replication compute is free and each capacity unit includes 1 TB of free mirroring storage (so F64 → 64 TB). Latency can be as low as ~15 seconds.

FlavorWhat it replicatesSources
Database mirroringData and metadata, written as Delta into OneLakeAzure SQL DB, Azure SQL MI, SQL Server, Azure Cosmos DB, Azure Database for PostgreSQL, Snowflake, Oracle, Google BigQuery, SAP Datasphere, Fabric SQL DB; MySQL & SharePoint list Preview
Metadata mirroringCatalog structure only — the data stays put and is reached through shortcutsAzure Databricks Unity Catalog; Dremio Preview
Open mirroringYou push change data into a OneLake landing zone via a public APIAny custom app or ISV
  • Creates two things in the workspace: the replication process, and a read-only SQL analytics endpoint.
  • Requires a running Fabric capacity — pausing the capacity halts replication.
  • Delta retention defaults to 1 day for databases created after mid-June 2025 (7 days for older ones); configurable in Settings → Delta table management or via retentionInDays in the API.
Mirroring vs. shortcut vs. Copy — the one-line rule
  • Mirroring — an operational database you want continuously and cheaply available for analytics, in Delta, near real time.
  • Shortcut — data already sitting in a lake (OneLake, ADLS, S3, GCS) that you do not want to duplicate.
  • Copy activity / Copy job — scheduled batch movement, or anywhere you need transformation, filtering or a gateway.

Ingesting with pipelines and Copy job

Copy activity

  • 50+ source and 40+ sink connectors; supports staging (an interim blob/lakehouse hop) for sources that cannot stream directly to the sink.
  • Degree of copy parallelism, fault tolerance (skip incompatible rows and log them), and copy behavior (preserve hierarchy, flatten hierarchy, merge files).
  • Connectivity to on-premises through the on-premises data gateway, and into a VNet through the VNet data gateway.
  • Output metrics available downstream: rowsRead, rowsCopied, rowsSkipped, throughput, dataConsistencyVerification.

Copy job

A standalone item — no pipeline required — for full copy, incremental copy and CDC replication.

Watermark-based incrementalCDC-based
TracksInserts and updatesInserts, updates and deletes
NeedsA reliable incremental column: ROWVERSION, datetime, date, string-as-datetime, integerCDC enabled at the source and supported by the connector
Enables SCD2 destinationNoYes

Destination update methods: Append (default), Merge (needs a key column; with CDC also applies deletes), Overwrite, and SCD Type 2 with effective dating. Copy job also writes optional audit columns per row — extraction time, source file path, workspace/job/run IDs, and the incremental window bounds — which is the built-in answer to row-level lineage questions. It resumes from the last successful checkpoint after a failure, supports Git/CI-CD and Variable libraries, and has an auto-partitioning Preview mode for parallel reads of large tables.

Transforming with PySpark, SQL and KQL

PySpark · the transformation vocabulary
from pyspark.sql import functions as F, Window

# Read / write
df = spark.read.format("delta").load("Tables/bronze/orders")
df = spark.read.option("header",True).option("inferSchema",True).csv("Files/raw/*.csv")
df.write.format("delta").mode("append").saveAsTable("silver.orders")

# Shape
df2 = (df.withColumn("OrderYear", F.year("OrderDate"))
         .withColumn("Net", F.col("Gross") - F.col("Discount"))
         .withColumnRenamed("cust_id", "CustomerId")
         .drop("_ingest_raw")
         .filter(F.col("Status") != "Cancelled"))

# Group and aggregate
agg = (df2.groupBy("CustomerId", "OrderYear")
          .agg(F.sum("Net").alias("Revenue"),
               F.countDistinct("OrderId").alias("Orders"),
               F.max("OrderDate").alias("LastOrder")))

# Denormalize — broadcast the small side to avoid a shuffle
wide = df2.join(F.broadcast(dim_customer), "CustomerId", "left")

# Window functions: keep the latest row per key
w = Window.partitionBy("CustomerId").orderBy(F.col("ModifiedDate").desc())
latest = df2.withColumn("rn", F.row_number().over(w)).filter("rn = 1").drop("rn")

# Semi-structured
flat = (df.withColumn("j", F.from_json("payload", schema))
          .select("j.*")
          .withColumn("tag", F.explode("tags")))
pivoted = df2.groupBy("CustomerId").pivot("OrderYear").sum("Net")
Notebook magics and cross-language work
# %%pyspark  %%sql  %%scala  %%sparkr  %%html  %%configure

%%sql
CREATE OR REPLACE TABLE silver.customers AS
SELECT CustomerId, INITCAP(Name) AS Name, Country
FROM   bronze.customers
WHERE  CustomerId IS NOT NULL;

%%configure
{ "defaultLakehouse": { "name": "lh_silver" },
  "conf": { "spark.sql.shuffle.partitions": "200" } }
T-SQL · Warehouse transformation patterns
-- CTAS: the fastest way to materialize a transformed result
CREATE TABLE gold.SalesByRegion AS
SELECT r.RegionName,
       SUM(f.SalesAmount)                    AS Revenue,
       COUNT_BIG(*)                           AS OrderCount,
       SUM(f.SalesAmount) / NULLIF(COUNT_BIG(*),0) AS AvgOrder
  FROM dbo.FactSales f
  JOIN dbo.DimRegion r ON r.RegionKey = f.RegionKey
 GROUP BY r.RegionName;

-- Window functions
SELECT CustomerId, OrderDate, SalesAmount,
       SUM(SalesAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate
                              ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningTotal,
       LAG(SalesAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate)        AS PrevOrder,
       ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY OrderDate DESC)      AS Recency
  FROM dbo.FactSales;

-- Multi-level aggregation
SELECT Country, City, SUM(Revenue) AS Revenue, GROUPING(City) AS IsCityTotal
  FROM gold.Sales
 GROUP BY ROLLUP (Country, City);       -- also: CUBE, GROUPING SETS

-- Cross-warehouse / cross-lakehouse in one query (three-part naming)
INSERT INTO gold.DimProduct
SELECT * FROM LakehouseSilver.dbo.products;
KQL · transformation essentials
Events
| where Timestamp > ago(7d) and Level in ("Error", "Critical")
| extend Duration = EndTime - StartTime,
         Region   = tostring(parse_json(Properties).region)
| project Timestamp, DeviceId, Region, Duration, Message
| summarize ErrorCount = count(),
            Devices    = dcount(DeviceId),
            p95        = percentile(Duration, 95),
            LastSeen   = max(Timestamp)
          by Region, bin(Timestamp, 1h)
| order by Timestamp asc, ErrorCount desc

Duplicate, missing and late-arriving data

ProblemSparkT-SQLKQL
Exact duplicatesdf.dropDuplicates(["OrderId"])ROW_NUMBER() + WHERE rn = 1summarize arg_max(Timestamp, *) by Id
Duplicates in a streamdropDuplicatesWithinWatermark(["Id"])summarize take_any(*) by Id
Missing valuesdf.na.fill({"Qty":0}), df.na.drop(subset=[…])COALESCE(), ISNULL()coalesce(), iff(isnull(x), 0, x)
Unknown dimension keyLeft join then coalesce(key, lit(-1))ISNULL(d.Key, -1) or insert an inferred memberleftouter join + default
Late-arriving eventswithWatermark("ts","10 minutes")Reprocess the affected partition with MERGEIngestion-time policies; query on ingestion_time()
Bad rowsRoute to a quarantine table instead of failing the jobTRY_CAST + a rejects tableUpdate policy with IsTransactional = false
Warehouse ingestion: the numbers to remember
  • COPY INTO supports CSV, JSONL and Parquet from ADLS Gen2 and Azure Blob Storage; it authenticates by default with the Entra identity of the caller.
  • Keep source files at least 4 MB (Microsoft's performance guidance targets 100 MB – 1 GB per file) and use many files in parallel.
  • ADLS Gen2 outperforms Blob Storage.
  • Avoid singleton INSERT statements — batch with COPY INTO, INSERT…SELECT or CTAS.
  • OPENROWSET(BULK …) queries external files inline; bcp is available in Preview for client-side loading.

Fabric Warehouse T-SQL surface area

Supported

Tables, views, stored procedures, functions, roles and permissions · IDENTITY columns · MERGE · TRUNCATE TABLE · session-scoped #temp tables · CTEs (nested CTEs Preview) · a subset of query/join hints · sp_rename for columns · ALTER TABLE ADD nullable column / DROP COLUMN / add-drop NOT ENFORCED constraints · ALTER COLUMN Preview · explicit transactions with snapshot isolation.

Not supported

Triggers · Materialized views · Synonyms · CREATE USER · BULK LOAD · recursive queries · manually created multi-column statistics · SELECT … FOR XML · SET ROWCOUNT · SET TRANSACTION ISOLATION LEVEL · PREDICT · vector data type · / or \ in schema/table names.

The SQL analytics endpoint (on a Lakehouse or mirrored database) is read-only: no DDL, no INSERT/UPDATE/DELETE. It does support views, functions, stored procedures, RLS, CLS and DDM.

Schema-enabled lakehouses

  • Schemas group tables by domain and are on by default for new lakehouses. Every schema-enabled lakehouse has a dbo schema that cannot be renamed or removed. Schema names accept only letters, digits and underscores.
  • Write into one with df.write.mode("overwrite").saveAsTable("marketing.campaigns"). Without a schema prefix, the table lands in dbo.
  • Schema shortcuts map a whole schema to another lakehouse's schema or an ADLS Gen2 folder.
  • Cross-workspace Spark SQL uses the four-part name workspace.lakehouse.schema.table (three parts for a non-schema lakehouse).
  • Limitation: schema-enabled lakehouses cannot be shared through workspace-level sharing — expose them through shortcuts in a lakehouse the user can already reach.

Objective 2.3Ingest and transform streaming data

The Real-Time Intelligence stack

Discover
Real-Time hubCatalog of every stream and event source in the tenant.
Move
EventstreamNo-code ingest, transform and route.
Store
Eventhouse → KQL DBTime-series store, queried with KQL.
Analyze
KQL queryset · DashboardExploration and visualization.
Act
ActivatorRules, alerts, and triggering Fabric items.

Choosing a streaming engine

EventstreamSpark Structured StreamingKQL update policyDataflow Gen2
CodeNo-code canvas (SQL operator Preview)PySpark / ScalaKQLPower Query M
LatencySecondsSeconds to minutes (micro-batch)At ingestion timeMinutes (batch)
Transform richnessLow — filter, fields, aggregate, join, union, expandUnboundedMedium — any KQL over the incoming extentHigh but batch
PersonaIntegrator, analystData engineerKQL developerAnalyst
Pick it whenRouting many sources to many sinks with light shapingComplex joins, ML scoring, custom stateReshaping data as it lands in an EventhouseThe requirement is not really streaming

Eventstream

Sources

Azure

Event Hubs · IoT Hub · Event Grid · Service Bus · Blob Storage events · Azure Data Explorer Preview · IoT Operations

CDC

Azure SQL DB · Azure SQL MI · SQL Server on VM · PostgreSQL · MySQL · Cosmos DB · Oracle Preview · MongoDB Preview · Mirrored database change feed Preview

Messaging

Apache Kafka · Confluent Cloud · Amazon MSK · Amazon Kinesis · Google Cloud Pub/Sub · MQTT Preview · Solace PubSub+ Preview

Fabric-native

Workspace item events · OneLake events · Job events · Capacity events · Anomaly detection events Preview

Custom

Custom endpoint / custom app (Kafka-protocol connection string) · HTTP Preview

Sample data

Bicycles · Yellow Taxi · Stock market · Buses · Real-time weather — for demos and for exam labs

Transformation operators

OperatorDoes
FilterKeep events matching a condition (null checks, comparisons, by field data type)
Manage fieldsAdd, remove, rename fields; change data types
AggregateSum / min / max / average over a time window
Group byAggregations across events in a time window, grouped by one or more fields, with the full set of window types
UnionCombine two or more streams with matching field names and types; non-matching fields are dropped
ExpandOne row per element of an array
JoinCombine two streams on a matching condition
SQL operator PreviewCode-first SQL for windowing, joins and advanced aggregation

Destinations

  • Eventhouse — two modes: direct ingestion (fastest path, raw events straight into a KQL table) or event processing before ingestion (apply operators first).
  • Lakehouse — writes Delta. Input format JSON, Avro or CSV. Tune Minimum rows (1 – 2,000,000) and Maximum duration (1 minute – 2 hours): fewer rows or shorter duration ⇒ more small files.
  • Derived stream — the transformed stream itself, re-published so several destinations (and the Real-Time hub) can consume it. Supports pause/resume.
  • Activator — for rules and alerts.
  • Custom endpoint — external apps read it over the Kafka protocol.
  • Spark notebook Preview — hand events to a Structured Streaming job.
Eventstream limits and the Lakehouse schema trap
  • Max message size 1 MB; max retention 90 days; delivery guarantee is at least once; F4 or larger is the recommended capacity.
  • The Lakehouse destination applies schema enforcement based on the first record. Extra columns in later events are dropped, missing columns become NULL, and a record with no overlap at all fails schema conversion. Do not point a schema-drifting source (like database CDC) straight at a Lakehouse destination — use an Eventhouse, or DeltaFlow.
  • DeltaFlow Preview flattens nested Debezium CDC JSON into a tabular schema, registers it in the Fabric schema registry, auto-creates destination tables and handles schema evolution.

Eventhouse, KQL databases and OneLake

  • An Eventhouse is a container holding one or more KQL databases that share its capacity and resources. Each database gets an embedded KQL queryset.
  • By default the service suspends when idle and reactivates in a few seconds. The capacity planner lets you set a 7-day recurring schedule of 60-minute blocks with a minimum CU per block (default minimum 2 CU) plus autoscale above it — the answer when a question says "queries must never pay a cold-start penalty during business hours."
  • Data is indexed and partitioned by arrival time, which is why time-filtered KQL is so fast.

OneLake availability — "one logical copy"

Enable it at database or table level (with an optional backfill of existing tables) and the KQL data is also materialized as Delta in OneLake, readable by Spark, the SQL endpoint, Warehouse, Lakehouse and Direct Lake — with no extra storage cost. The database's retention policy governs the OneLake copy too.

KQL · tuning and monitoring the mirroring policy
// Default: batch until ~200–256 MB Parquet files or up to 3 hours. Range: 5 min – 3 h.
.alter-merge table Telemetry policy mirroring dataformat=parquet
    with (IsEnabled=true, TargetLatencyInMinutes=5)

// Latency of 00:00:00 means everything is in OneLake
.show table mirroring operations
What OneLake availability forbids

While it is enabled you cannot rename tables, change a column's type, apply row-level security, or delete / truncate / purge data. Disable it, do the work, re-enable it. And lowering TargetLatencyInMinutes creates many small files and degrades read performance — the classic wrong answer to "queries got slower after we reduced latency."

Native tables vs. OneLake shortcuts vs. query acceleration

Native KQL tableOneLake shortcutShortcut + query acceleration
Where the data livesInside the EventhouseExternally, referencedExternally, with a cached hot window
PerformanceBestLowestNear-native for recent data
DuplicationYesNoneCache only
Pick whenData is queried constantly and updated constantlyOccasional or ad-hoc access to external DeltaFrequent queries over recent external Delta data

Query acceleration caches shortcut data within a configurable window measured in days (inherited from the parent database by default), based on modificationTime in the Delta log. Enable it at shortcut creation (Accelerate toggle) or afterwards via Manage → Data policies → Query acceleration. It works on Delta tables only, requires workspace Admin / Member / Contributor, and for compliance you want the Eventhouse in the same region as the data.

Processing data with KQL

KQL · operator reference
// ---- filtering and shaping ----
| where Level == "Error" and Timestamp between (ago(1d) .. now())
| where Message has "timeout"        // 'has' = indexed term match, FAST
| where Message contains "time"     // substring scan, SLOW — know the difference
| take 10  /  | limit 10            // arbitrary rows, no ordering guarantee
| top 10 by Duration desc          // ordered
| project Timestamp, DeviceId, Duration
| project-away RawPayload
| project-rename ts = Timestamp
| extend Minutes = Duration / 1m
| distinct DeviceId
| sort by Timestamp desc

// ---- aggregation ----
| summarize count(), dcount(DeviceId), sum(Bytes), avg(Latency),
            min(Timestamp), max(Timestamp),
            percentile(Latency, 95), percentiles(Latency, 50, 90, 99),
            make_list(EventId), make_set(Region),
            arg_max(Timestamp, *),        // the whole latest row per group
            arg_min(Timestamp, Status),
            take_any(*)
          by Region, bin(Timestamp, 5m)

// ---- time series ----
| make-series Total = sum(Bytes) default=0
    on Timestamp from ago(7d) to now() step 1h by DeviceId
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total)
| render timechart

// ---- joins ----
Devices
| join kind=leftouter (Telemetry | summarize LastSeen = max(Timestamp) by DeviceId)
     on DeviceId
| join kind=inner hint.strategy=broadcast (SmallLookup) on $left.Id == $right.Key
| lookup (DimDevice) on DeviceId        // optimized left-outer against a small dim
| union withsource=SourceTable Errors, Warnings

// ---- semi-structured ----
| extend p = parse_json(Payload)
| extend City = tostring(p.location.city), Temp = todouble(p.temp)
| mv-expand tag = p.tags to typeof(string)
| parse Message with "user=" User " action=" Action

// ---- utilities ----
let threshold = 500;
let hot = materialize(Telemetry | where Timestamp > ago(1h));   // cache a reused subquery
let demo = datatable(Id:int, Name:string) [1, "a", 2, "b"];
| extend Ingested = ingestion_time()      // when Kusto received it, vs. event time
| serialize | extend Delta = Value - prev(Value, 1)

Join kinds — the whole table

KindReturnsOutput columns
innerunique (default!)Left rows deduplicated on the join key, matched to rightBoth sides
innerStandard inner join, no dedupBoth sides
leftouter / rightouterAll rows from that side, nulls where unmatchedBoth sides
fullouterAll rows from both sidesBoth sides
leftsemi / rightsemiRows from that side that have a matchThat side only
leftanti / rightantiRows from that side that have no matchThat side only
Two KQL facts that cost people marks

1. The default join kind is innerunique, not inner. It silently de-duplicates the left side, so counts come out lower than expected. Always state kind=inner when you mean a true inner join.
2. Put the smaller table on the left of a join for better performance — the opposite of the SQL habit.

Update policies and materialized views

KQL · update policy (transform at ingestion)
.create table RawLogs (OriginalRecord:string)
.create table ParsedLogs (Timestamp:datetime, ThreadId:int, Message:string)

.create function ExtractLogs() {
    RawLogs
    | parse OriginalRecord with "[" Timestamp:datetime "] [ThreadId:" ThreadId:int "] " Message:string
    | project-away OriginalRecord
}

.alter table ParsedLogs policy update
@'[{ "IsEnabled": true,
    "Source": "RawLogs",
    "Query": "ExtractLogs()",
    "IsTransactional": true,
    "PropagateIngestionProperties": false }]'

// Discard the raw copy once transformed
.alter-merge table RawLogs policy retention softdelete = 0s
PropertyMeaning
IsEnabledOn/off
SourceThe table whose ingestion fires the policy
SourceIsWildCardTreat Source as a pattern (SourceTable*); the function then uses $source_table
QueryThe transformation, usually a stored function
IsTransactionaltrue ⇒ a policy failure fails the source ingestion too. Default false, which means bad data lands in the source table only
PropagateIngestionPropertiesCarry extent tags and creation time to the target
ManagedIdentityRequired if the query reads tables in another database

Update policies fire on .ingest, .set, .append, .set-or-append, .set-or-replace, .move extents and .replace extents. They cannot do cross-cluster queries, callouts, or use database()/cluster() qualified names. Inspect failures with .show ingestion failures | where OriginatesFromUpdatePolicy == true.

Update policy vs. materialized view: an update policy transforms at ingestion time and writes to a second table (good for parsing, splitting, filtering). A materialized view (.create materialized-view) maintains an incrementally updated aggregation over a source table (good for summarize/arg_max rollups you query constantly).

Spark Structured Streaming

PySpark · read a stream, write to a Delta table
from pyspark.sql import functions as F
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, TimestampType

schema = StructType([
    StructField("deviceId", StringType(),   False),
    StructField("temp",     DoubleType(),   True),
    StructField("eventTime",TimestampType(),True)])

raw = (spark.readStream.format("eventhubs").options(**ehConf).load())

parsed = (raw
    .withColumn("body", F.col("body").cast("string"))
    .select(F.from_json("body", schema).alias("e"))
    .select("e.*"))

query = (parsed
    .repartition(48)                                # match available cores
    .writeStream
    .format("delta")
    .option("checkpointLocation", "Files/checkpoints/telemetry")
    .outputMode("append")
    .partitionBy("deviceId")
    .trigger(processingTime="1 minute")
    .toTable("silver.telemetry"))
PySpark · watermarks, windows and upserts
# Watermark = how long to wait for late events before dropping them and closing state
windowed = (parsed
    .withWatermark("eventTime", "10 minutes")
    .groupBy(F.window("eventTime", "5 minutes"), "deviceId")   # TUMBLING
    .agg(F.avg("temp").alias("AvgTemp"), F.count("*").alias("Readings")))

# HOPPING / sliding: windowDuration then slideDuration
F.window("eventTime", "10 minutes", "5 minutes")

# SESSION: gap timeout
F.session_window("eventTime", "5 minutes")

# Upsert into a Delta table from a stream
def upsert(batch_df, batch_id):
    batch_df.createOrReplaceTempView("updates")
    batch_df.sparkSession.sql("""
        MERGE INTO silver.telemetry AS t
        USING updates AS u ON t.deviceId = u.deviceId AND t.eventTime = u.eventTime
        WHEN MATCHED THEN UPDATE SET *
        WHEN NOT MATCHED THEN INSERT *""")

(parsed.writeStream
    .foreachBatch(upsert)
    .option("checkpointLocation", "Files/checkpoints/upsert")
    .trigger(availableNow=True)      # process all available data, then stop
    .start())
SettingOptionsNotes
Output modeappend · update · completeappend for Delta sinks. complete rewrites the whole result each batch — aggregations only.
TriggerprocessingTime="1 minute" · availableNow=True · once=True · continuousavailableNow is the modern "batch-up-the-backlog then stop" trigger — ideal for scheduled micro-batch pipelines.
CheckpointcheckpointLocationMandatory. Holds offsets and state; deleting it re-processes from scratch. One checkpoint per query.
Optimize writespark.databricks.delta.optimizeWrite.enabled = TrueMerges/splits partitions on write so you do not have to repartition() by hand.

For production streaming use a Spark job definition with a retry policy rather than a notebook, and monitor it on the Structured Streaming tab of the monitoring hub (Input rate, Process rate, Input rows, Batch duration, Operation duration).

Windowing functions — one table, three engines

WindowBehaviorOverlapEventstream / SQLSparkKQL
TumblingFixed, contiguous, non-overlapping segmentsNoTumblingWindow(second, 10)window("ts","10 seconds")summarize … by bin(ts, 10s)
HoppingFixed size, advancing by a hop; an event can land in several windowsYesHoppingWindow(second, 10, 5)window("ts","10 seconds","5 seconds")range + mv-expand, or make-series
SlidingEmits only when the window content changes (an event enters or leaves)YesSlidingWindow(second, 10)Approximated with a small hopseries_fir() over a series
SessionGrows while events keep arriving; closes after a gap timeout or a max durationNoSessionWindow(second, 5, 10)session_window("ts","5 minutes")scan operator
SnapshotGroups events sharing the exact same timestampNoGROUP BY System.Timestamp()groupBy("ts")summarize … by ts
Three rules that answer most windowing questions
  • Every window emits its result at the end of the window.
  • A hopping window whose hop size equals its window size is a tumbling window.
  • "Report the average every minute over the last five minutes" = hopping (size 5 min, hop 1 min). "Report the average for each five-minute block" = tumbling.

Activator

  • Objects are formed by grouping events on an object key (device ID, account ID). Rules are then evaluated per object instance.
  • Stateless rules judge each event alone and fire in subseconds. Stateful rules keep memory per object: BECOMES, INCREASES/DECREASES, EXIT RANGE, heartbeat (absence of data), and aggregations over a lookback window.
  • Rules fire on entry into a new state, which is what suppresses repeated alerts.
  • Sources: Eventstream, Fabric workspace item events, Azure Blob events, Real-Time dashboards, Power BI reports (periodic observations tied to the refresh schedule), SQL query rules over a Warehouse Preview.
  • Actions: email, Teams message, Power Automate flow — and Fabric items: pipeline, notebook, Spark job definition, Dataflow, Copy job, user data function.
Domain 3 · 30–35% of the exam

Monitor and optimize an analytics solution

The domain candidates under-prepare, and it is worth exactly as much as the other two. Three objective groups: monitoring Fabric items, identifying and resolving errors, and optimizing performance across six different engines.

Objective 3.1Monitor Fabric items

The monitoring surfaces, and which one to reach for

SurfaceScopeUse it for
Monitoring hubAll items you can access, tenant-wide"What ran, when, and did it succeed?" One place for pipelines, notebooks, Dataflows, Spark jobs, Copy jobs, semantic models, Lakehouse maintenance and more
Item Recent runsOne itemRun history for that item only
Capacity Metrics appOne capacityCU consumption, bursting, overages, throttling, which operation caused them
Workspace monitoringOne workspaceDetailed diagnostic logs and metrics in a queryable Eventhouse (30 days)
Admin monitoring workspaceTenantFeature usage and adoption, tenant-level activity
Purview / unified audit logTenantWho did what — compliance and security auditing

Monitoring hub details: shows the 100 most recent activities per item type from the past 30 days. Filter by status, item type, start time, submitted-by and location; search by name; sort and rearrange columns. Per row you can open a details pane (status, start time, duration, error detail), open Historical runs for the full 30-day history of that item, and configure schedule failure notifications Preview (needs Contributor or Write on the item). Dataflow Gen1 does not appear.

Monitor data ingestion

  • Pipelines — run history plus per-activity runs. The Copy activity's output carries rowsRead, rowsCopied, rowsSkipped, throughput, dataConsistencyVerification, and duration broken into queue/transfer time.
  • Copy job — its own real-time dashboard: per-table status and progress, run history and failure alerts; also surfaces in workspace monitoring.
  • Eventstream — node status, throughput metrics and error metrics in the live view and in workspace monitoring.
  • Eventhouse ingestion.show ingestion failures is the single most important command; the Eventhouse System overview page shows ingestion rate, top ingested databases, storage, compute usage and schema changes.
  • Mirroring — replication status and per-table row counts on the mirrored database's monitoring page; a paused capacity stops replication.

Monitor data transformation

  • Spark — five entry points: the monitoring hub, item Recent runs, in-notebook contextual monitoring (per-cell job progress, tasks, executors, logs), Spark job definition inline monitoring, and pipeline Spark-activity deep links. Behind them sit the Spark Advisor (real-time code and error advice), the extended Apache Spark History Server, and notebook snapshots that capture the exact code and output of a run.
  • Dataflow Gen2 — refresh history with duration and per-query performance, integrated into the monitoring hub.
  • Warehouse — Query Insights views plus live DMVs:
T-SQL · Query Insights and DMVs
-- Retained 30 days; up to ~15 minutes latency; user queries only, system queries excluded
SELECT TOP 100 distributed_statement_id, query_hash, allocated_cpu_time_ms, label, command
FROM   queryinsights.exec_requests_history
ORDER BY allocated_cpu_time_ms DESC;

-- Cold-start detection: nonzero remote-storage scan means it was NOT cached
SELECT distributed_statement_id, query_hash,
       data_scanned_remote_storage_mb, data_scanned_memory_mb, data_scanned_disk_mb, command
FROM   queryinsights.exec_requests_history
ORDER BY data_scanned_remote_storage_mb DESC;

SELECT * FROM queryinsights.long_running_queries    ORDER BY median_total_elapsed_time_ms DESC;
SELECT * FROM queryinsights.frequently_run_queries  ORDER BY number_of_successful_runs   DESC;
SELECT * FROM queryinsights.exec_sessions_history;
SELECT * FROM queryinsights.sql_pool_insights;       -- resource allocation & pool pressure

-- Live state (right now, not history)
SELECT * FROM sys.dm_exec_requests;
SELECT * FROM sys.dm_exec_sessions;
SELECT * FROM sys.dm_exec_connections;
Tag your queries with OPTION (LABEL)

Add OPTION (LABEL = 'nightly_fact_load') to your ETL statements. The label lands in queryinsights, so you can filter run history to one pipeline step. Queries with the same shape (same structure, different predicates) are aggregated together in the insight views.

Monitor semantic model refresh

  • Import and DirectQuery models: refresh history on the model, plus a Semantic model refresh activity you can chain at the end of a pipeline.
  • Direct Lake models do not "refresh" data — they frame (a metadata-only operation taking seconds that points the model at the newest Delta files) and load column segments into memory on demand (transcoding). What you monitor is framing success and whether queries have fallen back to DirectQuery.

Capacity monitoring and alerts

  • Capacity Metrics app — the Compute page shows CU seconds by operation, split into interactive and background; the timepoint detail drills into a single 30-second slot to name the culprit operation; the Overages tab charts carryforward, cumulative usage and burndown; the system events table records throttling episodes; Minutes to burndown estimates recovery.
  • Alerts — Activator rules on an Eventstream, KQL queryset or Real-Time dashboard; alerts on a Power BI visual; pipeline failure notification via the Outlook or Teams activity, or the built-in schedule failure notifications; capacity notifications configured by the capacity admin.

Objective 3.2Identify and resolve errors

Treat this section as a symptom → cause → fix lookup. The exam phrases these as "a job fails with X — what should you do first?"

Pipeline errors

SymptomLikely causeFix
Activity fails with a connector error codeCredentials expired, firewall, wrong pathRead ErrorCode, Message and failureType in the activity output; test the connection; check gateway status
Downstream activities run even though an upstream one failedDependency arrow set to On completion instead of On successFix the dependency condition
Pipeline "succeeds" but nothing loadedAll activities on On skip/On completion paths, or a ForEach over an empty arrayAdd a Lookup row-count check plus a Fail activity to make emptiness an explicit error
Transient network failuresNo retry configuredSet Retry and Retry interval on the activity's General tab
Long-running activity hangsDefault timeout too generousSet an explicit Timeout
Error message not capturedOn the failure path, log @activity('Copy1').error.message and @pipeline().RunId to a table

Dataflow Gen2 errors

  • Start at Refresh history → the failed refresh → per-query error detail.
  • Staging failures — the internal DataflowsStagingLakehouse / DataflowsStagingWarehouse items. If staging is failing, check capacity throttling before anything else.
  • Data destination errors — schema mismatch between the query output and the existing destination table, or a type the destination cannot accept. Check the destination's fixed vs. dynamic schema setting.
  • Gateway errors — on-premises gateway offline, out of date, or missing the driver for that source.
  • Evaluation errors — an M step failing on real data (nulls, unexpected types). Fix with try … otherwise or an explicit type conversion step.
  • Query folding broken — a step that cannot fold pulls everything into the mashup engine and the refresh crawls. Right-click a step → View native query; move unfoldable steps to the end.

Notebook and Spark errors

ErrorMeaningFix
HTTP 430 TooManyRequestsForCapacityNo Spark vCores left in the capacity (including burst)Cancel an active job in the monitoring hub, wait for the queue, use a smaller pool, or scale the SKU. Interactive jobs are rejected; background jobs queue
Session fails to start / Livy errorCapacity exhausted, or an environment publish failedCheck capacity; re-publish the environment; check Private Link VNet provisioning (10–15 min on first job)
Executor OOM / Java heap spaceSkewed join, huge collect(), too few partitionsBigger node size, broadcast the small side, salt the skewed key, increase spark.sql.shuffle.partitions, never collect() a large frame
Py4JJavaErrorThe Python wrapper surfacing a JVM exceptionRead past the Python trace to the Java Caused by line
ModuleNotFoundError after publishLibrary installed at session scope, not in the EnvironmentAdd it to the Environment's library list and publish; use Full mode for scheduled jobs
Concurrent-write / ConcurrentAppendExceptionTwo jobs writing the same Delta tablePartition the writes with replaceWhere, serialize them, or retry

Eventhouse and Eventstream errors

  • .show ingestion failures — the first command in any Eventhouse investigation. Common reasons: schema mismatch, a missing or wrong ingestion mapping, malformed JSON/CSV, and throttling.
  • Add | where OriginatesFromUpdatePolicy == true to isolate failures caused by an update policy.
  • If a transactional update policy is failing, the source ingestion fails too — set IsTransactional = false if partial success is acceptable.
  • Eventstream: source connectivity (credentials, firewall, consumer group already in use), destination write failures, and schema drift against a Lakehouse destination's first-record schema. Check node status and error metrics in the live view.

T-SQL and SQL analytics endpoint errors

  • Unsupported T-SQL — triggers, materialized views, synonyms, recursive CTEs, SET TRANSACTION ISOLATION LEVEL. Migrated code fails here first.
  • A new Lakehouse table is not visible in the SQL endpoint — the endpoint's metadata sync is asynchronous. Refresh the endpoint metadata (or use the Refresh SQL endpoint pipeline activity after the load step). This is a very common exam scenario.
  • Non-scalable operation warning — a global TOP/ORDER BY forced single-node execution. Add OPTION (FORCE DISTRIBUTED PLAN) or restructure.
  • Locking / conflict errors — long-open explicit transactions. Keep transactions short and batch-oriented; retry with exponential backoff.
  • Constraint violations that never fire — remember constraints are NOT ENFORCED; duplicates are your ETL's fault, not the engine's.

OneLake shortcut errors

  • Expired or revoked credentials on the cloud connection — the most frequent cause of a shortcut that "worked yesterday." Recreate or update the connection.
  • Permission errors on internal shortcuts — authorization uses the calling user's identity; they need read permission on the target item, not just on the shortcut.
  • Direct Lake over SQL, or T-SQL in delegated identity mode, passes the calling item owner's identity instead of the user's. Switch to Direct Lake over OneLake, or T-SQL in user-identity mode.
  • Table not recognized — a shortcut in Tables/ that is not a valid Delta table, or has a space in its name, or sits in a subdirectory.
  • Chaining limit — more than 5 levels of shortcut-to-shortcut.
  • Cross-region latency — reduce it with shortcut caching (retention 1–28 days; files over 1 GB are not cached).
  • Firewalled ADLS Gen2 — needs workspace identity plus trusted workspace access, or a managed private endpoint.

Objective 3.3Optimize performance

Optimize a Lakehouse table

Almost every Lakehouse performance problem is the small file problem: streaming or frequent micro-batch writes produce thousands of tiny Parquet files, and metadata overhead swamps the read.

OperationWhat it doesNotes
OPTIMIZEBin-compaction: merges small Parquet files into larger onesRun after heavy ingestion or many updates
V-OrderWrite-time sorting, encoding and compression of the Parquet layout~15% slower writes, up to 50% more compression and much faster reads. Still 100% open-source-Parquet compliant
VACUUMDeletes unreferenced files older than the retention thresholdDefault retention 7 days. Shorter intervals are refused unless spark.databricks.delta.retentionDurationCheck.enabled = false; shortening it destroys time-travel history and can break concurrent readers
ZORDER BYCo-locates related values so file skipping works on high-cardinality filter columnsPair with partitioning on a low-cardinality column
Merge transactions / deletion vectors cleanupFolds deletion-vector files back into the Parquet dataAvailable in the maintenance dialog
Delta maintenance and V-Order control
-- Table maintenance from Spark SQL
OPTIMIZE silver.orders ZORDER BY (CustomerId, OrderDate);
VACUUM   silver.orders RETAIN 168 HOURS;   -- 168 h = the 7-day default
DESCRIBE HISTORY silver.orders;

-- V-Order, three levels of control
SET spark.sql.parquet.vorder.default;                 -- inspect (session default: false)
SET spark.sql.parquet.vorder.default = TRUE;          -- session
ALTER TABLE person SET TBLPROPERTIES("delta.parquet.vorder.enabled" = "true");  -- table
PySpark · per-write V-Order and write tuning
(df.write.format("delta").mode("overwrite")
   .option("replaceWhere", "start_date >= '2026-01-01' AND end_date <= '2026-01-31'")
   .option("parquet.vorder.enabled", "true")
   .saveAsTable("myschema.mytable"))

spark.conf.set("spark.databricks.delta.optimizeWrite.enabled", True)   # right-size files on write
spark.conf.set("spark.databricks.delta.stats.collect", True)          # stats for file skipping
V-Order defaults differ by workload

Warehouse: V-Order is applied automatically to the Parquet it produces, whatever the ingestion method. Disable it only for a purely write-intensive warehouse — and note it cannot be re-enabled once disabled at the warehouse level.
Spark / Lakehouse: V-Order is off by default in new workspaces so writes stay fast. Turn it on for read-heavy gold tables, or use a read-heavy resource profile. OPTIMIZE applies it as part of maintenance.

Partitioning guidance: partition only tables large enough to justify it, on a low-cardinality column (year, month, region), aiming for partitions in the hundreds-of-MB-to-GB range. Over-partitioning recreates the small file problem. Use ZORDER, not partitioning, for high-selectivity columns. Run maintenance ad hoc from the Lakehouse explorer (right-click table → Maintenance), or schedule it with the Lakehouse maintenance pipeline activity Preview. Track it in the monitoring hub under activity names containing TableMaintenance. OPTIMIZE and VACUUM apply to Delta tables only — not Hive Parquet, ORC, AVRO or CSV.

Optimize a pipeline

  • Raise the Copy activity's degree of copy parallelism; enable staging when the direct path is slow.
  • In a ForEach, turn Sequential off and set Batch count (max 50) to control concurrency.
  • Push filtering to the source (a query rather than a whole-table read) so less data crosses the wire.
  • Prefer Copy job over hand-built incremental logic when the pattern is standard — it partitions and parallelizes automatically.
  • Never loop row-by-row. Replace an inner ForEach that inserts single rows with a bulk copy or a stored procedure.
  • Match the compute to the job — do not spin up a Spark session for a file move.

Optimize a data warehouse

LeverGuidance
StatisticsFabric maintains histogram, average-column-length and cardinality statistics automatically. You can still CREATE STATISTICS / UPDATE STATISTICS manually in a maintenance window. Multi-column statistics cannot be created manually.
Data typesPrefer smallint/int/bigint over decimal for whole numbers. Never write bare decimal (it becomes decimal(18,0), 9 bytes/row). Size varchar(n) to the data — avoid varchar(8000) and varchar(max). Use date/time/datetime2, never strings for dates. Declare NOT NULL where you can.
Type parityKeep types identical on both sides of JOIN and WHERE comparisons to avoid implicit conversions.
Cold startThe first run pays for loading data from OneLake into memory plus auto-statistics. data_scanned_remote_storage_mb = 0 means fully cached — the ideal steady state.
IngestionCOPY INTO in parallel; files ≥ 100 MB (never below 4 MB); batch transactions; avoid trickle singleton inserts.
Query shapeProject only the columns you need; filter early; avoid SELECT *; prefer TRUNCATE+CTAS over large UPDATE/DELETE.
TransactionsSnapshot isolation, ACID. Keep them short — rollback is cheap (Parquet version revert) but long transactions hold locks. Monitor sys.dm_tran_locks.
Plan controlOPTION (FORCE DISTRIBUTED PLAN) when you get a non-scalable-operation warning.
ModelA star schema outperforms a wide denormalized table for BI workloads.

Optimize Eventstreams and Eventhouses

  • Ingestion batching vs. streaming ingestion policy — batching (the default) trades a little latency for far better file sizes and query performance; streaming ingestion gives sub-second visibility at higher cost. Choose per table.
  • Caching (hot) policy — the amount of recent data held on the cluster's SSD. Widening the hot window speeds up queries and costs more; narrowing it does the reverse. Set it to cover the period people actually query.
  • Retention policy — how long data survives at all. Retention must be ≥ the hot window to be meaningful.
  • Update policies for ingestion-time reshaping; materialized views for incrementally maintained aggregations that queries hit repeatedly.
  • Partitioning policy — only for specific high-cardinality or ingest-time-scrambled patterns; it is not a default.
  • Eventstream — size the capacity (F4+), keep the Lakehouse destination's Minimum rows and Maximum duration high enough to avoid small files, and run Optimize table in notebook on streaming Delta targets.
  • Do not lower TargetLatencyInMinutes on the mirroring policy below what you need — small files hurt every downstream reader.

Optimize Spark performance

Fabric-specific accelerators

  • Native Execution Engine — a vectorized C++ engine that runs Spark operations natively; large speedups with no code change.
  • Intelligent Cache — automatic node-local caching of frequently read Delta/Parquet/CSV files.
  • Autotune — ML-driven per-query tuning of shuffle partitions, broadcast thresholds and join preferences.
  • Adaptive Query Execution — re-plans at runtime using actual statistics.

Session and concurrency

  • Starter pools for 5–10 s startup; custom live pools when you need libraries pre-installed and still want ~5 s.
  • High concurrency mode to share one session across notebooks (and across notebook activities in a pipeline).
  • Dynamic executor allocation + autoscale instead of a fixed oversized pool.
  • Bursting (3×) is on by default; a capacity admin can disable job-level bursting so one job cannot monopolize the capacity.

Query-level tuning

  • Broadcast the small side of a join (F.broadcast(df)).
  • Handle skew by salting the hot key or letting AQE split it.
  • Tune spark.sql.shuffle.partitions to the data volume, not the default.
  • cache()/persist() a DataFrame reused several times — and unpersist() it.
  • Avoid Python UDFs; prefer built-in functions or pandas_udf.
  • Never collect() a large DataFrame to the driver.

Concurrency limits (core-based)

1 CU = 2 Spark vCores; default burst 3×. On F64: 128 base → 384 with burst, queue limit 64. Background jobs (pipeline-, scheduler-triggered, Spark job definitions) queue FIFO and expire after 24 hours; interactive notebook jobs are rejected with HTTP 430, never queued.

Optimize query performance (semantic layer)

Direct LakeImportDirectQuery
EngineVertiPaq, reading Delta directlyVertiPaq, on a copyDelegated to the source
RefreshFraming — metadata only, secondsFull data refresh, minutes to hoursNone
Data latencyLowAs stale as the last refreshLive
LicensingRequires Fabric capacityAny licenceAny licence
  • Direct Lake on OneLake reads any Fabric Delta tables, supports composite models and calculated columns, applies security at the semantic layer, and has no DirectQuery fallback.
  • Direct Lake on SQL reads Lakehouse/Warehouse tables and views, honours SQL RLS — and falls back to DirectQuery when it hits a non-materialized view or SQL-based granular access control. Fallback is controlled by the Direct Lake behavior property. Fallback is usually the reason a Direct Lake report "suddenly got slow."
  • Guardrails scale with the SKU — Parquet file count, row-group count, row count and memory. Exceeding memory is not a hard stop; it causes paging and degraded performance. F2–F8: 300 M rows, 10 GB, 3 GB memory. F64/P1: 1.5 B rows, unlimited size, 25 GB memory. F512/P4: 12 B rows, 200 GB memory.
  • The single best optimization for a Direct Lake model is well-maintained Delta tables: V-Order applied, files compacted, row-group counts under the guardrail.
  • Not supported in Direct Lake: complex column types (Binary, GUID), non-numeric floats, strings over 32,764 characters, user-defined hierarchies on Direct Lake tables, gateways, personal workspaces, and cross-region source workspaces.

Cheat sheets

Numbers worth memorizing

ValueWhat it applies to
1 CU = 2 Spark vCores; burst 3×Spark capacity sizing. F64 → 384 vCores
10 min / 60 min / 24 hThrottling stages: overage protection → interactive delay (20 s) → interactive rejection → background rejection
5–64 min · 24 hSmoothing windows: interactive · background
30 secondsA capacity timepoint (2,880 per day)
7 daysDefault VACUUM retention
30 daysQuery Insights retention · monitoring hub history · workspace monitoring retention
1–28 daysOneLake shortcut cache retention (files > 1 GB not cached)
100,000 / 10 / 5Shortcuts per item / per OneLake path / max chain depth
1 MB · 90 days · at least onceEventstream max message size · max retention · delivery guarantee
5 min – 3 hEventhouse OneLake mirroring TargetLatencyInMinutes range (targets 200–256 MB files)
1 – 2,000,000 rows · 1 min – 2 hEventstream Lakehouse destination minimum rows · maximum duration
2–10 stages (default 3)Deployment pipelines
20 schedules · 50 ForEach batch countPer pipeline · ForEach parallelism cap
1,000 / 1,000 / 10,000 / 1 MBVariable library: variables · value sets · total cells · item size
1 TB per CUFree mirroring storage (F64 → 64 TB)
20 minutesDefault Spark session expiration (pool deallocates 2 min later)
24 hoursSpark background job queue expiry
4 MB / 100 MB – 1 GBWarehouse ingestion: absolute minimum file size / recommended file size
700 / 1000Passing score

Decision flowcharts in words

"Where should this data live?"

Streaming/telemetry/logs, KQL people → Eventhouse. Full T-SQL DML and multi-table transactions → Warehouse. Spark, unstructured, ML → Lakehouse. OLTP application → SQL database in Fabric. It already exists in another lake → shortcut. It is an operational DB you want continuously → mirror.

"How should I get this data in?"

Continuous from a supported operational DB → Mirroring. Already in a lake → Shortcut. Scheduled bulk/incremental/CDC, no orchestration → Copy job. Needs orchestration or many activities → Pipeline + Copy activity. Analyst with Power Query skills → Dataflow Gen2. Complex/custom → Notebook. Event-driven → Eventstream.

"Why is it slow?"

Lakehouse → small files; run OPTIMIZE, check V-Order, check partitioning. Warehouse → cold cache, bad data types, stale statistics, single-node plan. Spark → skew, shuffle partitions, missing broadcast, session startup. Eventhouse → hot cache too small, no materialized view, filtering on an unindexed substring. Power BI → Direct Lake fallback or a guardrail breach.

"Who can see what?"

Whole workspace → workspace role. One item → item permission. Specific tables/folders for a Viewer → OneLake security role. Specific rows → RLS. Specific columns → CLS. Obscure a value for most users → dynamic data masking. Classify and protect on export → sensitivity label.

KQL vs. T-SQL vs. PySpark — same operation, three dialects

OperationKQLT-SQLPySpark
Filter| where x > 5WHERE x > 5.filter(F.col("x") > 5)
Select columns| project a, bSELECT a, b.select("a","b")
New column| extend c = a + bSELECT a + b AS c.withColumn("c", F.col("a")+F.col("b"))
Aggregate| summarize sum(x) by gGROUP BY g.groupBy("g").agg(F.sum("x"))
Top N| top 10 by x descSELECT TOP 10 … ORDER BY x DESC.orderBy(F.desc("x")).limit(10)
Distinct countdcount(x)COUNT(DISTINCT x)F.countDistinct("x")
Latest row per keysummarize arg_max(ts, *) by kROW_NUMBER() … WHERE rn = 1row_number().over(w)
Time bucketbin(ts, 1h)DATETRUNC(hour, ts)F.window("ts","1 hour")
Explode array| mv-expand tagsF.explode("tags")
Parse JSONparse_json(col)JSON_VALUE()F.from_json(col, schema)
Upsert.set-or-appendMERGEDeltaTable.merge()

A four-week study plan

Week 1
Fundamentals + Domain 2ACapacity, OneLake, item taxonomy, the store decision guide. Then loading patterns and dimensional modelling. Build a medallion lakehouse by hand.
Week 2
Domain 2B + 2CShortcuts, mirroring, Copy job, PySpark/T-SQL transformations. Then Eventstream → Eventhouse end to end, and a KQL drill every day.
Week 3
Domain 1Spark pools and environments, Git + deployment pipelines + variable libraries, the full security stack, orchestration patterns and expressions.
Week 4
Domain 3 + reviewMonitoring surfaces, the error tables, every optimization lever. Then re-walk the official skills outline and mark anything you cannot explain out loud.

The readiness checklist

Tick these off only when you can explain each one to another person without notes.

  • Domain 1 · Implement and manage
  • Configure Spark workspace settings — pools, node sizes, autoscale, dynamic allocation, high concurrency, environments
  • Configure domain, OneLake and Apache Airflow workspace settings
  • Configure version control (Git integration) and implement database projects
  • Create and configure deployment pipelines, deployment rules and variable libraries
  • Implement workspace-level and item-level access controls
  • Implement row-, column-, object- and folder/file-level access controls
  • Implement dynamic data masking
  • Apply sensitivity labels and endorse items
  • Implement and use Fabric audit logs and workspace monitoring
  • Configure and implement OneLake security roles
  • Choose between Dataflow Gen2, a pipeline and a notebook
  • Design and implement schedules and event-based triggers
  • Implement orchestration patterns with notebooks and pipelines, including parameters and dynamic expressions
  • Domain 2 · Ingest and transform
  • Design and implement full and incremental data loads
  • Prepare data for loading into a dimensional model (SCD 1/2/3, surrogate keys, inferred members)
  • Design and implement a loading pattern for streaming data
  • Choose an appropriate data store
  • Choose between Dataflows Gen2, notebooks, KQL and T-SQL for transformation
  • Create and manage OneLake shortcuts
  • Implement mirroring
  • Ingest data by using pipelines and Copy job
  • Transform data by using PySpark, SQL and KQL
  • Denormalize, group and aggregate data
  • Handle duplicate, missing and late-arriving data
  • Choose an appropriate streaming engine
  • Choose between native tables and OneLake shortcuts in Real-Time Intelligence
  • Choose between query acceleration and standard OneLake shortcuts
  • Process data by using Eventstreams, Spark Structured Streaming and KQL
  • Create windowing functions
  • Domain 3 · Monitor and optimize
  • Monitor data ingestion, data transformation and semantic model refresh
  • Configure alerts
  • Identify and resolve pipeline errors
  • Identify and resolve Dataflow Gen2 errors
  • Identify and resolve notebook errors
  • Identify and resolve Eventhouse errors
  • Identify and resolve Eventstream errors
  • Identify and resolve T-SQL errors
  • Identify and resolve OneLake shortcut errors
  • Optimize a Lakehouse table
  • Optimize a pipeline
  • Optimize a data warehouse
  • Optimize Eventstreams and Eventhouses
  • Optimize Spark performance
  • Optimize query performance
Two days before the exam

Stop reading and start doing. Build one thing end to end in a trial capacity: land a CSV in a Lakehouse, transform it with a notebook, load a small star schema into a Warehouse, stream sample data through an Eventstream into an Eventhouse, write three KQL queries against it, connect the workspace to Git, and promote it through a two-stage deployment pipeline. Everything you fumble in that hour is what to revise.