Analyze user behavior with DQL

  • Latest Dynatrace
  • How-to guide
  • 6-min read
  • Published May 07, 2026

The New RUM Experience captures rich behavioral data—clicks, navigations, session flows, and custom properties—in user events and user sessions. DQL gives you direct access to this data so you can answer behavioral questions that go beyond what Users & Sessions Users & Sessions shows out of the box—for example, to identify which UI elements drive the most engagement, map navigation flows, or measure bounce rates.

Run the queries below in Notebooks Notebooks for ad-hoc exploration, or convert any of them into a custom metric based on user events or user sessions.

User interactions

User interactions capture clicks, taps, and keypresses on UI elements. Analyzing them helps you understand which parts of your UI users engage with most and whether interaction volume changes after releases.

Interactions over time

Tracking interaction count over time helps you detect drops in engagement or spot spikes that follow a UI change or a new feature rollout.

fetch user.events
| filter characteristics.has_user_interaction
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| makeTimeseries interactions = count()

Top elements by interaction count

Knowing which UI elements receive the most interactions helps you identify high-traffic areas, validate design decisions, and spot underused features. The query below ranks UI elements by interaction count across all frontends.

fetch user.events
| filter characteristics.has_user_interaction
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| filter in(interaction.name, {"click", "touch", "key_press"})
| summarize {
sessions = countDistinct(dt.rum.session.id),
interactions = count()
}, by: {element = ui_element.name, type = ui_element.tag_name, frontend = frontend.name}
| sort interactions desc

To count how often users interact with a specific element, filter by element name and tag type.

fetch user.events
| filter characteristics.has_user_interaction
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| filter in(interaction.name, {"click", "touch", "key_press"})
| filter ui_element.tag_name == "button"
| filter ui_element.name == "Close details" // Replace with the element name you want to analyze
| summarize {
sessions = countDistinct(dt.rum.session.id),
interactions = count()
}, by: {element = ui_element.name}

For web frontends, you can improve element naming consistency and enrich interactions with feature or component context to make your analysis more convenient. See Customize UI element naming for user interactions and Associate user interactions with features and UI components.

Navigations connect the sequence of views a user visits during a session. Analyzing navigation patterns reveals your most-visited content and how users move through your frontend.

Tracking navigations over time helps you spot engagement drops or validate that a new feature or content change is driving more traffic. Navigation events fire on every page load and SPA view transition, so their count reflects total view activity.

fetch user.events
| filter characteristics.has_navigation
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| makeTimeseries navigations = count()

Top pages and views

Knowing which pages or views attract the most visitors helps you focus optimization efforts and measure the reach of key content. The query below ranks them by the number of unique sessions that visited them.

fetch user.events
| filter characteristics.has_navigation
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| summarize sessions = countDistinct(dt.rum.session.id), by: {view = view.url.path}
| sort sessions desc

To measure engagement across views, use the following query.

fetch user.events
| filter characteristics.has_user_interaction
| filter interaction.name == "click"
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| summarize {
sessions = countDistinct(dt.rum.session.id),
clicks = count()
}, by: {view = view.url.path}
| sort clicks desc

Landing views

Knowing where sessions begin helps you identify your most common entry points and prioritize their experience.

fetch user.events
| filter characteristics.has_navigation
| filter view.sequence_number == 1
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| summarize sessions = countDistinct(dt.rum.session.id), by: {landing_view = view.url.path}
| sort sessions desc

Incoming flows

Understanding where users come from when they arrive on a specific view helps you evaluate referral paths and optimize the journey toward key content.

fetch user.events
| filter characteristics.has_navigation
| filter view.url.path == "/overview" // Replace with the destination view URL path
| filter view.source.url.path != "/overview" // Replace with the same value above to exclude self-navigations
| summarize sessions = countDistinct(dt.rum.session.id), by: {source_view = view.source.name}
| sort sessions desc

Outgoing flows

Understanding where users go after leaving a specific view helps you identify whether they follow the expected journey or drop off. Use this to validate conversion funnels and detect dead ends.

fetch user.events
| filter characteristics.has_navigation
| filter view.source.url.path == "/login" // Replace with the source view URL path
| filter view.url.path != "/login" // Replace with the same value above to exclude self-navigations
| summarize sessions = countDistinct(dt.rum.session.id), by: {next_view = view.name}
| sort sessions desc

Event and session properties segmentation

Event and session properties let you attach business context to user events and sessions—for example, a customer tier, a feature flag state, or a checkout outcome. Once defined, you can use these properties as DQL filter and group-by dimensions to segment any behavioral analysis by your own business criteria.

To track navigations over time broken down by a property, use the following example.

fetch user.events
| filter characteristics.has_navigation
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| filter isNotNull(event_properties.customer_tier) // Replace with the property name you have configured
| makeTimeseries sessions = countDistinct(dt.rum.session.id), by: {event_properties.customer_tier} // Replace with the same property name

To compare the distribution of a property across all sessions, use the following example.

fetch user.sessions
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| filter isNotNull(session_properties.payment_provider) // Replace with the property name you have configured
| summarize by: {session_properties.payment_provider}, sessions = count() // Replace with the same property name

Bounce rate

A high bounce rate can indicate a content-intent mismatch—users landing on a view that doesn't meet their expectations—or friction in an onboarding or checkout flow. The queries below, best suited for historical timeframes, use an example custom definition, which you can adjust to match your product's definition.

fetch user.sessions
| fieldsAdd bounced = navigation_count <= 1
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| summarize by: {bounced}, count = count()

To compare bounce rates across frontends, add a by dimension.

fetch user.sessions
| fieldsAdd bounced = navigation_count <= 1
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| summarize {
total = count(),
bounced = countIf(bounced == true)
}, by: {frontend.name}
| fieldsAdd bounce_rate_pct = toDouble(bounced) / total * 100
| sort bounce_rate_pct desc

UTM campaign attribution

UTM parameters are tags appended to URLs by marketing teams to identify which campaigns, channels, or ad creatives drive traffic to your frontend. The query below extracts UTM values from navigation event URLs and ranks them by session count, showing which campaigns deliver the most visits. Use this to correlate campaign performance with user behavior on your web frontends.

fetch user.events
| filter characteristics.has_navigation
//| filter frontend.name == "FRONTEND-NAME" // Optional: filter to a specific frontend
| fields view.url.path, view.url.query, dt.rum.session.id
| filterOut isNull(view.url.query)
| parse view.url.query, "ARRAY{LD:key('&'| EOF)}*:params"
| expand params
| parse decodeUrl(params), "LD:key '=' LD:value"
| filter in(key, array("utm_campaign", "utm_content", "utm_medium", "utm_source", "utm_term"))
| summarize by: {key, value}, count = countDistinct(dt.rum.session.id)
| sort count desc
Related tags
Digital Experience