Trabajar con datos en tiempo real en un Eventhouse de Fabric
El motor Kusto por dentro: KQL databases, ingesta, optimización de queries, materialized views, stored functions, update policies y todas las policies que controlan el comportamiento. 🌸
IntermedioAvanzado
🎯
1. Objetivos y encaje en el DP-700
¿Qué te enseña este módulo?
Este es el módulo del motor Kusto por dentro. Vamos a profundizar en:
La estructura del Eventhouse.
Cómo escribir KQL efectivo y optimizado.
Features avanzadas: materialized views, stored functions, update policies.
Todas las policies que controlan el comportamiento de las KQL databases.
Los objetivos oficiales:
Crear un Eventhouse en Microsoft Fabric.
Escribir queries KQL efectivas.
Entender cómo usar materialized views y stored functions en una KQL database.
Optimización de queries, cache policy, retention, partitioning, materialized views para performance
🎯 Qué es CRÍTICO dominar:
Optimización de KQL (filter early, project early, orden de joins)
Materialized views vs stored functions vs update policies — cuándo cada uno
Todas las policies y su propósito (salen en preguntas de escenario)
Row-Level Security en KQL
Cache policy y retention policy (para performance y compliance)
🏛️
2. Anatomía del Eventhouse (repaso)
Recordatorio
Ya vimos el Eventhouse en el Módulo 8. Repaso rápido: un Eventhouse es un contenedor que hospeda una o más KQL databases.
Contenido de una KQL database
Cada KQL database puede contener:
Tables (donde viven los datos)
Stored procedures
Materialized views
Stored functions
Data streams
Shortcuts (a otras KQL databases o storage externo)
KQL queryset (embebido)
Con cada KQL database viene un KQL queryset embebido para escribir y ejecutar queries.
⚠️ Un Eventhouse ≠ una KQL database. Ojo cosita con esta distinción: el Eventhouse es el contenedor y la KQL database es el objeto donde viven los datos. Un Eventhouse puede tener varias KQL databases (para separar dominios, permisos, etc.).
💾
3. KQL databases: default y adicionales
Database default automática
Cuando creas un Eventhouse, se crea automáticamente una KQL database por defecto con el mismo nombre. Puedes usar esa o crear otras adicionales.
Por qué varias databases en un Eventhouse
Separar dominios: sales_db, iot_db, logs_db.
Permisos distintos: cada database con sus roles.
Retención distinta: unas con 30 días, otras con 5 años.
Compartir capacidad: mismo Eventhouse, distintas DBs.
Cómo crear una database adicional
Desde el Eventhouse:
Click en New database.
Elegir tipo: KQL database estándar (con sus propios datos) o database shortcut (referencia a otra database).
Configurar nombre y descripción.
📥
4. Ingesta de datos: todas las formas
Hay muchísimas formas de ingestar datos en una KQL database:
Local files
Sube ficheros directamente desde tu equipo: CSV, TSV, JSON, Parquet, Avro, ORC, etc. Ideal para pruebas o cargas puntuales.
Azure Storage
Azure Blob Storage con SAS o managed identity.
Azure Data Lake Storage Gen2.
Amazon S3
Directamente desde buckets S3. Ingesta multi-cloud sin intermediarios.
Fuentes de streaming
Azure Event Hubs
Azure IoT Hub
Fabric Eventstream (el más común en Fabric)
Descubrimiento desde el Real-Time Hub
OneLake
Ingestar desde otros items de Fabric vía OneLake.
Data Factory copy y Dataflows
Copy Data activity desde pipelines.
Dataflows Gen2 como destino.
Conectores externos
Apache Kafka
Confluent Cloud Kafka
Apache Flink
MQTT (Message Queuing Telemetry Transport)
Amazon Kinesis
Google Cloud Pub/Sub
Menú Get Data
Todo se accede desde el menú Get Data de la KQL database. El asistente de la UI te guía por: elegir origen, configurar la conexión, definir la tabla destino (crear una nueva o usar existente), mapear el schema y configurar una transformación opcional.
🎯 Comparativa rápida
Necesito...
Método
Carga puntual de un CSV
Local file upload
Ingesta continua de un stream
Eventstream o Event Hubs source
Batch periódico desde S3
Data Factory pipeline
Datos multi-cloud (Kafka on-prem)
Kafka connector
Datos de otro item de Fabric
OneLake ingestion
🔗
5. Database shortcuts
¿Qué son?
Los database shortcuts son referencias a KQL databases existentes en otros eventhouses o en databases de Azure Data Explorer. La idea: consultar datos de KQL databases externas como si estuvieran almacenados localmente, sin copiarlos realmente.
Casos de uso
Analítica cross-workspace: analizar datos de varios workspaces sin duplicar.
Migración gradual: exponer un ADX legacy en Fabric sin migrarlo.
Data mesh: cada dominio tiene su database y otros la consumen vía shortcuts.
Reducir costes de storage: no duplicar datos de acceso frecuente.
⚠️ Diferencia con los shortcuts de OneLake: los OneLake shortcuts (Módulo 1) apuntan a ficheros/carpetas (Delta, Parquet, CSV). Los database shortcuts (KQL) apuntan a KQL databases completas con todas sus tablas. Conceptos distintos, usos distintos.
🌊
6. OneLake availability (repaso profundo)
Recordatorio
Ya vimos la OneLake availability en el Módulo 8. Es la feature del Eventhouse que hace que los datos KQL estén también disponibles en OneLake en formato Delta Parquet, para que otros workloads de Fabric los consuman.
Activación
Se activa a nivel de database completa o a nivel de tabla individual, desde el editor del Eventhouse.
Adaptive Parquet file batching
Para evitar muchos ficheros pequeños, el Eventhouse hace un batching adaptativo.
Por defecto: hasta 3 horas de delay, o hasta alcanzar ficheros óptimos (200-256 MB).
Configurable: TargetLatencyInMinutes entre 5 minutos y 3 horas.
⚠️ Bajar mucho el delay → muchos ficheros pequeños → lecturas ineficientes en OneLake. Y la tabla es read-only una vez creada.
Integración cross-workload
Cuando activas la OneLake availability, otros workloads de Fabric pueden consumir los datos:
Power BI Direct Lake sobre las tablas KQL.
El Lakehouse puede crear shortcuts a las tablas KQL.
El Warehouse puede hacer joins con datos KQL.
Los notebooks pueden leerlas con Spark.
🎯 Cuándo activarla
SÍ activar: cuando necesitas integración cross-Fabric, reports de Power BI con datos KQL o data mesh entre workloads.
NO activar (default): si solo usas KQL para queries, sin integración externa, y quieres minimizar el overhead de escritura.
📝
7. KQL Queryset
Recordatorio
Cada KQL database viene con un queryset embebido. Es el espacio de trabajo para escribir queries.
Features avanzadas
Más allá de escribir queries, el queryset permite:
Guardar queries con nombre para uso futuro.
Multi-tab: varias pestañas de query simultáneas.
Compartir queries con otros usuarios para colaborar.
Visualizaciones: renderizar los resultados como gráficos inline.
Soportar KQL y T-SQL.
Visualizaciones en el queryset
Puedes convertir los resultados en bar charts, line charts, pie charts, column charts, time charts, cards y tablas. Se hace con el operador | render:
TaxiTrips
| summarize count() by taxi_id
| top 10 by count_
| render columnchart
Se visualiza directamente como column chart.
Querysets standalone
Además del queryset embebido, puedes crear querysets standalone en el workspace. Ventajas: consultar varias databases desde un único queryset y compartir queries entre usuarios independientemente de las databases.
🌸
8. KQL syntax fundamental (repaso profundo)
La filosofía de las pipes 🚰
KQL usa pipes (|) para encadenar operaciones. Los datos fluyen de izquierda a derecha y de arriba a abajo.
tabla
| operador1
| operador2
| operador3
Cada operador trabaja sobre los resultados del anterior. El orden importa mucho.
Interpretación: empieza con toda la tabla TaxiTrips, filtra a los trips con fare > 20 $, selecciona 3 columnas concretas y devuelve las 10 primeras filas que quedaron.
Query mínima: solo la tabla
TaxiTrips
Devuelve todas las columnas de la tabla, con el número de filas limitado por el default de la herramienta.
take para muestreo
TaxiTrips
| take 100
Devuelve 100 filas (sin orden garantizado). Útil para explorar la estructura sin procesar toda la tabla.
summarize para agregar
TaxiTrips
| summarize trip_count = count() by taxi_id
Cuenta los trips por taxi_id. Devuelve una tabla resumen con dos columnas: taxi_id y trip_count.
Los operadores más usados
Operador
Función
take
N filas (sin orden garantizado)
where
Filtrar filas por condición
project
Seleccionar columnas concretas
extend
Añadir columnas calculadas
summarize
Agregar (equivalente a GROUP BY)
sort by / order by
Ordenar
top
Top N con orden
limit
Limitar el output
join
Unir tablas
union
Combinar tablas
render
Visualizar
count
Contar filas
distinct
Valores únicos
Funciones temporales críticas
| where timestamp > ago(30min) // Últimos 30 min
| where timestamp > ago(1h) // Última hora
| where timestamp > ago(1d) // Último día
| where timestamp > ago(7d) // Últimos 7 días
| where timestamp between (datetime(2026-01-01) .. datetime(2026-01-31))
bin() para agrupar por bucket temporal:
| summarize count() by bin(timestamp, 5m)
// Agrupa por bucket de 5 minutos
También: now(), ago(x), startofday(), endofday(), startofweek() y startofmonth().
Operadores de string
| where name contains "kawaii" // Contains (case-insensitive)
| where name has "kawaii" // Búsqueda por palabra completa (más rápido)
| where name startswith "kaw" // Empieza por
| where name endswith "ii" // Termina en
| where name matches regex "^k.*i$" // Regex
has es más eficiente que contains porque busca palabras completas usando el índice.
🎯
9. Case sensitivity (CRÍTICO)
Este es un punto que pilla a MUCHA gente y cae en el examen.
⚠️ KQL es case-sensitive para TODO: nombres de tablas, nombres de columnas, nombres de funciones, operadores, keywords y valores de string. Todos los identificadores deben coincidir exactamente.
Ejemplos de errores comunes
-- ❌ ERROR: la tabla se llama TaxiTrips, no taxitrips
taxitrips
| take 10
-- ✅ CORRECTO
TaxiTrips
| take 10
-- ❌ ERROR: la columna es fare_amount, no Fare_Amount
TaxiTrips
| where Fare_Amount > 20
-- ✅ CORRECTO
TaxiTrips
| where fare_amount > 20
Los valores de string también
-- Estos son valores DISTINTOS
| where vendor_id == "VTS" -- Match exacto
| where vendor_id == "vts" -- No hace match (valor distinto)
Si necesitas un matching case-insensitive:
| where vendor_id =~ "vts" -- =~ es igualdad case-insensitive
Consecuencia práctica
Sé muy consistente con el naming de tus schemas. Si defines una tabla como TaxiTrips, refiérete siempre a ella como TaxiTrips.
⚡
10. Best practices de queries
Por qué importa la optimización
El rendimiento en las KQL databases depende de la cantidad de datos procesados. Entender cómo KQL procesa los datos te permite escribir queries que:
⚡ Van más rápido: reducen los datos escaneados (millones → miles).
💰 Usan menos recursos: seleccionar 3 columnas en vez de 50 reduce el overhead.
📈 Funcionan bien conforme crecen los datos: una query que hoy va bien con 1M de filas sigue yendo bien con 10M.
📌 Principio clave: cuantos menos datos procese tu query, más rápido irá.
Los 4 pilares de la optimización
Filtrar pronto y de forma efectiva (sección 11).
Reducir columnas pronto (sección 12).
Optimizar las aggregations (sección 13).
Optimizar los joins (sección 14).
🔍
11. Filter early: reducir data escaneada
Idea central
Filtrar reduce la cantidad de datos que tienen que procesar las operaciones siguientes. Las KQL databases usan índices y técnicas de organización de datos que hacen el filtrado temprano especialmente eficiente.
Filtrado temporal primero
El filtrado por tiempo es especialmente efectivo porque los Eventhouses típicamente contienen datos de series temporales (indexados por tiempo).
Bien ✅:
TaxiTrips
| where pickup_datetime > ago(30min) // Filtra primero: usa el índice temporal
| project trip_id, vendor_id, pickup_datetime, fare_amount
| summarize avg_fare = avg(fare_amount) by vendor_id
El filtro temporal primero usa el índice de tiempo → salta cantidades enormes de datos.
Ordenar por selectividad
Ordena los filtros por cuántos datos eliminan. Como un embudo: pon primero el filtro que elimina más filas y luego los más específicos sobre lo que queda.
TaxiTrips
| where pickup_datetime > ago(1d) // Filtro temporal primero: elimina la mayoría
| where vendor_id == "VTS" // Vendor concreto: elimina más
| where fare_amount > 0 // Filtro de valor: elimina menos
| summarize trip_count = count()
Antipatrón: filtrar tarde
Mal ❌:
TaxiTrips
| project trip_id, pickup_datetime, fare_amount // Proyecta primero
| summarize avg_fare = avg(fare_amount) by vendor_id // Agrega primero
| where pickup_datetime > ago(30min) // Filtra al final ❌
Aquí el filtro no evita el procesamiento de las filas antiguas: procesas todo antes de filtrar.
📏
12. Reduce columns early
Idea central
Proyectar (seleccionar) solo las columnas que necesitas reduce el uso de recursos. Especialmente importante cuando trabajas con tablas anchas (muchas columnas).
Proyectar pronto → procesar solo 3 columnas en vez de 50.
Cuándo combinarlo con el filtro
Mejor patrón: proyectar pronto + filtrar pronto, juntos. KQL lo optimizará bien.
TaxiTrips
| where pickup_datetime > ago(30min) // Filtra primero
| project trip_id, vendor_id, pickup_datetime, fare_amount // Proyecta después
| summarize avg_fare = avg(fare_amount) by vendor_id
En la práctica el optimizador los combina, pero es mejor escribir explícitamente el project temprano.
📊
13. Optimizar aggregations
Limitar resultados al explorar
Las aggregations son intensivas en recursos porque combinan muchos datos. Al explorar, limita los resultados:
TaxiTrips
| where pickup_datetime > ago(1d)
| summarize trip_count = count() by trip_id, vendor_id
| limit 1000 // Limit para exploración
Sin limit, si hay muchas combinaciones, puedes tener millones de filas en el output.
Usar top en vez de sort + limit
Bien ✅:
TaxiTrips
| summarize count() by vendor_id
| top 10 by count_ desc
Menos eficiente:
TaxiTrips
| summarize count() by vendor_id
| sort by count_ desc
| take 10
top está optimizado exactamente para este caso de uso.
Varias aggregations en un solo summarize
Mejor ✅:
TaxiTrips
| summarize
trip_count = count(),
avg_fare = avg(fare_amount),
max_fare = max(fare_amount),
total = sum(fare_amount)
by vendor_id
En vez de hacer 4 queries separadas.
🔀
14. Optimizar joins (el orden importa)
📌 🎯 Regla clave: la tabla más PEQUEÑA primero. Cuando haces join de tablas, pon la más pequeña primero. Razón: KQL procesa la primera tabla para hacer match con la segunda. Empezar con una tabla pequeña significa menos filas que procesar → join más eficiente.
Ejemplo
Bien ✅ (tabla pequeña primero):
// VendorInfo es pequeña (1000 vendors), TaxiTrips es grande (10M trips)
VendorInfo
| join kind=inner TaxiTrips on vendor_id
Evitar ❌:
// Empezar con la grande es menos eficiente
TaxiTrips
| join kind=inner VendorInfo on vendor_id
Filtrar ANTES del join
Otra optimización crucial: filtrar cada tabla antes de hacer el join.
Bien ✅:
VendorInfo
| where region == "NY" // Filtra la tabla pequeña
| join kind=inner (
TaxiTrips
| where pickup_datetime > ago(1d) // Filtra la tabla grande
) on vendor_id
Menos eficiente:
VendorInfo
| join kind=inner TaxiTrips on vendor_id
| where region == "NY" // Filtra tarde ❌
| where pickup_datetime > ago(1d) // Filtra tarde ❌
Tipos de join
KQL soporta varios kind=:
inner (default): solo los matches (menos filas).
leftouter: todos los de la izquierda + los matches de la derecha.
rightouter: todos los de la derecha + los matches de la izquierda.
fullouter: todos los de ambas.
leftanti: filas de la izquierda que NO tienen match en la derecha.
rightanti: filas de la derecha que NO tienen match en la izquierda.
leftsemi: filas de la izquierda que SÍ tienen match (sin columnas de la derecha).
rightsemi: filas de la derecha que SÍ tienen match.
innerunique: deduplicado automático en la izquierda.
⭐
15. Materialized views a fondo
Este es un tema estrella del módulo y del examen. Presta atención especial 💕
El problema que resuelven
Las KQL databases de los eventhouses contienen a menudo millones o miles de millones de filas de datos en streaming (IoT, logs, eventos). Ejecutar queries de agregación sobre esos datasets cada vez que las consultas consume mucho tiempo y cómputo.
La solución: materialized views
Las materialized views son agregaciones precomputadas que se actualizan automáticamente conforme llegan datos nuevos. En vez de recalcular las métricas desde toda la historia en cada query, la materialized view mantiene los resultados precomputados y solo procesa los datos nuevos para actualizarse.
Resultado: queries instantáneas en dashboards y reports, incluso con datasets enormes.
🌸 Analogía kawaii: piensa en un contador de galletas en la cafetería. Sin materialized view, cada vez que alguien pregunta "¿cuántas galletas hay?", vas y las cuentas todas. Con materialized view, mantienes un cartel actualizado con el recuento y, cuando llega una galleta nueva, sumas 1. Preguntar el recuento es instantáneo.
⚙️
16. Cómo funcionan por dentro
Las dos partes
Una materialized view consta de dos partes que trabajan juntas:
A) Materialized part 💾 — los resultados precomputados de los datos ya procesados, guardados en una "tabla oculta" interna.
B) Delta ⏱️ — los datos nuevos que han llegado desde la última actualización en background. Se procesan al vuelo al consultar.
Al consultar
Cuando haces una query a la materialized view, el sistema combina automáticamente ambas partes en tiempo de query y devuelve resultados frescos y actualizados.
Esto significa que las materialized views SIEMPRE devuelven datos actuales, independientemente de cuándo se ejecutó la última materialización en background.
Proceso en background
Mientras tanto, un proceso en background mueve periódicamente los datos de la parte delta a la parte materializada, manteniendo actualizados los resultados precomputados.
Este enfoque da la velocidad de los resultados precomputados con la frescura de los datos en tiempo real.
Diagrama mental
Streaming ingesta continua
↓
Base table (raw)
↓
┌───┴───┐
│ │
Materialized Delta
part part
(viejo) (nuevo)
│ │
└───┬───┘
↓
Query result (fresh!)
El sistema combina ambas al consultar → resultado siempre actual.
🛠️
17. Crear y consultar materialized views
Sintaxis para crearlas
Una materialized view encapsula un statement summarize de KQL que se auto-actualiza.
Interpretación: crea una materialized view llamada TripsByVendor, basada en la tabla TaxiTrips, que precomputa el recuento de trips, la tarifa media y el revenue total, agrupado por vendor y fecha.
Consultar una materialized view
Una vez creada, se consulta como una tabla normal:
TripsByVendor
| where pickup_date >= ago(7d)
| project pickup_date, vendor_id, trips, avg_fare, total_revenue
| sort by pickup_date desc, total_revenue desc
Sintaxis idéntica a la de una tabla. El motor sabe combinar la parte materializada y la delta de forma transparente.
Cuándo usar materialized views
SÍ: agregaciones pesadas sobre datos en streaming, dashboards con métricas que se consultan con frecuencia, y cuando es aceptable un retardo de milisegundos por el refresh en background.
NO: queries ad-hoc únicas, agregaciones muy complejas con lógica cambiante, o cuando necesitas tiempo real exacto milisegundo a milisegundo.
⚠️ Limitaciones: la query dentro de la materialized view debe ser un summarize; los cambios en el schema de la tabla origen pueden romper la view; y consume storage adicional para la parte materializada.
🔧
18. Stored functions
¿Qué son?
KQL permite encapsular una query como una función para facilitar su reutilización. Puedes especificar parámetros para que la función acepte valores variables.
Por qué usarlas
En eventhouses con datos en streaming y varios usuarios escribiendo queries:
Evitas escribir la misma lógica de filtrado/transformación una y otra vez.
Defines una vez y reutilizas en muchas queries.
Aseguras cálculos consistentes cuando varias personas del equipo aplican la misma lógica.
Crear una función
.create-or-alter function trips_by_min_passenger_count(num_passengers:long)
{
TaxiTrips
| where passenger_count >= num_passengers
| project trip_id, pickup_datetime
}
Interpretación: crea (o actualiza) una función llamada trips_by_min_passenger_count, que acepta un parámetro num_passengers de tipo long, filtra los trips con ≥ N pasajeros y proyecta trip_id y pickup_datetime.
Llamar a la función
Se llama como si fuera una tabla:
trips_by_min_passenger_count(3)
| take 10
Encuentra 10 trips con al menos 3 pasajeros.
Casos de uso
Lógica de negocio centralizada: "definir un customer VIP", "definir un evento crítico".
Cálculos complejos reutilizables: convertir temperaturas, calcular distancia haversine.
Patrones de acceso comunes: "top productos del último mes por región".
Consistencia de equipo: todos usan la misma función = mismo resultado.
🎯 Materialized view vs stored function
Este contraste es muy examinable:
Materialized view
Stored function
Almacena el resultado
✅ Sí (precomputado)
❌ No
Re-ejecuta al llamarla
❌ No (combina partes)
✅ Sí (en cada llamada)
Se actualiza automáticamente
✅ Sí
N/A
Coste de storage
Sí
No
Coste de cómputo
Bajo (al consultar)
Alto (reejecuta la query)
Ideal para
Agregaciones frecuentes
Lógica de query reutilizable
🔄
19. Update policies
¿Qué son?
Las update policies son mecanismos de automatización que se disparan cuando se ingiere data en una tabla origen. Ejecutan una query para transformar los datos ingeridos y guardan el resultado en una tabla destino.
Uso típico
Recibes logs raw en una tabla origen; la update policy se dispara, parsea el JSON, extrae campos y aplica lógica; y el resultado procesado va a la tabla destino. Beneficio: automatización en el momento de la ingesta, sin schedule externo.
⚠️ Warning importante con .set-or-replace: por defecto, los datos de las tablas derivadas se reemplazan igual que en la tabla origen, y pueden perderse datos en tablas con relación de update policy. Recomendación: usa .set-or-append siempre que sea posible.
Opciones avanzadas
IsTransactional: si es true, la ingesta y la update policy son atómicas.
PropagateIngestionProperties: propaga las ingestion properties al destino.
ManagedIdentity: usa managed identity para la query (obligatorio si la tabla tiene RLS).
📚
20. Policies overview: todas las policies
Este es un tema muy examinable. Vamos a ver todas las policies que existen en las KQL databases con su propósito.
Tabla oficial completa
Policy
Propósito
Ingestion time policy
Añade una columna oculta datetime con la hora de ingesta
ManagedIdentity policy
Controla qué managed identities pueden usarse y para qué
Merge policy
Reglas para fusionar datos de distintos extents en uno solo
Mirroring policy
Gestionar la mirroring policy y sus operaciones
Partitioning policy
Reglas de particionado de extents para una tabla o materialized view
Retention policy
Mecanismo que elimina automáticamente datos de tablas o materialized views
Restricted view access policy
Capa extra de requisitos de permiso para acceder a la tabla
Row level security policy
Reglas de acceso a filas según pertenencia a grupos o contexto de ejecución
Row order policy
Mantiene un orden específico de filas dentro de un extent
Sandbox policy
Controla el uso y el comportamiento de los sandboxes (entornos aislados)
Sharding policy
Reglas de cómo se crean los extents
Streaming ingestion policy
Configuración de la ingesta de datos en streaming
Update policy
Permite añadir datos a una tabla destino cuando se añaden a la origen
Query weak consistency policy
Controla el nivel de consistencia de los resultados de query
🎯 Cuáles son las MÁS examinables:Retention policy (limpieza), Cache policy (performance), Row-level security policy (seguridad), Restricted view access policy (seguridad), Update policy (transformación), Streaming ingestion policy (ingesta), Partitioning policy (performance) y Merge policy (performance). Las vemos una a una 💕
⏳
21. Retention policy
¿Qué hace?
La retention policy define cuánto tiempo se guardan los datos en una tabla antes de eliminarse automáticamente. Es el equivalente KQL a un TTL (Time-To-Live).
SoftDeletePeriod: cuánto tiempo antes de borrar (formato días.horas:minutos:segundos). Ejemplo: 30.00:00:00 = 30 días.
Recoverability: si se puede recuperar tras eliminar.
Casos de uso
Logs: retención típica de 30-90 días.
Telemetría IoT: 7-30 días para el hot, y mover a Lakehouse para el cold.
Compliance: retener 7+ años en tablas concretas.
Reducir costes de storage: eliminar datos antiguos sin uso.
Automático vs manual
La retention policy hace la limpieza automáticamente en background. No tienes que hacer nada manual.
⚡
22. Cache policy
¿Qué hace?
La cache policy controla cuántos datos se mantienen en la caché (premium storage rápido). Datos en caché = queries rapidísimas. Datos fuera de la caché (standard storage) = queries más lentas.
Configuración
.alter table MyTable policy caching hot = 7d
Aquí: mantén los últimos 7 días en hot cache.
Cómo elegir el periodo de caché
Regla: cachea lo que consultas con frecuencia. Si tus dashboards consultan los últimos 30 días → cache = 30d. Si consultas históricos rara vez → caché pequeña y retención larga en standard.
Relación con los storage tiers
Recuerda del Módulo 8: el OneLake Cache Storage (premium) da queries rápidas y es más caro; el OneLake Standard Storage es persistente y más barato. La cache policy determina qué va en la caché.
Ejemplo real
Tabla IoT_Logs con retention policy de 1 año y cache policy de 30 días:
Últimos 30 días → caché → queries instantáneas.
Días 31-365 → standard storage → queries más lentas pero accesibles.
Más de 365 días → eliminados.
🔐
23. Row-Level Security en KQL
¿Qué es?
Row-Level Security (RLS) = mecanismo que filtra las filas visibles a un usuario según su identidad o pertenencia a grupos. Cada usuario ve solo las filas que le corresponden, incluso ejecutando la misma query.
Cómo funciona en KQL
Se define una query de RLS que actúa como filtro implícito. La query devuelve las filas que el usuario actual puede ver.
Donde AnonymizeSensitiveData es una stored function que define la lógica:
.create function AnonymizeSensitiveData() {
SalesData
| where region in ((current_principal_details().userPrincipalName |
lookup MyRegionMapping on user))
}
Forzar RLS temporalmente
Cuando la policy está deshabilitada, puedes forzarla en una query concreta:
set query_force_row_level_security;
SalesData
| take 100
Útil para testear queries de RLS sin activarla para todos los usuarios.
Permisos requeridos
Para activar/desactivar la RLS policy: permiso Table Admin como mínimo.
⚠️ Limitaciones importantes:
No hay límite en el número de tablas con RLS.
NO se puede configurar en External Tables.
NO se puede activar RLS en tablas que: son referenciadas por una update policy sin managed identity, son referenciadas por un continuous export sin impersonation, o tienen configurada una restricted view access policy.
La query de RLS NO puede referenciar otras tablas que también tengan RLS.
La query de RLS NO puede referenciar tablas de otras databases.
Filter predicates vs block predicates
En KQL de Fabric solo se soportan los filter predicates — filtran las filas silenciosamente. (En SQL Server tradicional existen los block predicates para bloquear escrituras, pero en KQL de Fabric solo hay filter.)
🚫
24. Restricted view access policy
¿Qué hace?
Añade una capa extra de requisitos de permiso para acceder a la tabla. Con esta policy activa, incluso los usuarios con permiso normal necesitan un permiso adicional específico para ver la tabla.
Cuándo usarla
Datos muy sensibles que requieren una aprobación extra.
Cumplimiento de requisitos de seguridad estrictos.
Tablas con datos financieros o PII.
⚠️ Compatibilidad: el RLS y la restricted view access policy son mutuamente excluyentes. No puedes activar ambos en la misma tabla.
🌊
25. Streaming ingestion policy
¿Qué hace?
Configura la ingesta de datos en streaming. Con la streaming ingestion activada, los datos son consultables casi de inmediato (segundos) tras ser ingeridos, en vez de por lotes.
Con streaming ingestion activada: ✅ los datos están disponibles para consulta más rápido, pero ⚠️ hay más overhead de cómputo por fila y es más caro por fila.
Sin streaming (batch): ✅ más barato por fila y optimizado para alto volumen, pero ⚠️ con un retardo de segundos-minutos antes de poder consultar.
Cuándo usar cada uno
Streaming ingestion: dashboards en tiempo real, alertas críticas.
Batch ingestion: cargas masivas, analítica batch, optimización de costes.
🧩
26. Partitioning policy
¿Qué hace?
Define las reglas para particionar los extents de una tabla o materialized view. Beneficio: las queries que filtran por la partition key son mucho más rápidas (partition pruning).
Uso típico
Particionar por customer_id (si las queries filtran por customer), por tenant_id en escenarios multi-tenant, o por date / month para análisis de series temporales.
Baja cardinalidad: pocas partition keys → poco beneficio.
Cardinalidad muy alta: demasiadas partition keys → overhead.
Las queries no filtran por la key: sin filtro no hay pruning.
🔀
27. Merge policy
¿Qué hace?
Define las reglas para fusionar datos de distintos extents en uno solo. En KQL los datos se almacenan en extents (bloques); muchos extents pequeños → mal rendimiento. El merge los consolida.
Similar al OPTIMIZE de Delta
Es un concepto parecido al OPTIMIZE de Delta Lake que vimos en el Módulo 4: consolidar ficheros pequeños en grandes. La diferencia: el OPTIMIZE de Delta es manual o programado, mientras que el merge de KQL es un proceso automático en background (configurable).
Los valores por defecto suelen funcionar bien. Solo tócalos si tienes casos específicos.
🤖
28. Copilot para KQL
Copilot for Real-Time Intelligence
Fabric incluye Copilot para RTI, que ayuda a generar queries KQL desde lenguaje natural, explicar queries existentes, sugerir optimizaciones y depurar queries que fallan.
Habilitación
Requiere que el admin habilite Copilot para el tenant/workspace. Una vez habilitado, aparece la opción en la barra de menú del queryset.
Uso
Abrir el panel de Copilot.
Escribir la pregunta en lenguaje natural: "Muéstrame las top 10 ventas del último día por región".
Copilot genera el KQL.
Puedes ejecutarlo directamente o editarlo antes.
NL2KQL
Natural Language to KQL es la feature específica que traduce lenguaje natural a KQL. Está integrada en Copilot. Especialmente útil si vienes de SQL y estás aprendiendo KQL.
⚠️
29. Trampas típicas y confusiones frecuentes
Trampa 1: "KQL es case-insensitive como SQL"
❌ KQL ES CASE-SENSITIVE PARA TODO: tablas, columnas, funciones, keywords y valores de string. Fundamental.
Trampa 2: "Las materialized views se actualizan por schedule"
❌ Se actualizan automáticamente conforme llegan datos. No requieren schedule.
Trampa 3: "Las materialized views devuelven datos desactualizados"
❌ Siempre devuelven datos actuales, combinando la parte materializada y la delta.
Trampa 4: "Las update policies se ejecutan por schedule"
❌ Se disparan cuando se ingiere data en la tabla origen. No son schedule-based.
Trampa 5: "Las stored functions almacenan resultados"
❌ Almacenan la LÓGICA de la query, no los resultados. Se re-ejecutan en cada llamada.
Trampa 6: "Puedo activar RLS y restricted view access policy a la vez"
❌ Son mutuamente excluyentes. Solo uno por tabla.
Trampa 7: "El RLS funciona en External Tables"
❌ NO se puede configurar RLS en External Tables.
Trampa 8: "El RLS puede referenciar otras tablas con RLS"
❌ La query de RLS NO puede referenciar otras tablas con RLS habilitado.
Trampa 9: "La cache policy elimina datos"
❌ La cache policy solo controla qué está en la caché premium. NO elimina datos. La que elimina es la retention policy.
Trampa 10: "La retention policy elimina datos instantáneamente"
❌ Es un proceso en background. Puede tardar en eliminar. No es en tiempo real.
Trampa 11: "Filtrar tarde es igual de eficiente"
❌ Filtrar pronto es CRÍTICO para el rendimiento. El orden importa mucho.
Trampa 12: "En los joins el orden no importa"
❌ La tabla más pequeña PRIMERO para ser eficiente. Sí importa.
Trampa 13: "El streaming ingestion siempre es mejor"
❌ Tiene overhead extra. Para cargas masivas, el batch es más eficiente y barato.
Trampa 14: "Cada Eventhouse tiene solo una KQL database"
❌ Un Eventhouse puede tener varias KQL databases. La default se crea automáticamente, pero puedes crear más.
Trampa 15: "has y contains son intercambiables"
❌ has busca palabras completas usando el índice → más rápido. contains busca la subcadena en cualquier posición. Usa has cuando aplique.
Trampa 16: "Las update policies afectan igual a .set-or-replace que a .set-or-append"
⚠️ Con .set-or-replace los datos de las tablas derivadas también se reemplazan — puede perderse data. Recomendación: .set-or-append siempre que se pueda.
Trampa 17: "Una materialized view se puede basar en cualquier query"
❌ Debe ser un statement summarize. Otros operadores no valen.
Trampa 18: "Puedo cambiar el schema de la tabla origen libremente"
⚠️ Los cambios en el schema del origen pueden romper las materialized views que dependen de él.
🆚
30. Comparativas clave (chuleta)
Materialized view vs Stored function vs Update policy