Como expandir arrays com mv-expand em KQL: exemplo
Em tabelas de telemetria é muito comum encontrar colunas dinâmicas que guardam listas dentro de uma única célula: etiquetas de um evento, IDs de produtos de uma encomenda, códigos de erro. Expandir arrays com mv-expand em KQL transforma cada valor dessa lista numa linha própria, e é isso que permite a seguir filtrar, agrupar e contar como em qualquer tabela normal. Todos os exemplos abaixo podem ser copiados tal e qual para um KQL Queryset, porque criam os dados em memória.
Pré-requisitos
- Acesso a uma base KQL (Eventhouse no Microsoft Fabric) ou a um cluster de Azure Data Explorer.
- Um KQL Queryset ou a Web UI do Kusto para escrever consultas.
- Noções básicas dos operadores
where,projectesummarize. - Não é preciso carregar dados: usamos o operador
datatablepara criar tabelas de exemplo.
Passo 1: Criar dados de exemplo com datatable
Antes de expandir arrays em KQL, precisamos de uma tabela com uma coluna do tipo dynamic. O operador datatable cria uma tabela temporária, sem tocar na base de dados — perfeito para aprender e testar.
let Eventos = datatable(id:int, utilizador:string, etiquetas:dynamic)
[
1, "ana", dynamic(["bi", "fabric"]),
2, "rui", dynamic(["kql"]),
3, "maria", dynamic(["bi", "kql", "azure"])
];
Eventos
O resultado tem 3 linhas. Repare que a coluna etiquetas mostra a lista inteira numa só célula — e é exatamente por isso que não conseguimos, por agora, contar quantas vezes aparece cada etiqueta.
Passo 2: Expandir o array com mv-expand
O operador mv-expand (multi-value expand) pega numa coluna dinâmica e produz uma linha por cada elemento do array. As restantes colunas da linha original são repetidas em cada nova linha.
Eventos
| mv-expand etiquetas
Agora temos 6 linhas: 2 para a "ana", 1 para o "rui" e 3 para a "maria". A coluna etiquetas deixou de conter uma lista e passa a conter um único valor por linha.
Passo 3: Dar um nome e um tipo ao valor expandido
Por omissão, o valor expandido continua a ser do tipo dynamic, o que atrapalha comparações de texto e ordenações. A boa prática é atribuir um nome novo à coluna e converter o tipo com to typeof().
Eventos
| mv-expand etiqueta = etiquetas to typeof(string)
| project id, utilizador, etiqueta
A coluna etiqueta é agora uma string verdadeira. Isto permite escrever, por exemplo, | where etiqueta == "kql" sem surpresas.
Passo 4: Saber a posição de cada valor com with_itemindex
Às vezes a ordem dentro do array tem significado (o primeiro erro, a primeira categoria). A opção with_itemindex devolve o índice de cada elemento, começando em 0.
Eventos
| mv-expand with_itemindex = posicao etiqueta = etiquetas to typeof(string)
| project id, utilizador, posicao, etiqueta
| where posicao == 0
Este exemplo devolve apenas a primeira etiqueta de cada evento — uma forma simples de extrair o valor principal de uma lista.
Passo 5: Expandir um dicionário (property bag)
Uma coluna dynamic também pode guardar um objeto com pares chave/valor. Com kind=array, cada par é expandido para um pequeno array de dois elementos: na posição 0 fica a chave e na posição 1 fica o valor.
let Propriedades = datatable(id:int, props:dynamic)
[
1, dynamic({"pais": "PT", "plano": "pro"}),
2, dynamic({"pais": "ES", "plano": "free"})
];
Propriedades
| mv-expand kind = array props
| extend chave = tostring(props[0]), valor = tostring(props[1])
| project id, chave, valor
O resultado é uma tabela longa e estreita, com uma linha por propriedade — o formato ideal para depois pivotar ou filtrar por chave.
Passo 6: Agrupar depois de expandir
Com o array expandido, o summarize volta a ser trivial. Este é o objetivo real da maioria das consultas com mv-expand em KQL: contar quantas vezes cada valor aparece.
Eventos
| mv-expand etiqueta = etiquetas to typeof(string)
| summarize eventos = count() by etiqueta
| order by eventos desc
Deve obter bi e kql com 2 eventos cada, e fabric e azure com 1.
Erro comum: se a coluna for texto (por exemplo, um JSON guardado comostring), omv-expandnão a consegue expandir. Converta primeiro comextend lista = todynamic(coluna_texto)e só depois expanda.
Verificar o resultado
Confirme três coisas. Primeiro, o número de linhas: Eventos | mv-expand etiquetas | count deve devolver 6, ou seja, a soma dos tamanhos de todos os arrays. Segundo, o tipo da coluna: adicione | extend t = gettype(etiqueta) e confirme que aparece string (e não array). Terceiro, se estiver a trabalhar com arrays muito grandes, tenha presente que o mv-expand expande até 2048 valores por linha por omissão; pode ajustar esse máximo com a opção limit.
Conclusão
Com mv-expand, to typeof() e with_itemindex já consegue transformar qualquer coluna dinâmica numa tabela plana pronta a agregar. O passo natural a seguir é conhecer o mv-apply, que expande o array, aplica uma subconsulta a cada elemento e volta a agregar — muito útil, por exemplo, para ficar apenas com o maior valor de cada lista. Uma dica final: expanda o mais tarde possível na consulta, depois dos where, porque a expansão multiplica linhas e é aí que a performance se perde. Que colunas dinâmicas tem nos seus dados à espera de serem expandidas?