Using Data Views for Engagement Reporting

How to turn Marketing Cloud system data views into reliable engagement reports, with correct join keys, unique counts, journey attribution and a history that outlasts retention.

Type
Article
Topic
Marketing Cloud
Reading time
5 min
Updated
Author
GeekMidst Team
Reviewed by
Site owner
Banner for the GeekMidst article “Using Data Views for Engagement Reporting”, covering Before you start and The data views you will use most

Standard tracking screens answer "how did this email do?" but not "how engaged is each segment over the last quarter?" or "which journey step drives clicks?". For those questions most teams query the system data views in Marketing Cloud Engagement. They are powerful, but easy to misread: a wrong join double-counts, opens are inflated by privacy features, and the data only goes back so far.

Diagram showing _Open and _Click reduced to unique rows per send, joined to _Sent and _Job on four keys in a Query Activity, writing summary and history data extensions
Pre-aggregate opens and clicks, join on all four send keys, and snapshot results because data views hold a limited history. View full size

Before you start

Data views are read-only system tables that you query with SQL in a Query Activity in Automation Studio. You cannot open them directly in Contact Builder, so every result goes into a target data extension you create first. In a child business unit, some account-wide views are referenced with an ent. prefix (for example ent._Subscribers); check which applies in your account structure. The examples use 30-day windows to keep queries fast.

The data views you will use most

What one row representsKey fields for joins
_SentOne email sent to one subscriber in one jobJobID, ListID, BatchID, SubscriberID, SubscriberKey, EventDate
_OpenOne open event; IsUnique marks the firstJobID, ListID, BatchID, SubscriberID, IsUnique
_ClickOne click on a tracked linkJobID, ListID, BatchID, SubscriberID, URL, LinkName, IsUnique
_BounceOne bounce, with category and SMTP reasonJobID, ListID, BatchID, SubscriberID, BounceCategory
_Unsubscribe and _ComplaintOne unsubscribe or reported complaintJobID, ListID, BatchID, SubscriberID
_JobOne send job, with email name and delivery timeJobID, EmailName, DeliveredTime
_Journey and _JourneyActivityJourney versions and their activitiesVersionID, JourneyActivityObjectID

Joining correctly

An individual send is identified by four fields together: JobID, ListID, BatchID and SubscriberID. Join engagement views to _Sent on all four. Joining on SubscriberKey alone links a subscriber's opens from one email to their sends of another and inflates every rate.

Two more rules prevent most reporting errors:

  • Use IsUnique = 1 for unique opens and unique clicks. Without it, a subscriber who opens five times counts five times.
  • Pre-aggregate before joining. Joining _Sent to both _Open and _Click at row level multiplies rows (every open times every click). Reduce each view to one row per send first, then join.

Worked example: a weekly job summary

The query below returns sends, unique openers and unique clickers per job delivered in the last seven days. Each engagement view is reduced to distinct sends before the join.

sql
SELECT
    j.JobID,
    j.EmailName,
    CAST(j.DeliveredTime AS DATE) AS SendDate,
    COUNT(*) AS Sent,
    SUM(CASE WHEN o.SubscriberID IS NOT NULL THEN 1 ELSE 0 END) AS UniqueOpeners,
    SUM(CASE WHEN c.SubscriberID IS NOT NULL THEN 1 ELSE 0 END) AS UniqueClickers
FROM _Job j
INNER JOIN _Sent s
    ON s.JobID = j.JobID
LEFT JOIN (
    SELECT DISTINCT JobID, ListID, BatchID, SubscriberID
    FROM _Open
    WHERE IsUnique = 1 AND EventDate >= DATEADD(DAY, -14, GETDATE())
) o ON o.JobID = s.JobID AND o.ListID = s.ListID
   AND o.BatchID = s.BatchID AND o.SubscriberID = s.SubscriberID
LEFT JOIN (
    SELECT DISTINCT JobID, ListID, BatchID, SubscriberID
    FROM _Click
    WHERE IsUnique = 1 AND EventDate >= DATEADD(DAY, -14, GETDATE())
) c ON c.JobID = s.JobID AND c.ListID = s.ListID
   AND c.BatchID = s.BatchID AND c.SubscriberID = s.SubscriberID
WHERE j.DeliveredTime >= DATEADD(DAY, -7, GETDATE())
GROUP BY j.JobID, j.EmailName, CAST(j.DeliveredTime AS DATE)
Target: Weekly_Job_Summary_DE with JobID (Number, key), EmailName (Text), SendDate (Date), Sent, UniqueOpeners, UniqueClickers (Number). Overwrite action.

The engagement subqueries look back 14 days so that opens and clicks on emails sent near the start of the 7-day window are still counted. Rates are calculated afterwards, in your reporting tool or a second query, so that you can recalculate them with different definitions without re-reading the data views.

Reporting by journey and activity

Journey sends appear in _Sent like any other send. The field TriggererSendDefinitionObjectID links each send to _JourneyActivity.JourneyActivityObjectID, and _JourneyActivity.VersionID links to _Journey. That gives you sends per journey, version and email activity.

sql
SELECT
    jy.JourneyName,
    jy.VersionNumber,
    ja.ActivityName,
    COUNT(*) AS Sent
FROM _Sent s
INNER JOIN _JourneyActivity ja
    ON ja.JourneyActivityObjectID = s.TriggererSendDefinitionObjectID
INNER JOIN _Journey jy
    ON jy.VersionID = ja.VersionID
WHERE s.EventDate >= DATEADD(DAY, -30, GETDATE())
GROUP BY jy.JourneyName, jy.VersionNumber, ja.ActivityName
Sends per journey version and email activity in the last 30 days. Extend with the same pre-aggregated open and click joins as above.

Interpreting opens honestly

Mail privacy features in some clients load images automatically, which records an open whether or not the person read the email. Unique open rates are therefore inflated and uneven across audiences. Use clicks, conversions and unsubscribes as primary engagement signals, and treat opens as a rough indicator of deliverability rather than interest. The guide to measuring marketing automation honestly covers holdouts and baselines.

Building a history that outlasts retention

A simple pattern works for most teams. Create a Send_Engagement_History_DE with one row per job, keyed on JobID, holding the counts from the weekly summary above plus a SnapshotDate. Schedule a daily automation that recalculates the last 14 days of jobs and writes them with the Update action, so late opens and clicks are picked up while older rows stay frozen once they leave the window. Add the business unit name if you report across units.

Keep definitions in one place. Write down what "sent", "unique clicker" and "engaged subscriber" mean for your reports, which data views each comes from and which filters apply. When two dashboards disagree, that document is usually where the answer is.

Common mistakes

  • Joining engagement to sends on SubscriberKey or JobID alone.
  • Forgetting IsUnique = 1, then reporting total opens as unique opens.
  • Joining opens and clicks at row level and counting inflated rows.
  • Using EventDate windows that cut off engagement from emails sent near the window edge.
  • Relying on data views for long-term trends without snapshots.
  • Comparing business units without checking which views are business unit-level and which are account-wide.

Checklist

  • Target data extensions created with the right types and keys
  • Joins use JobID, ListID, BatchID and SubscriberID together
  • Unique counts filtered with IsUnique
  • Engagement pre-aggregated before joining
  • Snapshots scheduled for history beyond the retention window
  • Opens labelled as indicative in every report

For a hands-off setup, see marketing analytics and reporting, and keep the Marketing Cloud SQL cheat sheet nearby.

Need reporting you can trust?

We can design the queries, snapshot tables and definitions behind your engagement reporting so numbers are consistent from one month to the next.

Discuss your reporting →

Tell us what you are trying to fix.

Share your Salesforce setup, goals and constraints. A practitioner reads every message and replies with questions, first thoughts or an honest view of whether you need outside help at all.

Talk to a Salesforce Expert
Book a Consultation