Un funnel de checkout répond à la question e-commerce la plus fondamentale : où perdons-nous des utilisateurs ? GA4 tracke le flux e-commerce standard à travers une séquence d’événements, et construire un funnel depuis les données BigQuery nécessite de combiner la clé de session composite avec l’agrégation COUNT(DISTINCT CASE WHEN ...).
Ce pattern utilise une analyse de funnel au niveau session — comptant combien de sessions ont atteint chaque étape, pas combien d’événements ont été déclenchés. La distinction est importante car un utilisateur pourrait consulter le même produit cinq fois dans une session ; c’est une session qui a consulté un produit, pas cinq entrées dans le funnel.
La requête complète du funnel de checkout
WITH funnel_events AS ( SELECT CONCAT( user_pseudo_id, '-', (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') ) AS session_id, event_name, event_timestamp FROM `project.dataset.events_*` WHERE event_name IN ( 'view_item', 'add_to_cart', 'begin_checkout', 'add_shipping_info', 'add_payment_info', 'purchase' ) AND _TABLE_SUFFIX BETWEEN '20240101' AND '20240131')
SELECT COUNT(DISTINCT CASE WHEN event_name = 'view_item' THEN session_id END) AS sessions_viewed_item, COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN session_id END) AS sessions_added_to_cart, COUNT(DISTINCT CASE WHEN event_name = 'begin_checkout' THEN session_id END) AS sessions_began_checkout, COUNT(DISTINCT CASE WHEN event_name = 'add_shipping_info' THEN session_id END) AS sessions_added_shipping, COUNT(DISTINCT CASE WHEN event_name = 'add_payment_info' THEN session_id END) AS sessions_added_payment, COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN session_id END) AS sessions_purchased, SAFE_DIVIDE( COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN session_id END), COUNT(DISTINCT CASE WHEN event_name = 'view_item' THEN session_id END) ) AS overall_conversion_rateFROM funnel_eventsComment fonctionne le pattern
Le CTE extrait uniquement les événements pertinents au funnel, rendant la requête efficace en filtrant tôt. Le CONCAT construit la clé de session composite — user_pseudo_id seul n’est pas assez granulaire (un utilisateur a plusieurs sessions), et ga_session_id seul n’est pas unique entre les utilisateurs.
La requête principale utilise COUNT(DISTINCT CASE WHEN event_name = '...' THEN session_id END) pour chaque étape. Le CASE WHEN retourne l’ID de session uniquement pour les événements correspondants ; COUNT(DISTINCT ...) compte ensuite les sessions uniques à cette étape. Les sessions qui n’ont pas atteint une étape retournent NULL depuis le CASE, que COUNT(DISTINCT) ignore.
SAFE_DIVIDE prévient les erreurs de division par zéro lorsque le haut du funnel a zéro sessions (possible sur des plages de dates courtes ou des requêtes fortement filtrées).
Lecture des résultats
Un funnel de checkout sain montre des taux de déperdition progressifs. Les patterns typiques :
- view_item vers add_to_cart : 5 à 15 % est normal pour la plupart des sites e-commerce. En dessous de 5 %, cela suggère des problèmes de prix, de page produit, ou de confiance.
- add_to_cart vers begin_checkout : 30 à 50 %. L’abandon à cette étape pointe souvent vers l’abandon du panier — surprise sur les frais de livraison, obligation de créer un compte, ou étapes suivantes peu claires.
- begin_checkout vers purchase : 50 à 70 %. Une déperdition significative ici indique généralement des frictions de paiement, des coûts inattendus révélés au checkout, ou des défaillances techniques dans le flux de paiement.
Lorsqu’une étape particulière montre une déperdition excessive par rapport aux benchmarks, investiguez l’expérience au niveau de la page à cette étape. Le funnel vous dit où les personnes partent ; comprendre pourquoi nécessite une analyse qualitative ou des tests A/B.
Ajouter une analyse de funnel au niveau produit
Le funnel de base montre le flux global. Pour voir quels produits ont la meilleure ou la pire conversion, ajoutez le UNNEST des items :
SELECT item.item_name, item.item_category, COUNTIF(event_name = 'view_item') AS product_views, COUNTIF(event_name = 'add_to_cart') AS add_to_carts, COUNTIF(event_name = 'purchase') AS purchases, SAFE_DIVIDE( COUNTIF(event_name = 'add_to_cart'), COUNTIF(event_name = 'view_item') ) AS view_to_cart_rate, SAFE_DIVIDE( COUNTIF(event_name = 'purchase'), COUNTIF(event_name = 'add_to_cart') ) AS cart_to_purchase_rateFROM `project.dataset.events_*`, UNNEST(items) AS itemWHERE event_name IN ('view_item', 'add_to_cart', 'purchase') AND _TABLE_SUFFIX BETWEEN '20240101' AND '20240131'GROUP BY item.item_name, item.item_categoryHAVING product_views > 100ORDER BY product_views DESCNotez que cela utilise COUNTIF plutôt que COUNT(DISTINCT CASE WHEN ...). Au niveau produit, les comptages d’événements (et non de sessions) sont plus naturels — vous voulez savoir combien de fois un produit a été consulté, ajouté au panier, et acheté. Le CROSS JOIN sur items est sûr ici car nous filtrons sur des événements e-commerce qui ont toujours des items renseignés.
Le filtre HAVING product_views > 100 élimine les produits avec trop peu de vues pour des taux de conversion significatifs. Un produit avec 3 vues et 1 achat a un taux de 33 %, mais ce chiffre n’a aucune valeur statistique.
Analyse temporelle du funnel
Pour suivre la performance du funnel dans le temps, ajoutez une dimension date :
WITH funnel_events AS ( SELECT PARSE_DATE('%Y%m%d', event_date) AS event_date, CONCAT( user_pseudo_id, '-', (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') ) AS session_id, event_name FROM `project.dataset.events_*` WHERE event_name IN ('view_item', 'add_to_cart', 'begin_checkout', 'purchase') AND _TABLE_SUFFIX BETWEEN '20240101' AND '20240131')
SELECT event_date, COUNT(DISTINCT CASE WHEN event_name = 'view_item' THEN session_id END) AS sessions_viewed, COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN session_id END) AS sessions_purchased, SAFE_DIVIDE( COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN session_id END), COUNT(DISTINCT CASE WHEN event_name = 'view_item' THEN session_id END) ) AS conversion_rateFROM funnel_eventsGROUP BY event_dateORDER BY event_dateCela donne une courbe de tendance du taux de conversion quotidien. Les pics et creux se corrèlent avec les promotions, les modifications du site ou les problèmes de tracking. Si le taux de conversion chute soudainement sans cause connue, vérifiez si des événements du funnel ont cessé de se déclencher — un tag de checkout cassé est un coupable courant.
Mises en garde importantes
C’est un funnel non strict, pas une séquence. La requête compte les sessions qui ont chaque événement, indépendamment de l’ordre. Une session où quelqu’un a acheté puis ensuite consulté un autre produit compte quand même comme ayant à la fois view_item et purchase. Pour une analyse de funnel strictement séquentielle (où l’étape N doit se produire après l’étape N-1), vous avez besoin de l’ordonnancement des timestamps d’événements et de fonctions fenêtre — une requête significativement plus complexe.
Le chiffre d’affaires doit venir du RECORD ecommerce, pas des items. Si vous ajoutez du chiffre d’affaires à ce funnel, utilisez ecommerce.purchase_revenue sur l’événement purchase, pas SUM(item.item_revenue) après le UNNEST des items. Ce dernier nécessite une gestion soignée pour éviter le double comptage lorsqu’une transaction a plusieurs items. Consultez Schéma e-commerce GA4 dans BigQuery pour les patterns de calcul du chiffre d’affaires.
Les sessions peuvent s’étendre sur minuit. Une session qui commence à 23h55 et génère un achat à 00h05 a des événements dans deux valeurs event_date différentes. Le funnel temporel ci-dessus compte par date d’événement, pas par date de session. Pour l’attribution par date de session, joignez une table de sessions qui assigne une seule date à chaque session.