Home
/
Blog
/
Engineering
Engineering

Joindre deux data extensions SFMC en SQL : INNER JOIN ou LEFT JOIN

INNER JOIN ou LEFT JOIN ? Des requêtes SQL SFMC prêtes à copier pour combiner deux data extensions, joindre _Open et éviter le piège des lignes en double.

Vous avez une data extension clients et une data extension achats, et l'audience dont vous avez besoin se trouve à cheval sur les deux. Les filtered data extensions ne lisent qu'une table à la fois, donc la campagne attend que quelqu'un écrive du SQL.

Pour joindre deux data extensions dans SFMC, vous écrivez une query activity dans Automation Studio avec un INNER JOIN si vous voulez uniquement les contacts présents dans les deux tables, ou un LEFT JOIN si vous voulez tous les contacts de la première table enrichis de ce qui correspond dans la seconde. La jointure se fait sur une clé commune, presque toujours SubscriberKey.

Ce guide détaille les deux types de jointure avec des requêtes SQL complètes et prêtes à copier, montre comment joindre une data extension à une data view comme _Open, et passe en revue les pièges qui faussent silencieusement une audience : lignes en double, clés incompatibles et champs cibles mal nommés.

‍

Pourquoi combiner deux data extensions impose une jointure

‍

Dans Marketing Cloud Engagement, une data extension se comporte comme une table SQL Server et contient un seul type d'enregistrement : clients, commandes, consentements. Les filter activities et les filtered data extensions lisent une seule source. Dès que votre brief dit clients ayant acheté dans les 90 derniers jours et que la date d'achat vit dans une autre table, il faut passer par le SQL.

‍

La query activity est le seul outil natif qui joint des tables

Dans Automation Studio, la query activity lit une ou plusieurs data extensions ou data views et écrit le résultat dans une data extension cible créée à l'avance. Elle n'accepte que des instructions SELECT : la cible et l'action de données (Overwrite, Update ou Append) se règlent dans l'activité, jamais dans le code.

Le dialecte est celui de SQL Server. DATEADD(), GETDATE() et CASE WHEN fonctionnent ; les variables, les tables temporaires et ORDER BY sans TOP ne passent pas. La plupart des erreurs SQL courantes dans SFMC viennent de l'oubli d'une de ces règles.

‍

Choisissez la clé de jointure avant d'écrire une ligne

Une jointure a besoin d'une colonne qui désigne la même chose dans les deux tables : en général SubscriberKey, parfois EmailAddress ou un CustomerID importé du CRM. Le type de données doit être identique des deux côtés ; joindre un champ Text à un champ Number force une conversion qui échoue ou fait disparaître des lignes. Si un nom de champ contient des espaces, entourez-le de crochets, par exemple [Date dernier achat].

‍

Joindre deux data extensions SFMC avec INNER JOIN

‍

Un INNER JOIN ne renvoie une ligne que si la clé existe dans les deux data extensions. C'est le bon choix quand la seconde table est une condition de l'audience : acheteurs, membres du programme de fidélité, contacts rattachés à un magasin.

‍

La requête

Supposons que Customers_Master contient une ligne par abonné et que Purchase_Summary contient une ligne par abonné avec sa date de dernier achat et sa valeur cumulée. L'audience visée : tous ceux qui ont acheté dans les 90 derniers jours.

SELECT
    c.SubscriberKey,
    c.EmailAddress,
    c.FirstName,
    p.LastPurchaseDate,
    p.LifetimeValue
FROM Customers_Master AS c
INNER JOIN Purchase_Summary AS p
    ON c.SubscriberKey = p.SubscriberKey
WHERE p.LastPurchaseDate >= DATEADD(DAY, -90, GETDATE())

Ligne par ligne :

  • La liste SELECT prend les champs d'identité dans la table clients et les champs d'achat dans la table résumé, chacun préfixé par son alias.
  • FROM Customers_Master AS c désigne la première table et lui donne l'alias court c.
  • INNER JOIN Purchase_Summary AS p ON c.SubscriberKey = p.SubscriberKey ne conserve que les abonnés présents dans les deux tables, appariés sur la clé.
  • La clause WHERE limite aux achats des 90 derniers jours grâce à DATEADD et GETDATE.

Data extension cible : Buyers_Last90Days, sendable, avec SubscriberKey en clé primaire. Action de données : Overwrite, pour reconstruire l'audience en entier à chaque exécution.

Tout contact de Customers_Master sans ligne dans Purchase_Summary disparaît du résultat. C'est voulu, mais une table d'achats vide ou périmée produit alors une audience vide sans aucune erreur : comparez le nombre de lignes à votre attente après la première exécution.

‍

LEFT JOIN dans SFMC : garder tous les abonnés, marquer les correspondances

‍

Un LEFT JOIN conserve toutes les lignes de la première table (celle de gauche) et y attache les données de la seconde quand elles existent ; en l'absence de correspondance, les colonnes de droite reviennent à NULL. Utilisez-le quand la seconde table enrichit l'audience au lieu de la définir.

‍

Ajouter un statut avec CASE WHEN

Cette requête garde tous les clients et attribue à chacun le statut Prospect ou Customer selon qu'une ligne d'achat existe ou non.

SELECT
    c.SubscriberKey,
    c.EmailAddress,
    c.FirstName,
    CASE
        WHEN p.SubscriberKey IS NULL THEN 'Prospect'
        ELSE 'Customer'
    END AS CustomerStatus
FROM Customers_Master AS c
LEFT JOIN Purchase_Summary AS p
    ON c.SubscriberKey = p.SubscriberKey
  • LEFT JOIN Purchase_Summary AS p garde toutes les lignes de Customers_Master, qu'une correspondance soit trouvée ou non.
  • CASE WHEN p.SubscriberKey IS NULL teste la clé de droite : NULL signifie qu'aucune ligne d'achat n'existe pour cet abonné.
  • L'expression CASE reçoit l'alias CustomerStatus pour atterrir dans le champ du même nom dans la cible.

Data extension cible : Customers_WithStatus, clé primaire SubscriberKey, action Overwrite. Journey Builder peut ensuite brancher sur CustomerStatus avec un decision split au lieu d'avoir deux sources d'entrée.

‍

Transformer le LEFT JOIN en exclusion

Filtrez sur IS NULL et le LEFT JOIN devient une anti-jointure : tous les contacts de la première table absents de la seconde.

SELECT
    c.SubscriberKey,
    c.EmailAddress
FROM Customers_Master AS c
LEFT JOIN Purchase_Summary AS p
    ON c.SubscriberKey = p.SubscriberKey
WHERE p.SubscriberKey IS NULL
  • La ligne WHERE p.SubscriberKey IS NULL ne garde que les lignes sans correspondance, c'est-à-dire exactement les prospects qui n'ont jamais acheté.
  • Testez la colonne clé de la table de droite, pas un attribut facultatif qui peut lui aussi valoir NULL sur une ligne appariée.

Cible : Prospects_NoPurchase, clé primaire SubscriberKey, Overwrite. Le même besoin s'écrit souvent avec NOT EXISTS ; notre guide sur les abonnés qui ont ouvert sans cliquer détaille cette variante.

‍

Joindre une data extension à une data view

‍

Les data views _Sent, _Open et _Click sont des tables système en lecture seule qui conservent environ six mois d'historique d'engagement, et elles se joignent à vos propres data extensions comme n'importe quelle table.

‍

SubscriberKey fait le pont

Chaque data view de tracking porte SubscriberKey, JobID et EventDate. Joignez sur SubscriberKey, puis filtrez sur EventDate pour rester dans votre fenêtre. Les horodatages de tracking sont stockés en heure standard du centre des États-Unis, sans heure d'été : une fenêtre en jours entiers est plus sûre qu'une fenêtre en heures.

‍

Agrégez le côté « plusieurs » avant qu'il multiplie vos lignes

Un abonné ouvre beaucoup d'e-mails, donc _Open contient de nombreuses lignes par clé. Jointe telle quelle à une table à une ligne par abonné, dix ouvertures donnent dix copies du même client. Repliez la data view avec GROUP BY et des fonctions d'agrégation pour rester à une ligne par abonné.

SELECT
    c.SubscriberKey,
    c.EmailAddress,
    MAX(o.EventDate) AS LastOpenDate,
    COUNT(o.JobID) AS OpensLast30Days
FROM Customers_Master AS c
INNER JOIN _Open AS o
    ON c.SubscriberKey = o.SubscriberKey
WHERE o.IsUnique = 1
  AND o.EventDate >= DATEADD(DAY, -30, GETDATE())
GROUP BY c.SubscriberKey, c.EmailAddress
  • INNER JOIN _Open AS o ON c.SubscriberKey = o.SubscriberKey ne garde que les clients ayant au moins une ouverture enregistrée.
  • WHERE o.IsUnique = 1 compte une ouverture par envoi : un abonné qui rouvre dix fois le même e-mail n'est pas compté dix fois.
  • MAX(o.EventDate) renvoie l'ouverture la plus récente et COUNT(o.JobID) le nombre d'envois distincts ouverts sur la période.
  • GROUP BY reprend toutes les colonnes non agrégées du SELECT, ce qui ramène le résultat à une ligne par abonné.

Cible : Openers_Last30Days, clé primaire SubscriberKey, Overwrite. Si la data extension est partagée depuis la business unit parente, préfixez son nom par ENT., par exemple ENT.Customers_Master. Les data views ne prennent pas de préfixe.

‍

Les pièges de jointure qui cassent une audience SFMC

‍

Démultiplication des lignes et clés primaires en double

Quand la table de droite contient plusieurs lignes par clé (une par commande, une par ouverture, une par entrée en journey), la jointure renvoie une ligne par correspondance. Si la cible a SubscriberKey en clé primaire, l'activité échoue sur le doublon ; si elle n'a pas de clé primaire, vous envoyez plusieurs fois le même e-mail.

La correction passe par GROUP BY avec des agrégats, par ROW_NUMBER() dans une sous-requête pour ne garder qu'une ligne par clé, ou par un test d'existence avec EXISTS à la place de la jointure.

‍

Clés et champs cibles qui ne s'alignent pas

Une SubscriberKey stockée avec des espaces en fin de chaîne ou dans un autre type de données ne correspondra pas comme prévu, et NULL n'est jamais égal à NULL : les lignes à clé vide sortent entièrement d'un INNER JOIN. Avant d'accuser la jointure, comptez les clés distinctes de chaque table et comparez avec le résultat.

Le bouton Validate Syntax vérifie que les tables et colonnes de la requête existent, pas que vos colonnes de sortie correspondent à la data extension cible. Un alias mal orthographié laisse le champ cible vide sans message d'erreur : donnez à chaque colonne calculée exactement le nom du champ cible.

Surveillez enfin le volume. Un LEFT JOIN entre deux grandes tables sur une large fenêtre de dates est une cause classique du timeout de 30 minutes. Filtrez tôt, ne sélectionnez que les colonnes utiles et joignez sur les clés primaires, qui sont indexées. Le choix de l'action de données compte aussi : notre article Overwrite, Update ou Append explique lequel retenir.

‍

Réponse rapide : INNER JOIN ou LEFT JOIN dans SFMC

‍

Vous voulezType de jointureClause en plusPoint de vigilance
Uniquement les abonnés présents dans les deux tablesINNER JOINAucuneTable de droite vide = audience vide
Tous les abonnés, enrichis quand une correspondance existeLEFT JOINCASE WHEN clé de droite IS NULLNULL dans les colonnes de droite
Les abonnés absents de la seconde tableLEFT JOINWHERE clé de droite IS NULLTester la clé, pas un champ facultatif
Les abonnés actifs dans une data viewINNER JOIN plus GROUP BYFenêtre sur EventDateDémultiplication sans agrégation

Avant d'enregistrer l'activité :

  1. Vérifiez que les deux tables partagent une clé de même type de données.
  2. Décidez si la seconde table est une condition (INNER) ou un enrichissement (LEFT).
  3. Regardez si la table de droite a plus d'une ligne par clé et agrégez si c'est le cas.
  4. Nommez chaque colonne de sortie exactement comme le champ de la data extension cible.
  5. Exécutez une fois, comparez le nombre de lignes à votre attente et vérifiez cinq abonnés au hasard.

‍

Questions fréquentes

‍

Peut-on joindre plus de deux data extensions dans une seule requête SFMC ?

Oui. Enchaînez autant de clauses JOIN que nécessaire, chacune avec sa propre condition ON. Gardez un alias par table et un préfixe par colonne, et rappelez-vous que chaque jointure un-à-plusieurs supplémentaire multiplie le risque de doublons.

‍

Quelle différence entre INNER JOIN et LEFT JOIN dans Marketing Cloud ?

INNER JOIN ne renvoie que les lignes dont la clé existe dans les deux tables : la seconde table agit comme un filtre. LEFT JOIN renvoie toutes les lignes de la première table et met NULL dans les colonnes de droite sans correspondance : la seconde table agit comme un enrichissement.

‍

Pourquoi ma jointure SFMC renvoie-t-elle des abonnés en double ?

Parce que la table de droite contient plusieurs lignes par clé. Commandes, ouvertures et clics sont stockés à raison d'une ligne par événement. Utilisez GROUP BY avec des agrégats, une sous-requête ROW_NUMBER() ou un test EXISTS pour revenir à une ligne par abonné avant d'écrire dans une data extension à clé primaire.

‍

Faut-il le préfixe ENT. pour joindre une shared data extension ?

Seulement quand la data extension est partagée depuis la business unit parente et que vous interrogez depuis une business unit enfant. Écrivez ENT. suivi du nom, par exemple ENT.Customers_Master. Les data extensions locales et les data views ne prennent pas ce préfixe.

‍

Décrivez la jointure en français, QAiry écrit le SQL

‍

Chaque requête de ce guide a commencé par une phrase : clients ayant acheté dans les 90 derniers jours, prospects n'ayant jamais acheté. QAiry prend cette phrase, lit vos data extensions et vos data views directement dans SFMC, choisit la jointure et la clé, puis renvoie un SQL prêt pour la production et une data extension cible que vous relisez avant toute exécution. Pour ne plus écrire la prochaine à la main, générez votre SQL SFMC à partir d'une demande en langage naturel.

Installez QAiry gratuitement dans votre SFMC ou regardez les démos produit pour voir une jointure construite à partir d'une seule demande.

Share this article
QAiry for SFMC

Skip the SQL. Build segments by chatting.

QAiry turns plain English requests into Salesforce Marketing Cloud audience segments and data extensions — no SQL, no IT ticket, no waiting.

Built for SFMC · ISV Partner · GDPR-ready