🔀

Funciones Lógicas

IF, SWITCH, AND, OR, COALESCE, IFERROR y los operadores de bits — toda la lógica condicional de DAX

Principiante Intermedio
🔍

IF e IF.EAGER

IF(<condición>, <valor_si_verdadero>, [<valor_si_falso>])
Principiante
La función condicional básica. Evalúa una condición y devuelve uno de dos valores. Si el valor_si_falso se omite, devuelve BLANK cuando la condición es FALSE.

Comportamiento lazy (evaluación perezosa):

IF es LAZY — solo evalúa la rama que va a devolver. Si la condición es TRUE, no evalúa el valor_si_falso. Esto es importante para evitar errores y mejorar el rendimiento.

Ejemplo básico:

// Clasificar ventas por importe
Tamaño Venta =
IF(
    'Ventas'[Importe] > 1000,
    "Grande",
    "Pequeña"
)

Con BLANK en el else (valor omitido):

// Solo muestra algo cuando se cumple la condición
Alerta Stock = IF('Productos'[Stock] < 10, "⚠️ Stock bajo")
// Cuando Stock >= 10 → BLANK (la celda aparece vacía)

IF anidados (úsalos con moderación — SWITCH es mejor para múltiples condiciones):

Segmento =
IF(
    'Clientes'[Gasto Anual] > 10000, "Premium",
    IF(
        'Clientes'[Gasto Anual] > 3000, "Standard",
        "Basic"
    )
)

Ejemplo con medidas (no columnas calculadas):

// En una medida: mostrar margen o ventas según slicer
Métrica =
IF(
    SELECTEDVALUE('KPI Selector'[Tipo]) = "Margen",
    [Total Margen],
    [Total Ventas]
)
⚠️ Cuidado con IF dentro de iteradores
Cuando usas IF en una expresión de SUMX o AVERAGEX, evalúa la condición para CADA FILA. Asegúrate de que la condición sea eficiente. Si la condición es compleja, considera moverla a una columna calculada.
IF.EAGER(<condición>, <valor_si_verdadero>, [<valor_si_falso>])
Avanzado
Versión EAGER (ansiosa) de IF — evalúa AMBAS ramas siempre, independientemente de la condición. En algunos escenarios puede ser más eficiente que IF cuando ambas ramas necesitarían calcularse de todos modos.

Cuándo usar IF.EAGER vs IF:

  • IF es mejor cuando: una de las ramas es costosa de calcular o puede dar error
  • IF.EAGER es mejor cuando: ambas ramas son expresiones simples y el motor puede paralelizarlas

Ejemplo:

// Uso típico en medidas con datos que raramente dan error
Margen % = IF.EAGER(ISBLANK([Ventas]), BLANK(), DIVIDE([Margen], [Ventas]))
// IF.EAGER evalúa DIVIDE aunque ISBLANK sea TRUE
// Usarlo cuando sabes que DIVIDE no va a dar error en ninguna rama
🔄

SWITCH — el IF múltiple

SWITCH(<expresión>, <valor1>, <resultado1>, <valor2>, <resultado2>, ..., [<resultado_por_defecto>])
Principiante — Intermedio
Evalúa una expresión y devuelve el resultado correspondiente al primer valor que coincida. Mucho más legible que los IF anidados para múltiples condiciones. Existe en dos modos: coincidencia de valor y modo TRUE().

Modo 1 — coincidencia de valor (como un switch/case):

// Nombre completo del mes según número
Nombre Mes =
SWITCH(
    MONTH('Calendario'[Fecha]),
    1, "Enero",    2, "Febrero",  3, "Marzo",
    4, "Abril",    5, "Mayo",     6, "Junio",
    7, "Julio",    8, "Agosto",   9, "Septiembre",
    10, "Octubre", 11, "Noviembre", 12, "Diciembre",
    "Mes desconocido"  -- valor por defecto
)

// Icono por estado del pedido:
Icono Estado =
SWITCH(
    'Pedidos'[Estado],
    "Pendiente",   "⏳",
    "Procesando",  "🔄",
    "Enviado",     "📦",
    "Entregado",   "✅",
    "Cancelado",   "❌",
    "❓"
)

Modo 2 — con TRUE() para rangos y condiciones (el más potente):

// SWITCH(TRUE(), ...) evalúa cada condición en orden
// y devuelve el resultado de la PRIMERA que sea verdadera
Clasificación Importe =
SWITCH(
    TRUE(),
    'Ventas'[Importe] > 10000, "⭐⭐⭐ Premium",
    'Ventas'[Importe] > 5000,  "⭐⭐ Standard",
    'Ventas'[Importe] > 1000,  "⭐ Basic",
    "Micro"
)

// Combinando condiciones:
Alerta =
SWITCH(
    TRUE(),
    'Productos'[Stock] = 0,           "🚨 SIN STOCK",
    'Productos'[Stock] < 5,           "⚠️ Stock crítico",
    'Productos'[Stock] < 'Productos'[StockMinimo], "⚡ Reponer",
    "✅ OK"
)
🏆 Best Practice
SWITCH(TRUE(), ...) es el patrón más usado en DAX para clasificaciones. Es más legible que los IF anidados, más eficiente en muchos casos y más fácil de mantener. Úsalo siempre que tengas más de 2-3 condiciones.
🧮

AND, OR, NOT y operadores lógicos

Tabla de equivalencias:

FUNCIÓN OPERADOR DESCRIPCIÓN
AND(a, b) a && b Verdadero si AMBAS condiciones son TRUE
OR(a, b) a || b Verdadero si AL MENOS UNA es TRUE
NOT(a) ! (no existe como op. en DAX) Invierte el valor lógico

Ejemplos:

// Cliente premium Y activo
Premium Activo = AND('Clientes'[Segmento] = "Premium", 'Clientes'[Activo] = TRUE)
// Equivalente con operador:
Premium Activo = 'Clientes'[Segmento] = "Premium" && 'Clientes'[Activo] = TRUE

// Pedido de España O Francia:
Iberia = OR('Clientes'[País] = "España", 'Clientes'[País] = "Francia")
// Equivalente (más escalable):
Iberia = 'Clientes'[País] IN {"España", "Francia"}

// NOT:
No Premium = NOT('Clientes'[Segmento] = "Premium")
// Equivalente:
No Premium = 'Clientes'[Segmento] <> "Premium"
💡 Tip
Cuando necesitas combinar más de 2 condiciones con AND, usa los operadores && encadenados: condA && condB && condC. La función AND() solo acepta exactamente 2 argumentos — para más condiciones necesitas anidar o usar &&.
🛡️

IFERROR e ISERROR

IFERROR(<valor>, <valor_si_error>)
Principiante — Intermedio
Si la expresión produce un error, devuelve el valor alternativo. Si no hay error, devuelve el resultado normal. Esencial para manejar divisiones por cero, conversiones fallidas y referencias a valores inexistentes.

Ejemplos:

// Proteger una división (aunque DIVIDE es mejor para esto):
Ratio = IFERROR('Ventas'[Importe] / 'Ventas'[Cantidad], 0)

// Convertir texto a número con protección:
Precio Limpio = IFERROR(VALUE('Productos'[PrecioTexto]), 0)

// Proteger LOOKUPVALUE cuando no encuentra match:
Categoría = IFERROR(
    LOOKUPVALUE('Categorías'[Nombre], 'Categorías'[ID], 'Productos'[CatID]),
    "Sin categoría"
)
⚠️ IFERROR es EAGER
IFERROR evalúa siempre AMBAS expresiones (es EAGER). Esto significa que si el valor_si_error es costoso de calcular, puede impactar el rendimiento incluso cuando no hay error. Para divisiones por cero, usa siempre DIVIDE() en lugar de IFERROR — es más eficiente y legible.
ISERROR(<valor>)
Intermedio
Devuelve TRUE si la expresión produce un error, FALSE si no. Se usa dentro de IF para tomar decisiones basadas en si algo da error.

Ejemplo:

// Alternativa a IFERROR:
Precio Limpio = IF(ISERROR(VALUE('Productos'[PrecioTexto])), 0, VALUE('Productos'[PrecioTexto]))
// Nota: aquí IFERROR es más eficiente porque no evalúa VALUE() dos veces
💎

COALESCE — la joya escondida

COALESCE(<valor1>, <valor2>, ..., <valorN>)
Intermedio
Devuelve el PRIMER valor no-BLANK de la lista. Es el equivalente DAX del COALESCE de SQL. Perfecta para proporcionar valores por defecto cuando puede haber nulos en cadena.

Por qué es tan útil:

// ❌ Sin COALESCE — verbose y difícil de leer:
Precio Final =
IF(NOT(ISBLANK('Productos'[PrecioOferta])),
    'Productos'[PrecioOferta],
    IF(NOT(ISBLANK('Productos'[PrecioEspecial])),
        'Productos'[PrecioEspecial],
        'Productos'[PrecioBásico]
    )
)

// ✅ Con COALESCE — limpio y elegante:
Precio Final = COALESCE('Productos'[PrecioOferta], 'Productos'[PrecioEspecial], 'Productos'[PrecioBásico])
// Devuelve PrecioOferta si no es BLANK, sino PrecioEspecial, sino PrecioBásico

Ejemplo con medidas:

// Usar el valor calculado o un fallback:
Ventas Mostrar = COALESCE([Ventas Reales], [Ventas Estimadas], 0)
// Primero intenta ventas reales, si son BLANK usa estimadas, si también son BLANK usa 0

TRUE() y FALSE()

TRUE() y FALSE() son funciones que devuelven los literales booleanos. En DAX moderno, puedes escribir directamente TRUE y FALSE sin paréntesis. Se usan principalmente en tablas calculadas, DATATABLE y como valores explícitos en condicionales.

Ejemplo:

// En una columna calculada:
'Clientes'[EsActivo] = IF('Clientes'[UltimaCompra] >= EDATE(TODAY(), -12), TRUE(), FALSE())

// Equivalente más conciso:
'Clientes'[EsActivo] = 'Clientes'[UltimaCompra] >= EDATE(TODAY(), -12)
// Las comparaciones ya devuelven TRUE/FALSE directamente
💻

BITAND, BITOR, BITXOR — operaciones de bits

Estas funciones realizan operaciones a nivel de bits sobre enteros. Son funciones de nicho, muy usadas en integración con sistemas que codifican flags/permisos como campos de bits (sistemas legacy, Active Directory, etc.).

Tabla de funciones:

FUNCIÓN OPERACIÓN EJEMPLO RESULTADO
BITAND(a, b) AND bit a bit BITAND(6, 3) → 010 & 011 2
BITOR(a, b) OR bit a bit BITOR(6, 3) → 110 | 011 7
BITXOR(a, b) XOR bit a bit BITXOR(6, 3) → 110 ^ 011 5
BITLSHIFT(n, d) Despl. izq. BITLSHIFT(2, 3) → 2 * 2³ 16
BITRSHIFT(n, d) Despl. der. BITRSHIFT(16, 2) → 16 / 2² 4

Ejemplo real — decodificar permisos de Active Directory:

// Un campo "Permisos" almacena flags en bits:
// Bit 0 (valor 1) = puede leer
// Bit 1 (valor 2) = puede escribir
// Bit 2 (valor 4) = puede administrar
// Si Permisos = 7 (111 en binario) → tiene todos los permisos

Puede Leer = IF(BITAND('Usuarios'[Permisos], 1) = 1, TRUE, FALSE)
Puede Escribir = IF(BITAND('Usuarios'[Permisos], 2) = 2, TRUE, FALSE)
Puede Administrar = IF(BITAND('Usuarios'[Permisos], 4) = 4, TRUE, FALSE)
💡 Funciones de nicho
Si nunca has trabajado con sistemas que usen campos de bits, es probable que no necesites estas funciones en el corto plazo. Pero si integras con Active Directory, sistemas ERP legacy o APIs que devuelven flags binarios, BITAND e BITOR serán tus mejores amigas.
🛠️

Patrones de lógica condicional

Patrón 1 — Clasificador con SWITCH(TRUE()):

Segmento RFM =
SWITCH(
    TRUE(),
    [Recencia] <= 30 && [Frecuencia] >= 5 && [MonetarioScore] = "Alto", "🌟 Campeón",
    [Recencia] <= 60 && [Frecuencia] >= 3, "💛 Leal",
    [Recencia] <= 90, "💚 En riesgo",
    [Recencia] > 180, "💔 Perdido",
    "📊 Normal"
)

Patrón 2 — Valor por defecto en cascada con COALESCE:

Precio Aplicado =
COALESCE(
    'Precios'[PrecioNegociado],   -- precio acordado con el cliente
    'Precios'[PrecioOferta],      -- precio de oferta activa
    'Precios'[PrecioTarifa]       -- precio de tarifa general
)

Patrón 3 — Mensaje de estado amigable:

Estado Display =
SWITCH(
    SELECTEDVALUE('Pedidos'[Estado]),
    "P", "⏳ Pendiente de procesar",
    "E", "🔄 En preparación",
    "S", "📦 Enviado — en camino",
    "D", "✅ Entregado",
    "C", "❌ Cancelado",
    IF(ISBLANK(SELECTEDVALUE('Pedidos'[Estado])), "Selecciona un pedido", "Estado desconocido")
)
🚀 Siguiente paso
¡La lógica condicional ya la tienes dominada! Con IF, SWITCH y COALESCE puedes manejar cualquier escenario de ramificación. El siguiente paso natural son las funciones de Texto para formatear y transformar los resultados de tus cálculos.