Skip to main content

JOIN — Referencia de SQL en Marketing Cloud

Cómo combinar Data Extensions en MC SQL — los cuatro tipos de join, la trampa de coerción de tipo de SubscriberKey, anti-joins, y la regla de staging que mantiene la performance honesta.

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

JOIN es cómo combinás filas de dos o más fuentes en un solo SELECT. En MC SQL los cuatro tipos estándar (INNER, LEFT, RIGHT, FULL OUTER) están soportados y se comportan como en T-SQL. Lo que NO es estándar es el dato — SubscriberKey parece entero pero está guardado como string, las System Data Views pueden o no matchear el row set que esperás, y el timeout de 30 minutos castiga los joins que tocan demasiadas fuentes a la vez.

Sintaxis oficial

-- INNER JOIN — solo filas que matchean en ambos lados
SELECT s.SubscriberKey, p.LastPurchase
FROM master_subscribers s
INNER JOIN purchases p
  ON s.SubscriberKey = p.SubscriberKey;

-- LEFT JOIN — cada fila del lado izquierdo; NULL del lado derecho
-- cuando no hay match
SELECT s.SubscriberKey, p.LastPurchase
FROM master_subscribers s
LEFT JOIN purchases p
  ON s.SubscriberKey = p.SubscriberKey;

-- LEFT JOIN ... WHERE right IS NULL — anti-join, "está en el
-- izquierdo pero no en el derecho". El patrón de dedup / supresión
-- más común en MC
SELECT s.SubscriberKey
FROM master_subscribers s
LEFT JOIN suppression_list x
  ON s.SubscriberKey = x.SubscriberKey
WHERE x.SubscriberKey IS NULL;

-- FULL OUTER JOIN — cada fila de ambos lados, NULL donde
-- el otro lado no matchea
SELECT
  COALESCE(s.SubscriberKey, p.SubscriberKey) AS SubscriberKey,
  s.EmailAddress,
  p.LastPurchase
FROM master_subscribers s
FULL OUTER JOIN purchases p
  ON s.SubscriberKey = p.SubscriberKey;

CROSS JOIN (producto cartesiano) está técnicamente soportado pero casi nunca es la respuesta correcta en MC — multiplica el conteo de filas de un lado por el del otro, lo que contra DEs de tamaño productivo te tira el timeout de 30 min al instante.

Referencia:

Lo que sobrevive en producción

SubscriberKey es un string — casteá los dos lados antes de joinear

La fuente de bug silencioso más común en joins de MC. Incluso cuando SubscriberKey parece entero en tu CRM, MC lo guarda como string. Un join ON sub.SubscriberKey = ext.UserId donde ext.UserId es INT se ve correcto, parsea, corre — y silenciosamente saltea cada fila donde los ceros a la izquierda difieran, donde un lado trimmee espacios al final y el otro no, o donde los dos lados difieran en largo aunque sea por un caracter.

-- EN RIESGO — coerción de tipo implícita, mismatches silenciosos
SELECT s.SubscriberKey, e.LoyaltyTier
FROM _Subscribers s
INNER JOIN ext_loyalty e
  ON s.SubscriberKey = e.UserId;

-- SEGURO — cast explícito en los dos lados + trim defensivo
SELECT s.SubscriberKey, e.LoyaltyTier
FROM _Subscribers s
INNER JOIN ext_loyalty e
  ON LTRIM(RTRIM(CAST(s.SubscriberKey AS NVARCHAR(255))))
   = LTRIM(RTRIM(CAST(e.UserId AS NVARCHAR(255))));

La primera versión es más corta. La segunda matchea el conteo de filas que esperabas. Ver gotchas — #9.

El anti-join es tu patrón de supresión

MC SQL no tiene EXCEPT confiablemente entre ediciones, y NOT IN contra una subquery de un millón de filas es una trampa de performance. El patrón durable es LEFT JOIN ... WHERE right IS NULL:

-- Supresión MC estándar: subscribers que no están en la lista de supresión
SELECT s.SubscriberKey
FROM master_subscribers s
LEFT JOIN suppression_list x
  ON s.SubscriberKey = x.SubscriberKey
WHERE x.SubscriberKey IS NULL;

-- Patrón estándar MC de dedup-por-más-reciente (sin window functions)
INSERT INTO de_stg_max_purchase_per_email
SELECT EmailAddress, MAX(LastPurchase) AS MaxPurchase
FROM master_subscribers
GROUP BY EmailAddress;

INSERT INTO de_stg_dedup_subs
SELECT m.SubscriberKey
FROM master_subscribers m
INNER JOIN de_stg_max_purchase_per_email s
  ON m.EmailAddress = s.EmailAddress
  AND m.LastPurchase = s.MaxPurchase;

Dos Activities stagéadas. Cada una auditable independientemente. Las dos sobreviven el próximo update de plataforma porque ninguna depende de sintaxis edition-specific.

Tres o más fuentes: stagéa, no encadenes

Una query con FROM A INNER JOIN B INNER JOIN C INNER JOIN D parsea bien, corre bien en dev con datos de muestra, y hace timeout a los 31 minutos en producción. Las decisiones del optimizer en MC SQL no son tuyas para controlar.

El patrón: cada Activity es dueña de a lo sumo un join. Stagéa A JOIN B → de_stg_ab, después de_stg_ab JOIN C → de_stg_abc, después de_stg_abc JOIN D → final. Tres Activities, tres checkpoints, tres conteos de filas que podés comparar contra expectativas. Cada una se mantiene bien bajo el timeout.

Especificá siempre el tipo de join

FROM a, b WHERE a.k = b.k (join implícito) es técnicamente SQL válido pero se lee como un pensamiento sin terminar o un bug. Usá INNER JOIN ... ON para que el próximo dev lea la intención en la sintaxis, no en el WHERE.

-- EVITAR — cross-join implícito + filtro, difícil de leer, fácil de leer mal
SELECT s.SubscriberKey, p.LastPurchase
FROM master_subscribers s, purchases p
WHERE s.SubscriberKey = p.SubscriberKey
  AND s.Status = 'Active';

-- CLARO — tipo de join explícito, ON para la condición de join,
-- WHERE para el filtro post-join
SELECT s.SubscriberKey, p.LastPurchase
FROM master_subscribers s
INNER JOIN purchases p
  ON s.SubscriberKey = p.SubscriberKey
WHERE s.Status = 'Active';

Decisión rápida

Usá INNER JOIN cuando:

  • Solo querés filas que matchean en ambos lados.
  • Por default. Si no sabés qué tipo de join necesitás, generalmente necesitás INNER.

Usá LEFT JOIN cuando:

  • Querés cada fila de la fuente primaria, con datos opcionales de la secundaria.
  • Estás construyendo un anti-join con WHERE right IS NULL (supresión, "falta en").
  • El conteo de filas del lado derecho es incierto y no querés perder filas del lado izquierdo.

Usá RIGHT JOIN solo como:

  • Un LEFT JOIN con los operandos al revés, cuando reordenar FROM perjudica la legibilidad. La mayoría de los equipos eligen una convención (siempre LEFT) y la mantienen.

Usá FULL OUTER JOIN cuando:

  • Genuinamente necesitás cada fila de ambos lados (reconciliación, auditoría) — y COALESCE las claves de join para surfacear el valor matcheado.

Evitá CROSS JOIN salvo que:

  • Estés generando una tabla chica de calendario / números para rangos de fecha, y LIMIT el resultado. Si no, la explosión de filas tira el timeout.

Relacionado

  • Basics — el subset de T-SQL soportado y la mecánica de target action
  • SELECT — la proyección que viene antes del join
  • FROM — fuentes, aliases, y el patrón de snapshot SDV que los joins heredan
  • MC SQL gotchas — ver #9 para la trampa de coerción de tipo de SubscriberKey y #6 para rotación de SDV

Próximas páginas de referencia: WHERE · LIKE · CASE · 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.