Understanding the STDEV() Function in SQL Server

In SQL Server the STDEV() function returns the statistical standard deviation of all values in a specified expression.

So we can use this function to get the standard deviation of all values in a column.

Syntax

The function can be used as an aggregate function or as an analytical/window function.

When used as an aggregate function, the syntax goes like this:

STDEV ( [ ALL | DISTINCT ] expression )

And as an analytical function:

STDEV ([ ALL ] expression) OVER ( [ partition_by_clause ] order_by_clause) 

The expression must be a numeric expression.

Example

Suppose we have a table like this:

SELECT * FROM Dogs;

Result:

DogName  Activity     Score
-------  -----------  -----
Bruno    Fetch Stick  43   
Cooper   Fetch Stick  43   
Max      Fetch Stick  91   
Bruno    Wag Tail     65   
Cooper   Wag Tail     51   
Max      Wag Tail     87   
Bruno    Keep Quiet   3    
Cooper   Keep Quiet   null 
Max      Keep Quiet   1    
null     Catch Cat    null 

We can use the STDEV() function against the Score column like this:

SELECT STDEV(Score) FROM Dogs;

Result:

33.704599092705436

The STDEV() function ignores NULL values and so these aren’t factored into the results.

The DISTINCT Argument

By default, all rows are evaluated, regardless of whether they contain unique values. However, we can use the DISTINCT keyword to specify that only distinct (unique) values are evaluated:

SELECT STDEV(DISTINCT Score) FROM Dogs;

Result:

36.339667425162666

We can see that this has changed the result slightly. This is because our underlying data contains duplicates – two rows contain 43. When we used the DISTINCT keyword, the STDEV() function treated those two as one. This resulted in a different standard deviation.

The ALL Argument

By default, all rows are evaluated regardless of whether they contain duplicates. In other words, this is the case when we don’t use the DISTINCT argument to eliminate duplicates from evaluation.

However, we also have the option of using the ALL keyword to explicitly specify that all values are evaluated:

SELECT STDEV(ALL Score) FROM Dogs;

Result:

33.704599092705436

We can see that this result is the same as the first example. That’s because the functionality is exactly the same when we use the ALL keyword.

The OVER Clause

We can include an OVER clause in order to use STDEV() as a window function.

Example:

SELECT 
    Activity,
    DogName,
    Score,
    STDEV(Score) OVER(ORDER BY Activity) AS "STDEV"
FROM Dogs;

Result:

Activity     DogName  Score  STDEV             
-----------  -------  -----  ------------------
Catch Cat    null     null   null              
Fetch Stick  Bruno    43     27.712812921102035
Fetch Stick  Cooper   43     27.712812921102035
Fetch Stick  Max      91     27.712812921102035
Keep Quiet   Bruno    3      36.86732971073441 
Keep Quiet   Cooper   null   36.86732971073441 
Keep Quiet   Max      1      36.86732971073441 
Wag Tail     Bruno    65     33.704599092705436
Wag Tail     Cooper   51     33.704599092705436
Wag Tail     Max      87     33.704599092705436

Here, I used an ORDER BY clause in the OVER clause to specify the logical order in which the operation is performed.

If we were to put another ORDER BY clause at the end of the query, it would determine how the result set is sorted.

Partitioning the Results

The OVER clause also accepts a PARTITION BY clause that allows us to partition the results by a given column.

Here’s an example:

SELECT 
    Activity,
    DogName,
    Score,
    STDEV(Score) OVER(PARTITION BY Activity) AS "STDEV"
FROM Dogs;

Result:

Activity     DogName  Score  STDEV             
-----------  -------  -----  ------------------
Catch Cat    null     null   null              
Fetch Stick  Bruno    43     27.712812921102035
Fetch Stick  Cooper   43     27.712812921102035
Fetch Stick  Max      91     27.712812921102035
Keep Quiet   Bruno    3      1.4142135623730951
Keep Quiet   Cooper   null   1.4142135623730951
Keep Quiet   Max      1      1.4142135623730951
Wag Tail     Bruno    65     18.147543451754924
Wag Tail     Cooper   51     18.147543451754924
Wag Tail     Max      87     18.147543451754924

In this case we partitioned by the Activity column, and so we got the standard deviation within each activity.

Here it is with an ORDER BY clause added to the OVER clause:

SELECT 
    Activity,
    DogName,
    Score,
    STDEV(Score) OVER(PARTITION BY Activity ORDER BY Score) AS "STDEV"
FROM Dogs;

Result:

Activity     DogName  Score  STDEV             
-----------  -------  -----  ------------------
Catch Cat    null     null   null              
Fetch Stick  Bruno    43     0                 
Fetch Stick  Cooper   43     0                 
Fetch Stick  Max      91     27.712812921102035
Keep Quiet   Cooper   null   null              
Keep Quiet   Max      1      null              
Keep Quiet   Bruno    3      1.4142135623730951
Wag Tail     Cooper   51     null              
Wag Tail     Bruno    65     9.899494936611665 
Wag Tail     Max      87     18.147543451754924

No DISTINCT Clause with the OVER Clause

We can’t use the DISTINCT clause when using the OVER clause.

Here’s what happens when we add the DISTINCT clause to the previous example:

SELECT 
    Activity,
    DogName,
    Score,
    STDEV(DISTINCT Score) OVER(PARTITION BY Activity ORDER BY Score) AS "STDEV"
FROM Dogs;

Result:

Msg 10759, Level 15, State 1, Line 5
Use of DISTINCT is not allowed with the OVER clause.

However, we can use the ALL clause if we want.