KQL en Microsoft Fabric: parse, extract y optimización de datos dinámicos
En el despliegue de soluciones analíticas dentro de Microsoft Fabric, uno de los cuellos de botella más comunes aparece cuando los datos no llegan en un esquema relacional limpio. En sectores como el retail o la industria, es habitual recibir telemetría, logs de servidores o trazas de aplicaciones en formatos de texto plano o JSON anidado. Intentar procesar estos volúmenes con herramientas tradicionales de ETL puede degradar la frescura de datos en Microsoft Fabric debido a la latencia que introduce el movimiento y la transformación pesada.
KQL (Kusto Query Language), el motor detrás de Real-Time Intelligence en Fabric, está diseñado precisamente para este escenario. Su capacidad para manejar columnas de tipo dynamic y realizar transformaciones al vuelo (query-time) permite una agilidad que el SQL tradicional difícilmente alcanza sin una infraestructura de pre-procesamiento costosa. Sin embargo, esta flexibilidad tiene un precio en términos de cómputo si no se aplican los operadores adecuados para cada volumen de datos.
Como consultor, he visto proyectos donde el uso indiscriminado de expresiones regulares o el desempaquetado de JSONs masivos agotaban los Capacity Units (CU) de la organización. El objetivo de este artículo es definir un criterio técnico claro: cuándo usar parse, cuándo recurrir a extract con Regex y cómo gestionar los tipos dinámicos sin comprometer la escalabilidad del sistema.
El tipo de datos dynamic: flexibilidad con responsabilidad
El tipo de datos dynamic en KQL es equivalente a un BLOB que puede contener un escalar, un array o un objeto JSON. A diferencia de un Warehouse tradicional donde cada columna debe estar tipada, KQL permite almacenar estructuras complejas y consultarlas directamente. Esto es fundamental para cumplir con estrategias para escalar Microsoft Fabric, ya que evita cambios constantes en el esquema (schema drift) cuando el origen de datos añade nuevos campos.
No obstante, el almacenamiento dynamic no es una carta blanca. Internamente, Kusto indexa las propiedades de estos objetos para acelerar las búsquedas, pero el acceso sigue siendo más costoso que el de una columna fuertemente tipada (int, long, datetime). La recomendación técnica es clara: si un campo del JSON se va a filtrar o agrupar en el 90% de las consultas, debe promocionarse a una columna física durante la ingesta. Si es un dato de diagnóstico o consulta ocasional, el tipo dynamic es su lugar ideal.
Operador parse: eficiencia para patrones conocidos
El operador parse es la herramienta de primera elección cuando trabajamos con cadenas de texto que siguen un patrón predecible pero no están estructuradas. Es extremadamente útil para logs de aplicaciones donde la estructura es fija (ej. Fecha – Nivel – Mensaje). Su principal ventaja es que es más rápido que las expresiones regulares porque utiliza un algoritmo de búsqueda de subcadenas más optimizado.
Un escenario típico es el procesamiento de logs de ventas de un terminal de punto de venta (TPV) antiguo que envía una cadena única. En lugar de llevar esto a Power Query y realizar múltiples pasos de «Dividir columna», KQL lo resuelve en una sola línea de ejecución paralela.
// Ejemplo de parseo de logs de ventas en retail
let LogsVentas = datatable(RawData:string)
[
"2023-10-01 10:15:00|Tienda_Madrid|Venta_OK|Importe:45.50",
"2023-10-01 10:16:10|Tienda_BCN|Venta_ERR|Importe:0.00",
"2023-10-01 10:18:22|Tienda_Sevilla|Venta_OK|Importe:120.30"
];
LogsVentas
| parse RawData with Fecha:datetime "|" Tienda:string "|" Estado:string "|Importe:" Importe:decimal
| where Estado == "Venta_OK"
| summarize Total = sum(Importe) by TiendaEn este ejemplo, parse extrae cuatro columnas tipadas en un solo paso. Es importante notar que si el patrón no coincide en alguna fila, los valores resultantes serán nulos para esa fila, pero la consulta no fallará. Para mejorar el rendimiento, siempre es preferible usar parse antes que cualquier operador de manipulación de strings manual.
Operador extract: potencia de Regex para casos complejos
Cuando la estructura del texto es inconsistente o el dato que buscamos puede aparecer en cualquier posición, parse se queda corto. Aquí entra extract, que permite usar expresiones regulares (RE2) para capturar grupos específicos. Aunque es más flexible, su coste computacional es significativamente mayor. En pensar como el motor de SQL Server y otros motores analíticos, entendemos que procesar Regex fila a fila es una operación intensiva de CPU.
He utilizado extract frecuentemente en el sector energético para capturar códigos de error embebidos en descripciones de alarmas enviadas por sensores IoT, donde el mensaje es un bloque de texto libre pero el código sigue un patrón como ERR-12345.
// Extracción de códigos de error mediante Regex
let AlarmasSensores = datatable(Descripcion:string)
[
"Fallo crítico en turbina, código ERR-9921, revisar presión",
"Aviso preventivo: temperatura elevada (ERR-1020)",
"Mantenimiento rutinario realizado sin incidencias"
];
AlarmasSensores
| extend CodigoError = extract(@"ERR-(\d+)", 1, Descripcion)
| where isnotempty(CodigoError)El parámetro 1 indica que queremos el primer grupo de captura de la expresión regular. Si el volumen de datos es de miles de millones de filas, aplicar extract en cada consulta penalizará la experiencia del usuario en Power BI. En esos casos, es preferible realizar esta extracción mediante una Update Policy en Kusto en el momento de la ingesta para persistir el CodigoError como una columna real.
Tratamiento de JSON y el plugin bag_unpack
El manejo de JSON es quizás la tarea más frecuente en Microsoft Fabric. KQL ofrece funciones como parse_json() para convertir un string en un objeto dinámico y el plugin bag_unpack para dinamizar (pivotar) las propiedades de un objeto en columnas independientes.
El error más común de un consultor junior es usar bag_unpack sobre tablas con millones de registros sin filtrar previamente. Este plugin debe calcular el esquema de salida en tiempo de ejecución, lo que puede ser extremadamente lento. Si conoces las claves del JSON, es mucho más eficiente usar el operador extend para extraer solo lo necesario.
| Método | Uso recomendado | Impacto en Rendimiento |
|---|---|---|
column.propiedad | Acceso directo a una clave conocida de un dynamic. | Mínimo. Muy eficiente. |
parse_json() | Convertir strings JSON a objetos dinámicos. | Medio. Necesario si el origen es string. |
evaluate bag_unpack() | Expandir todas las claves de un objeto a columnas. | Alto. Evitar en datasets masivos. |
mv-expand | Desanidar arrays (una fila por cada elemento del array). | Alto. Multiplica el número de filas. |
Al igual que ocurre al realizar pruebas de rendimiento en Fabric Warehouse, en KQL debemos medir el tiempo de respuesta y el uso de memoria. Si una consulta con bag_unpack tarda más de 5 segundos, es una señal inequívoca de que debemos estructurar los datos antes o limitar el alcance de la consulta con filtros temporales agresivos.
Escalabilidad y decisiones de diseño
Para mantener el rendimiento al escalar, la regla de oro es «filtrar primero, transformar después». KQL es excepcionalmente rápido filtrando por tiempo y por columnas indexadas. Si aplicas un parse o un extract sobre 100 millones de filas para luego filtrar por una fecha, estás desperdiciando recursos. Mueve siempre el filtro where a la parte superior de la consulta.
Otro aspecto crítico es la elección entre Ingestion-time transformation y Query-time transformation. En proyectos de alta escala, utilizamos funciones de actualización (Update Policies). Estas funciones actúan como disparadores que ejecutan una consulta KQL sobre cada lote de datos que entra y guardan el resultado en una segunda tabla estructurada. Esto permite que el usuario final consuma una tabla limpia, con tipos de datos correctos y sin necesidad de ejecutar lógicas de parseo complejas en cada informe de Power BI.
Errores comunes en la extracción de datos con KQL
- No tipar los resultados: Extraer un dato con
parsey dejarlo como string cuando es un número impide realizar cálculos agregados posteriormente sin untodouble()explícito. - Ignorar el Case-Sensitivity: KQL es sensible a mayúsculas. El operador
extracty las comparaciones de strings fallarán si no se tiene en cuenta. - Uso excesivo de mv-expand: Desanidar arrays de gran tamaño puede provocar errores de «Query exceeds memory limits».
- Parsear en cada nivel: Intentar parsear una cadena en varios pasos de
extenden lugar de usar un únicoparsecon todo el patrón.
Checklist para optimizar consultas de datos dinámicos
- ¿Has colocado los filtros de tiempo (
where Timestamp > ago(1d)) al principio de la consulta? - ¿Has preferido
parsesobreextractpara patrones fijos? - ¿Estás tipando las columnas extraídas directamente en el operador
parse? - ¿Has evaluado si el campo dinámico se consulta lo suficiente como para convertirlo en una columna física mediante una Update Policy?
- Si usas
bag_unpack, ¿has limitado previamente el conjunto de datos a las filas estrictamente necesarias?
Preguntas frecuentes
¿Cuándo es mejor usar parse-where en lugar de parse?
parse-where es una variante que filtra automáticamente las filas que no coinciden con el patrón especificado. Es ideal para limpiar datos ruidosos donde solo nos interesan las líneas que cumplen una estructura concreta, ahorrando un paso posterior de filtrado de nulos.
¿Cómo manejo JSONs con nombres de propiedades que contienen caracteres especiales?
En KQL, puedes acceder a propiedades con nombres complejos usando la sintaxis de corchetes: ColumnaDinamica["Propiedad con Espacios"]. Esto es vital cuando se integran datos de APIs externas que no siguen convenciones de nombrado estándar.
¿Es posible parsear datos dinámicos directamente en Power BI?
Aunque Power Query puede hacerlo, lo óptimo en términos de rendimiento es enviar la consulta KQL ya parseada desde Fabric. Esto aprovecha la potencia de cómputo distribuido de Kusto y envía a Power BI solo el conjunto de resultados estructurado y reducido.
¿Qué impacto tiene el desanidado de arrays (mv-expand) en la facturación de Fabric?
El impacto no es directo en la factura pero sí en el consumo de Capacity Units (CU). Una operación de mv-expand que multiplica por 10 el número de filas de una tabla de gran tamaño consumirá significativamente más memoria y CPU, lo que podría llevar a una limitación de recursos (throttling) si la capacidad es ajustada.





