# Help me convert span-based P90 downstream duration query to metric-based Timeseries DQL in milliseconds?

**URL:** <https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313>\
**Category:** Dashboarding\
**Tags:** dql, dynatrace-managed, dashboards\
**Created:** [June 28, 2026, 4:09am UTC](https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313 "2026-06-28T04:09:03Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![S1012](https://avatars.discourse-cdn.com/v4/letter/s/b4bc9f/32.png) [@S1012](https://community.dynatrace.com/u/S1012)\
**Post date:** [June 28, 2026, 4:09am UTC](https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313/1 "2026-06-28T04:09:03Z")

</div>

Hi Team,

I am trying to convert the following **span-based DQL query** into a **metric-based Timeseries query** using OpenPipeline metric extraction. The goal is to build a dashboard panel that shows the **P90 duration of downstream calls** , grouped by scope name.

Original span-based query

```markup
fetch spans
| filter service.name == "Service_Name"
| filter in(Service.domain, array($Service_Domain))
| filter in(Service.node, array($Service_Node))
| fieldsAdd scope_name = toString(otel.scope.name)
| summarize duration_p90 = percentile(duration, 90), by:{scope_name}
| sort duration_p90 desc

```

This query gives the expected P90 duration values in **milliseconds**.

I have created a custom metric through OpenPipeline:&nbsp; **spans.service\_request\_durations**

I am now trying to replicate the same logic using the metric-based Timeseries query below:

```markup
timeseries duration_p90_ns_series = percentile(
    spans.service_request_durations,
    percentile: 90,
    rollup: avg
  ),
  by: {otel.scope.name, Service.domain, Service.node}
| filter in(Service.domain, array($Service_Domain))
| filter in(Service.node, array($Service_Node))
| fieldsAdd scope_name = toString(otel.scope.name)
| fieldsAdd duration_p90_ms_value = arrayAvg(iCollectArray(toDouble(duration_p90_ns_series[]))) / 1000000.0
| filter isNotNull(duration_p90_ms_value)
| summarize duration_p90_ms = avg(duration_p90_ms_value), by: {scope_name}
| fieldsAdd duration_p90_ms = round(duration_p90_ms, decimals: 2)
| sort duration_p90_ms desc

```

However, when I run this Timeseries query, the value of duration\_p90\_ms\_value is displayed in **ps** , whereas the original fetch spans query shows the duration correctly in **ms**.

Could someone please help me understand the correct way to convert this metric-based Timeseries result into **milliseconds** , while keeping the same logic as the original span-based query?

---

<div class="post-metadata">

**Author:** ![t\_pawlak](https://dub1.discourse-cdn.com/dynatrace/user_avatar/community.dynatrace.com/t_pawlak/32/14991_2.png) [@t\_pawlak](https://community.dynatrace.com/u/t_pawlak)\
**Post date:** [June 29, 2026, 8:09am UTC](https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313/2 "2026-06-29T08:09:33Z")

</div>

Hi,

The issue is most likely caused by how the metric value is being handled in the timeseries query.

timeseries returns an array of values, so the conversion should be done after reducing the array to a scalar value. I would simplify the query and avoid converting each array element separately.

try domething like this:

```markup
timeseries {
    duration_p90 = percentile(spans.service_request_durations, 90)
},
by: { otel.scope.name, Service.domain, Service.node }
| filter in(Service.domain, array($Service_Domain))
| filter in(Service.node, array($Service_Node))
| fieldsAdd scope_name = toString(otel.scope.name)
| fieldsAdd duration_p90_ms_value = round(toDouble(arrayAvg(duration_p90)), decimals: 2)
| filter isNotNull(duration_p90_ms_value)
| summarize duration_p90_ms = avg(duration_p90_ms_value), by: { scope_name }
| sort duration_p90_ms desc

```

---

<div class="post-metadata">

**Author:** ![S1012](https://avatars.discourse-cdn.com/v4/letter/s/b4bc9f/32.png) [@S1012](https://community.dynatrace.com/u/S1012)\
**Post date:** [June 29, 2026, 8:58am UTC](https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313/3 "2026-06-29T08:58:34Z")

</div>

Hi&nbsp;[@t\_pawlak](https://community.dynatrace.com/user/42367)&nbsp;  
I am getting No records and a warning as below.

![S1012_0-1782723477624.png](https://global.discourse-cdn.com/dynatrace/original/3X/7/6/763d317760670910428271c0c158132627606b91.png)

_timeseries percentile function requires a rollup with the given metric key(s). Use the `rollup` parameter to calculate, for example, the 90th percentile of averages._

---

<div class="post-metadata">

**Author:** ![t\_pawlak](https://dub1.discourse-cdn.com/dynatrace/user_avatar/community.dynatrace.com/t_pawlak/32/14991_2.png) [@t\_pawlak](https://community.dynatrace.com/u/t_pawlak)\
**Post date:** [June 29, 2026, 12:32pm UTC](https://community.dynatrace.com/t/help-me-convert-span-based-p90-downstream-duration-query-to-metric-based-timeseries-dql-in-milliseconds/56313/4 "2026-06-29T12:32:07Z")

</div>

Hi,

```markup
timeseries {
  duration_p90 = percentile(
    spans.service_request_durations,
    percentile: 90,
    rollup: avg
  )
},
by: { otel.scope.name, Service.domain, Service.node }
| filter in(Service.domain, array("aaa"))
| filter in(Service.node, array("aaa"))
| fieldsAdd scope_name = toString(otel.scope.name)
| fieldsAdd duration_p90_ms_value = arrayAvg(duration_p90)
| filter isNotNull(duration_p90_ms_value)
| summarize duration_p90_ms = avg(duration_p90_ms_value), by: { scope_name }
| fieldsAdd duration_p90_ms = round(toDouble(duration_p90_ms), decimals: 2)
| sort duration_p90_ms desc

```

The warning is expected because percentile() on a metric requires a rollup parameter.
