PERCENTILE_DISC: Return an Observed Percentile Value

PERCENTILE_DISC selects an observed value rather than inventing an interpolated value between observations. I use it when the result must belong to the supplied population. The ordering and percentile still need an explicit contract.

Three wooden specimen trays hold leaves, seed pods and stones beside a magnifying glass.
Three wooden specimen trays holding leaves, seed pods and stones beside a magnifying glass.

Define the ordered observations first

The example supplies six present decimal observations and one missing value. The present values are ten, ten, twenty, forty, one hundred and one hundred twenty. The repeated ten remains part of the population.

The window expressions request the twenty-fifth, fiftieth and seventy-fifth percentiles. Two additional expressions request the endpoints zero and one. Every expression uses ascending ordering of the same Value column.

The input column is explicitly decimal(8,2). That preserves a typed numeric source rather than sorting numeric-looking text. The type also gives the observed-value result a clear scale contract.

I keep the complete input list small enough to sort by inspection. The repeated value and the wide gaps make the selected observations meaningful. A sequence of consecutive numbers could hide what the operation actually selects.

WITH Observations AS
(
    SELECT CAST(Value AS decimal(8,2)) AS Value
    FROM (VALUES (CAST(10 AS decimal(8,2))),(10),(20),(40),(100),(120),
        (CAST(NULL AS decimal(8,2)))) AS v(Value)
), Percentiles AS
(
    SELECT
        PERCENTILE_DISC(0.25) WITHIN GROUP (ORDER BY Value) OVER () AS P25,
        PERCENTILE_DISC(0.50) WITHIN GROUP (ORDER BY Value) OVER () AS P50,
        PERCENTILE_DISC(0.75) WITHIN GROUP (ORDER BY Value) OVER () AS P75,
        PERCENTILE_DISC(0.00) WITHIN GROUP (ORDER BY Value) OVER () AS PZero,
        PERCENTILE_DISC(1.00) WITHIN GROUP (ORDER BY Value) OVER () AS POne
    FROM Observations
)
SELECT DISTINCT P25,P50,P75,PZero,POne,
    CAST(SQL_VARIANT_PROPERTY(CAST(P25 AS sql_variant),'BaseType') AS nvarchar(128)) AS ResultType,
    CAST(SQL_VARIANT_PROPERTY(CAST(P25 AS sql_variant),'Scale') AS int) AS ResultScale
FROM Percentiles;
Native SSMS result showing five discrete percentiles and the decimal result type and scale.
The discrete percentiles select observed values: 10.00, 20.00 and 100.00 at the quarter, half and three-quarter points. The endpoints are 10.00 and 120.00. The result retains decimal scale 2. Open the result at full size.

Read discrete positions through the population

The expected twenty-fifth percentile is ten. The repeated ten occupies the first two positions of six present observations. Its cumulative share already reaches the requested threshold.

The expected fiftieth percentile is twenty. Three present observations have been reached at that value. The function does not average twenty and forty to create a new observation.

The expected seventy-fifth percentile is one hundred. Four of six observations through forty are still below that share. Reaching the fifth observation crosses the requested threshold.

These selected values are all members of the supplied data. A continuous percentile can answer an interpolation question instead. Choosing between the methods is about the required meaning, not which displayed number looks smoother.

Every result is a real observation

Keep missing values and duplicate values separate

The missing input is ignored for the percentile calculation. It does not become a seventh ranked value of zero. The complete present population still contains six observations.

The duplicate ten is not ignored. Removing duplicates before the calculation would define a different population. An observation list and a list of unique values answer different distribution questions.

I’d preserve repeated observations when each represents a real measurement or event. A duplicated import record could instead be an upstream quality problem. The percentile function does not decide which interpretation the data deserves.

The final SELECT DISTINCT reduces identical window outputs to one displayed row. It does not deduplicate the observations before the window functions run. Keeping those steps in separate query layers makes the distinction visible.

Check endpoint and type requirements

The expected zero-percentile endpoint is ten, while the one-percentile endpoint is one hundred twenty. Those values confirm the ascending direction. Reversing the ordered expression would change the requested distribution interpretation.

The expected ResultType is decimal, and ResultScale is two. PERCENTILE_DISC returns the type of its ordered expression. This differs from returning an approximate interpolated numeric type merely because a percentile was requested.

The SQL Server syntax includes OVER even when no partition is specified. That treats the supplied rowset as one population. Partitioning by a real group would calculate its percentile from that group’s observations instead.

The selected percentile values belong to the supplied observations. Their thresholds describe positions in the ordered population of present readings. Choose the percentile requirement before deciding whether an observed value is the appropriate output.

When adapting it, keep ties, missing input and at least one wide value gap. Write the expected observed threshold before execution. Those cases reveal accidental deduplication or interpolation that a neat consecutive list could conceal.

Write the expected value first, then run the query and compare.

An observed percentile is not a midpoint, it is a value you actually supplied.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Datatype, SQL Function, SQL Server
Previous Post
Data Mining: A Simple Introduction and a SQL Basket Example
Next Post
SQL SERVER – FIX – ERROR : Cannot drop the database because it is being used for replication. (Microsoft SQL Server, Error: 3724)

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.