Perché quello che ti insegnano sul CPL non basta
La prima cosa che impari quando entri in performance marketing è guardare il CPL. Costo per lead. Più basso, meglio è. La logica funziona finché tutti i lead valgono uguale. Nel B2B, nei servizi, nei prodotti ad alto LTV, i lead non valgono uguale. Mai.
Lo scenario tipico che vedo ogni settimana sui clienti che gestisco è questo. Due campagne Meta sullo stesso pubblico, stessa creative, stesso ad set strutturalmente. La prima genera 200 lead a 18 euro di CPL. La seconda ne genera 80 a 35 euro. Il cliente guarda il dashboard, indica la seconda, dice "questa la spegniamo, ha il CPL doppio". Il giudizio sembra ovvio.
Quando però facciamo il JOIN tra la tabella lead di Meta e la tabella deal del CRM, scopriamo che la prima campagna porta lead che chiedono il PDF, scaricano il lead magnet, e spariscono. La seconda porta lead che fanno la call commerciale e firmano nel 22% dei casi. Costo per cliente acquisito sulla prima: tendente a infinito. Sulla seconda: 159 euro. Tre mesi dopo aver invertito l'allocazione del budget, MRR sul canale è cresciuto del 37%.
CPL basso non significa campagna buona. CPL alto non significa campagna cattiva. CPL e basta è una vanity metric quando nessuno guarda il CRM. — Matteo Coloru
Il framework operativo, passo per passo
Il framework che applico ogni lunedì mattina sui clienti B2B è in tre step. Niente di magico, niente che richieda team data scientist. Servono due tabelle, una query, e un'ora di setup la prima volta.
Step 1, identity stitching prima di tutto
Identity stitching è il termine tecnico per quello che a parole suona banale: assicurarsi che la stessa persona compaia con la stessa identità in tutti i sistemi. Nel marketing significa che l'email del lead deve essere la stessa, normalizzata identicamente, dal pixel CAPI di Meta al form della landing al CRM.
In pratica: lowercase, trim degli spazi, rimozione dei caratteri non standard prima di salvare. Su Meta lato CAPI questo si imposta nel codice di tracking (mai mandare l'email così come l'utente l'ha digitata, sempre normalizzarla con email.toLowerCase().trim() prima del send). Sul form della landing si fa la stessa cosa. Sul CRM si fa la stessa cosa al momento della creazione del contatto. Tre punti, tre normalizzazioni identiche.
Match rate: la soglia che cambia tutto
Senza identity stitching, il match rate tra le due tabelle scende sotto il 60%. Vuol dire che quattro decisioni su dieci che prendi dopo il JOIN sono basate su un campione storto. Con identity stitching disciplinato, il match rate sale al 95%. La differenza tra il 60% e il 95% non è un dettaglio cosmetico, è la differenza tra un'analisi affidabile e una che ti porta a spegnere le campagne giuste.
Step 2, LEFT JOIN sulla tabella lead di Meta
La query che gira ogni lunedì mattina è strutturata in tre CTE. La prima normalizza le email lato Meta (LOWER, TRIM). La seconda fa la stessa cosa lato CRM, filtrando solo i deal aggiornati nel periodo di analisi. La terza fa il LEFT JOIN tra le due tabelle sulla colonna email normalizzata, raggruppa per campagna e ad set, e calcola sette colonne.
Le sette colonne sono queste:
- Numero di lead totali.
- Spesa attribuita.
- Lead orfani (non presenti nel CRM, segnale di tracking rotto se sopra il 30%).
- Clienti firmati (deal stage = 'won').
- Revenue generata.
- Costo per cliente acquisito (spesa / clienti firmati, con SAFE_DIVIDE).
- Lead-to-client rate (clienti firmati / lead totali, soglia di allarme sotto il 5% nel B2B).
La scelta del LEFT JOIN invece dell'INNER JOIN è deliberata. Voglio vedere tutti i lead che Meta dice di aver generato, anche quelli che nel CRM non sono mai arrivati. I lead orfani sono di per sé un'informazione: o c'è un data leak nel tracking, o sono lead spazzatura che il CRM ha filtrato come spam. In entrambi i casi voglio sapere quante campagne li producono e in quale percentuale, perché sono campagne da audit prima che da scaling.
L'errore più comune che vedo nei team junior è usare INNER JOIN per fare i report di marketing. Risultato: tutti i lead non finiti nel CRM scompaiono dal denominatore. Il CAC sembra magico, perché la spazzatura è sparita. Sembra magico finché un mese dopo qualcuno chiede "ma perché abbiamo speso tanto su quella campagna se i lead reali sono pochi?". E lì la verità viene fuori, ma il budget è già bruciato.
Step 3, leggere il risultato come fa un growth
La tabella che esce dalla query non si legge ordinandola per CPL. Si legge ordinandola per revenue, e dentro ogni riga si guardano due numeri: cost_per_client e lead_to_client_rate. Quelle sono le metriche che guidano le decisioni di scaling.
Tre euristiche operative che applico subito sulle righe della query:
- Se cost_per_client > LTV medio / 3, la campagna brucia margine: va spenta o ristrutturata creative.
- Se lead_to_client_rate < 5% in B2B (sotto il 2% in DTC), la qualità dei lead è degradata: va investigato il targeting prima di scalare il budget.
- Se leads_not_in_crm > 30%, c'è un problema di tracking che va fixato prima di prendere qualunque decisione (l'analisi a monte è zoppa).
Tre euristiche, tre soglie, tre decisioni. Non servono dashboard complesse. Serve un foglio di Google Sheet con il risultato della query incollato dentro e tre formule condizionali che colorano le righe. Verde se la riga è da scalare, giallo se è da osservare, rosso se è da spegnere. Si fa al lunedì mattina alle 8 prima della call delle 9 col cliente. Alla call non si dice "vediamo i numeri", si dice "spegniamo questi due, scaliamo questo, ne investighiamo uno". Differenza di setup mentale, differenza di valore percepito dal cliente.
Esempio applicato a un caso reale
Brand di servizi B2B nel mondo HR tech. Tre campagne Meta sullo stesso pubblico cold, stessa creative ma con tre angle diversi: efficiency angle, compliance angle, growth angle. Periodo di analisi: 60 giorni, marzo-aprile 2026. Spesa totale: 14.700 euro.
Dashboard Meta nativo. Campagna 1 (efficiency): 165 lead, CPL 22 euro. Campagna 2 (compliance): 89 lead, CPL 31 euro. Campagna 3 (growth): 121 lead, CPL 26 euro. Lettura immediata: la 1 è la migliore, la 2 è la peggiore. Il cliente voleva spegnere la 2 e scalare la 1.
Output dopo il LEFT JOIN tra meta_ads.leads e crm.deals (HubSpot, sincronizzato ogni 15 minuti via Make):
| Campagna | Lead | CPL | Clienti | Lead→Client | Cost / Client |
|---|---|---|---|---|---|
| 1 · Efficiency | 165 | 22 € | 4 | 2,4% | 907 € |
| 2 · Compliance | 89 | 31 € | 11 | 12,4% | 251 € |
| 3 · Growth | 121 | 26 € | 6 | 5,0% | 524 € |
Il giudizio iniziale ribaltato di 180 gradi. La campagna 2, quella che il cliente voleva spegnere, era la più profittevole con largo margine. La campagna 1, quella che il cliente voleva scalare, produceva curiosi che si fermavano al PDF. Tre mesi dopo aver triplicato il budget sulla 2 e spento la 1, il MER sul canale Meta è passato da 2,8 a 4,1. Niente magia, niente nuovo creative. Solo aver guardato il numero giusto.
Errori comuni e come evitarli
JOIN su email non normalizzate
Il 30% dei mancati match viene da qui. Stessa email scritta con maiuscole diverse o con uno spazio finale viene letta come due persone distinte da SQL. Lowercasare e trimmare prima del JOIN è obbligatorio. Su BigQuery: LOWER(TRIM(email)). Su Postgres uguale. Su MySQL idem. Tre caratteri di codice in più, 30% di match rate in più.
Confondere INNER e LEFT JOIN
Se il report di marketing usa INNER JOIN, automaticamente esclude tutti i lead non finiti nel CRM. Risultato apparente: il CAC è magico, perché la spazzatura è sparita dal denominatore. Risultato reale: stai prendendo decisioni su un campione filtrato di cui non sai nemmeno la regola di filtro. Default sicuro per il marketing: LEFT JOIN dalla tabella ads alla tabella CRM.
Duplicati silenziosi
Se nel CRM lo stesso email ha tre deal (un open, un lost, un won), il JOIN moltiplica per tre il count dei lead. Sintomo: hai più lead nel report dopo il JOIN che nella tabella Meta originale. Soluzione: deduplica prima di joinare con ROW_NUMBER() OVER (PARTITION BY email_norm ORDER BY closed_at DESC) e tieni solo il record più recente.
SAFE_DIVIDE dimenticato
Una campagna con 0 clienti firmati e una divisione spend / clients_won rompe la query in BigQuery, restituisce errore in Postgres, restituisce NULL silenzioso in MySQL (peggiore dei tre). Usa la versione safe del dialect specifico. È una riga in più di codice, evita ore di debug.
Filtri WHERE dopo il LEFT JOIN
Mettere WHERE c.deal_stage = 'won' su un LEFT JOIN trasforma il LEFT in INNER, perché filtri via i NULL. Per filtrare e mantenere il LEFT come LEFT, sposta il filtro nella clausola ON del JOIN, oppure usa COUNTIF(c.deal_stage = 'won') invece di filtrare a monte.
Domande frequenti su SQL JOIN per il marketing
Perché il CPL non basta per giudicare una campagna B2B?
Il CPL misura quanto costa generare un lead, non quanto vale. Nel B2B i lead hanno qualità molto diversa: alcuni firmano, altri spariscono dopo aver scaricato un PDF. Il numero che conta è il costo per cliente acquisito, che si calcola solo unendo i dati di Meta Ads con i dati del CRM via SQL JOIN. Senza quel JOIN, il CPL è una vanity metric che racconta metà della storia.
Cosa significa identity stitching nel marketing?
Il processo di assicurarsi che la stessa persona compaia con la stessa identità in tutti i sistemi: stessa email, normalizzata identicamente (lowercase, trim), dal pixel di tracking al form al CRM. Senza identity stitching, il match rate tra Meta Ads e CRM scende sotto il 60% e ogni analisi successiva è basata su un campione storto.
Quando usare LEFT JOIN invece di INNER JOIN nei report marketing?
LEFT JOIN è il default sicuro. Mantiene tutti i lead generati da Meta Ads, anche quelli non presenti nel CRM. I lead orfani sono di per sé un'informazione preziosa: indicano problemi di tracking o lead spazzatura. INNER JOIN esclude automaticamente i lead non matchati, facendo sembrare il CAC più basso e nascondendo i problemi di qualità.
Quanto tempo serve per impostare il primo JOIN tra Meta Ads e CRM?
Il setup tecnico richiede 4-6 ore la prima volta: export Meta Ads via Supermetrics o API in BigQuery (1h), sync CRM via Zapier o Make (1-2h), scrittura della query con CTE e SAFE_DIVIDE (1h), validazione e check del match rate (1-2h). Una volta impostato, gira automaticamente ogni notte.
Cosa fare se il match rate tra Meta e CRM è sotto il 60%?
Prima di analizzare le campagne va sistemato il setup di tracking. I controlli da fare in ordine: la normalizzazione delle email lato Meta CAPI (lowercase + trim), la stessa lato form, la stessa lato CRM. Senza fixare questo passaggio, ogni analisi è zoppa e va rimandata.
Quale strumento usare per scrivere queste query in produzione?
BigQuery è il default per chi gestisce dati di marketing: 5 dollari per TB scansionato, ma per i volumi tipici di un'agenzia o PMI si vive nel free tier. Per scrivere query interattivamente, DBeaver è gratuito. Hex è a pagamento (99 dollari al mese) ma vale i suoi soldi in team grazie a notebook collaborativi e schedulazione integrata.