Skip to main content

CASE — Referencia de SQL en Marketing Cloud

Lógica condicional en MC SQL — Simple vs Searched CASE, la trampa del ELSE faltante que devuelve NULL, compatibilidad de tipos entre branches, y la regla para cuándo stagéar a un DE de lookup en su lugar.

Referencia·Actualizado 2026-05-07·Escrito por Lira · Editado por German Medina

CASE es cómo metés lógica condicional dentro de una expresión SQL. En MC SQL funciona como T-SQL: un CASE devuelve un solo valor por fila basado en cuál WHEN matchea primero. Hay dos formas — CASE Simple (compara contra una sola columna) y CASE Searched (cada WHEN es su propia expresión booleana). Los bugs son mayormente sobre el ELSE que te olvidaste de escribir.

Sintaxis oficial

-- Simple CASE — chequeo de igualdad contra una sola columna
SELECT
  SubscriberKey,
  CASE LoyaltyTier
    WHEN 'gold'    THEN 'priority'
    WHEN 'silver'  THEN 'priority'
    WHEN 'bronze'  THEN 'standard'
    ELSE 'unsegmented'
  END AS Segment
FROM master_subscribers;

-- Searched CASE — cada WHEN es una expresión booleana completa
SELECT
  SubscriberKey,
  CASE
    WHEN LoyaltyTier IN ('gold','silver') AND LastPurchase >= DATEADD(day, -30, GETDATE())
      THEN 'priority-active'
    WHEN LoyaltyTier IN ('gold','silver')
      THEN 'priority-dormant'
    WHEN LoyaltyTier = 'bronze'
      THEN 'standard'
    ELSE 'unsegmented'
  END AS Segment
FROM master_subscribers;

-- CASE adentro de un agregado — contando matches por condición
SELECT
  EmailDomain,
  COUNT(*) AS TotalSubs,
  COUNT(CASE WHEN LoyaltyTier = 'gold'   THEN 1 END) AS GoldCount,
  COUNT(CASE WHEN LoyaltyTier = 'silver' THEN 1 END) AS SilverCount,
  SUM(CASE WHEN Status = 'Active'        THEN 1 ELSE 0 END) AS ActiveCount
FROM master_subscribers
GROUP BY EmailDomain;

CASE evalúa sus cláusulas WHEN en orden y devuelve el valor del primer match. El orden importa cuando las condiciones se solapan. La expresión tiene que devolver un solo valor de tipo compatible entre todas las ramas.

Referencia:

Lo que sobrevive en producción

Siempre incluí ELSE — el ELSE faltante devuelve NULL

Sin ELSE, cualquier fila que no matchee ningún WHEN devuelve NULL. El próximo WHERE, JOIN o INSERT INTO después tiene que lidiar con ese NULL — y la lógica de tres valores no se comporta como la mayoría de los lectores asume (ver WHERE y gotchas — #5).

-- EN RIESGO — sin ELSE, NULL se filtra aguas abajo
SELECT
  SubscriberKey,
  CASE LoyaltyTier
    WHEN 'gold'   THEN 'priority'
    WHEN 'silver' THEN 'priority'
  END AS Segment           -- bronze, NULL → devuelve NULL
FROM master_subscribers;

-- DURABLE — ELSE explícito captura todos los demás casos
SELECT
  SubscriberKey,
  CASE LoyaltyTier
    WHEN 'gold'   THEN 'priority'
    WHEN 'silver' THEN 'priority'
    ELSE 'standard'
  END AS Segment
FROM master_subscribers;

La disciplina: cada CASE termina con ELSE, incluso si el ELSE es un literal 'unknown' o NULL que escribiste a propósito. Escribir ELSE NULL está bien — le dice al próximo dev que pensaste en el caso no matcheado.

Todas las ramas tienen que devolver el mismo tipo

Si un WHEN devuelve un string y otro devuelve un entero, el motor castea implícitamente — a veces correctamente, a veces corrompiendo en silencio. Lockeá el tipo explícitamente.

-- EN RIESGO — las ramas devuelven tipos distintos, coerción implícita
SELECT
  SubscriberKey,
  CASE
    WHEN LoyaltyTier = 'gold'  THEN 1            -- INT
    WHEN LoyaltyTier = 'silver' THEN 'tier-2'    -- VARCHAR
    ELSE 0
  END AS TierScore
FROM master_subscribers;

-- DURABLE — todas las ramas del mismo tipo, cast explícito donde haga falta
SELECT
  SubscriberKey,
  CASE
    WHEN LoyaltyTier = 'gold'   THEN 'tier-1'
    WHEN LoyaltyTier = 'silver' THEN 'tier-2'
    ELSE 'tier-0'
  END AS TierScore
FROM master_subscribers;

El orden importa — el WHEN más específico primero

CASE devuelve el primer match. Si una condición más general viene antes que una más específica, la específica nunca se dispara.

-- BUG — el primer WHEN captura a todos con LoyaltyTier='gold',
-- así que el segundo WHEN (gold + compra reciente) es inalcanzable
CASE
  WHEN LoyaltyTier = 'gold' THEN 'gold'
  WHEN LoyaltyTier = 'gold' AND LastPurchase >= DATEADD(day, -30, GETDATE()) THEN 'gold-active'
  ELSE 'standard'
END

-- CORRECTO — el más específico primero
CASE
  WHEN LoyaltyTier = 'gold' AND LastPurchase >= DATEADD(day, -30, GETDATE()) THEN 'gold-active'
  WHEN LoyaltyTier = 'gold' THEN 'gold'
  ELSE 'standard'
END

Cuando el CASE se anida 3+ niveles, stagéa a un DE de lookup

CASE anidado se lee como un laberinto en code review y los cambios dan miedo. Si la lógica codifica reglas de negocio que cambian trimestralmente (definiciones de segmento, tiers de descuento, flags de elegibilidad), poné las reglas en un Data Extension chico y JOIN contra él.

-- EN RIESGO — CASE profundamente anidado, reglas de negocio enterradas en SQL
CASE
  WHEN LoyaltyTier = 'gold' THEN
    CASE WHEN LastPurchase >= DATEADD(day, -30, GETDATE()) THEN 'gold-active'
         WHEN LastPurchase >= DATEADD(day, -90, GETDATE()) THEN 'gold-warm'
         ELSE 'gold-cold' END
  WHEN LoyaltyTier = 'silver' THEN
    CASE WHEN LastPurchase >= DATEADD(day, -60, GETDATE()) THEN 'silver-active'
         ELSE 'silver-cold' END
  ELSE 'standard'
END

-- DURABLE — las reglas viven en de_lookup_segment_rules, JOIN para aplicar.
-- Marketing edita el DE; el SQL no cambia.
SELECT
  s.SubscriberKey,
  r.SegmentName
FROM master_subscribers s
LEFT JOIN de_lookup_segment_rules r
  ON s.LoyaltyTier = r.LoyaltyTier
  AND DATEDIFF(day, s.LastPurchase, GETDATE()) BETWEEN r.MinDays AND r.MaxDays;

El DE de lookup tiene dos columnas más, pero el SQL deja de ser la fuente de verdad para la lógica de negocio. Marketing puede leer de_lookup_segment_rules directamente y actualizarla sin abrir un ticket.

Decisión rápida

Usá CASE Simple (CASE col WHEN val THEN ...) cuando:

  • Estás comparando una columna contra un set chico de valores literales.
  • La igualdad es el único check que hace falta.

Usá CASE Searched (CASE WHEN expr THEN ...) cuando:

  • Las condiciones involucran múltiples columnas, rangos, o IN / LIKE.
  • Cada rama necesita su propia expresión booleana completa.

Incluí siempre ELSE cuando:

  • El resultado alimenta un filtro WHERE, una condición de JOIN, o una columna del DE de destino. La fuga de NULL aguas abajo es el bug.

Stagéa a un DE de lookup en lugar de CASE cuando:

  • La lógica codifica reglas de negocio que cambian seguido.
  • El anidado va 3+ niveles de profundidad.
  • Marketing necesita editar las reglas sin tocar SQL.

Usá CASE adentro de agregados cuando:

  • Estás contando / sumando filas que matchean una condición. Preferí SUM(CASE WHEN x THEN 1 ELSE 0 END) para garantizar output numérico.

Relacionado

  • Basics — subset de T-SQL soportado
  • SELECTCASE en proyección
  • WHERE — usando resultados de CASE en filtros (y la trampa NULL)
  • JOIN — cuándo levantar lógica fuera de CASE hacia un DE de lookup
  • MC SQL gotchas — ver #5 para implicaciones de la lógica NULL de tres valores

Próximas páginas de referencia: INSERT INTO · String / Date / Numeric / Conversion / Aggregate / Null Functions · Style Guide.

Más snippets how-to para debugging común en producción — sends de email, largo de valores, alcance de contactos, etc.